@@ -21,7 +21,7 @@ | |||||
#include <QJsonArray> | #include <QJsonArray> | ||||
#include <QJsonDocument> | #include <QJsonDocument> | ||||
#include <QJsonObject> | #include <QJsonObject> | ||||
#include<QSpinBox> | |||||
HMIDocument::HMIDocument(QWidget *parent) | HMIDocument::HMIDocument(QWidget *parent) | ||||
: BaseDocument(HMI, parent), | : BaseDocument(HMI, parent), | ||||
m_title("未命名HMI"), | m_title("未命名HMI"), | ||||
@@ -269,6 +269,7 @@ void HMIDocument::deleteSelectedItems() | |||||
} | } | ||||
// 显示属性对话框 | // 显示属性对话框 | ||||
// 在HMIDocument::showItemProperties()方法中替换相关代码 | |||||
void HMIDocument::showItemProperties() | void HMIDocument::showItemProperties() | ||||
{ | { | ||||
QList<QGraphicsItem*> selectedItems = m_scene->selectedItems(); | QList<QGraphicsItem*> selectedItems = m_scene->selectedItems(); | ||||
@@ -283,10 +284,17 @@ void HMIDocument::showItemProperties() | |||||
dialog.setWindowTitle("属性设置"); | dialog.setWindowTitle("属性设置"); | ||||
QFormLayout *form = new QFormLayout(&dialog); | QFormLayout *form = new QFormLayout(&dialog); | ||||
// 名称输入 - 使用NamedItem接口 | |||||
QLineEdit *nameEdit = new QLineEdit(namedItem->name()); | |||||
// 线圈地址输入 - 使用SpinBox替代LineEdit | |||||
QSpinBox *coilAddrSpin = new QSpinBox(); | |||||
coilAddrSpin->setRange(0, 65535); // 设置PLC线圈地址常见范围 | |||||
// 尝试从名称解析现有地址(如果之前用数字作为名称) | |||||
bool isNumber; | |||||
int addr = namedItem->name().toInt(&isNumber); | |||||
if (isNumber) { | |||||
coilAddrSpin->setValue(addr); | |||||
} | |||||
// 颜色选择(根据图形类型) | |||||
// 颜色选择(保持不变) | |||||
QColor tempColor1, tempColor2; | QColor tempColor1, tempColor2; | ||||
QPushButton *colorBtn1 = new QPushButton; | QPushButton *colorBtn1 = new QPushButton; | ||||
QPushButton *colorBtn2 = new QPushButton; | QPushButton *colorBtn2 = new QPushButton; | ||||
@@ -303,11 +311,11 @@ void HMIDocument::showItemProperties() | |||||
form->addRow("释放颜色:", colorBtn2); | form->addRow("释放颜色:", colorBtn2); | ||||
} | } | ||||
// 初始化颜色按钮 | |||||
// 初始化颜色按钮(保持不变) | |||||
colorBtn1->setStyleSheet("background-color:" + tempColor1.name()); | colorBtn1->setStyleSheet("background-color:" + tempColor1.name()); | ||||
colorBtn2->setStyleSheet("background-color:" + tempColor2.name()); | colorBtn2->setStyleSheet("background-color:" + tempColor2.name()); | ||||
// 颜色选择对话框 | |||||
// 颜色选择对话框(保持不变) | |||||
connect(colorBtn1, &QPushButton::clicked, [&]() { | connect(colorBtn1, &QPushButton::clicked, [&]() { | ||||
QColor c = QColorDialog::getColor(tempColor1, &dialog); | QColor c = QColorDialog::getColor(tempColor1, &dialog); | ||||
if (c.isValid()) { | if (c.isValid()) { | ||||
@@ -323,18 +331,18 @@ void HMIDocument::showItemProperties() | |||||
} | } | ||||
}); | }); | ||||
// 布局组装 | |||||
form->addRow("对象:", nameEdit); | |||||
// 布局组装(将名称输入替换为线圈地址) | |||||
form->addRow("线圈地址:", coilAddrSpin); // 这里是关键修改 | |||||
QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); | QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); | ||||
form->addRow(btnBox); | form->addRow(btnBox); | ||||
connect(btnBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); | connect(btnBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); | ||||
connect(btnBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); | connect(btnBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); | ||||
// 应用属性 | |||||
// 应用属性(使用SpinBox的值作为地址) | |||||
if (dialog.exec() == QDialog::Accepted) { | if (dialog.exec() == QDialog::Accepted) { | ||||
setModified(true); | setModified(true); | ||||
// 使用NamedItem接口设置名称 | |||||
namedItem->setName(nameEdit->text()); | |||||
// 将SpinBox的值转换为字符串保存到名称属性 | |||||
namedItem->setName(QString::number(coilAddrSpin->value())); | |||||
if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) { | if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) { | ||||
ellipse->setOnColor(tempColor1); | ellipse->setOnColor(tempColor1); | ||||
@@ -346,7 +354,6 @@ void HMIDocument::showItemProperties() | |||||
item->update(); | item->update(); | ||||
} | } | ||||
} | } | ||||
// 序列化图形项(用于复制粘贴) | // 序列化图形项(用于复制粘贴) | ||||
QByteArray HMIDocument::serializeItem(QGraphicsItem *item) | QByteArray HMIDocument::serializeItem(QGraphicsItem *item) | ||||
{ | { | ||||
@@ -204,31 +204,11 @@ void MainWindow::createMenus() | |||||
QMenu *simulationMenu = menuBar()->addMenu("仿真"); | QMenu *simulationMenu = menuBar()->addMenu("仿真"); | ||||
simulationMenu->setFont(itemFont); | simulationMenu->setFont(itemFont); | ||||
// 连接动作 | |||||
QAction *connectAction = new QAction("连接设置", this); | |||||
connectAction->setFont(itemFont); | |||||
connect(connectAction, &QAction::triggered, this, &MainWindow::onConnectClicked); | |||||
simulationMenu->addAction(connectAction); | |||||
// 运行动作 (暂时留空) | // 运行动作 (暂时留空) | ||||
QAction *runAction = new QAction("运行", this); | QAction *runAction = new QAction("运行", this); | ||||
runAction->setFont(itemFont); | runAction->setFont(itemFont); | ||||
runAction->setEnabled(false); | runAction->setEnabled(false); | ||||
simulationMenu->addAction(runAction); | simulationMenu->addAction(runAction); | ||||
} | |||||
void MainWindow::onConnectClicked() | |||||
{ | |||||
if (!m_serialDialog) { | |||||
m_serialDialog = new SerialPortManager(this); | |||||
connect(m_serialDialog, &SerialPortManager::logMessage,this, &MainWindow::handleSerialLog); | |||||
} | |||||
m_serialDialog->exec(); | |||||
} | |||||
void MainWindow::handleSerialLog(const QString &message) | |||||
{ | |||||
m_logEdit->append(message); | |||||
} | } | ||||
// 创建左侧工具栏 | // 创建左侧工具栏 | ||||
void MainWindow::createToolbars() | void MainWindow::createToolbars() | ||||
@@ -7,7 +7,6 @@ | |||||
#include <QAction> | #include <QAction> | ||||
#include<QTextEdit> | #include<QTextEdit> | ||||
#include "basedocument.h" | #include "basedocument.h" | ||||
#include "serialportmanager.h" | |||||
#include<QPushButton> | #include<QPushButton> | ||||
QT_BEGIN_NAMESPACE | QT_BEGIN_NAMESPACE | ||||
namespace Ui { class MainWindow; } | namespace Ui { class MainWindow; } | ||||
@@ -34,10 +33,7 @@ private slots: | |||||
void onCloseTab(int index); // 关闭标签页 | void onCloseTab(int index); // 关闭标签页 | ||||
void onClearLogButtonClicked(); | void onClearLogButtonClicked(); | ||||
void showLogContextMenu(const QPoint &pos); | void showLogContextMenu(const QPoint &pos); | ||||
void onConnectClicked(); // 连接菜单项槽函数 | |||||
void handleSerialLog(const QString &message); | |||||
private: | private: | ||||
SerialPortManager *m_serialDialog=nullptr; // 串口对话框 | |||||
void createMenus(); // 创建菜单栏 | void createMenus(); // 创建菜单栏 | ||||
QWidget* m_logPanelContainer; | QWidget* m_logPanelContainer; | ||||
QDockWidget* m_logDock; | QDockWidget* m_logDock; | ||||
@@ -1,4 +0,0 @@ | |||||
#ifndef MODBUSMANAGER_H | |||||
#define MODBUSMANAGER_H | |||||
#endif // MODBUSMANAGER_H |
@@ -1,263 +0,0 @@ | |||||
#include "serialportmanager.h" | |||||
#include <QSerialPortInfo> | |||||
#include <QVBoxLayout> | |||||
#include <QHBoxLayout> | |||||
#include <QGroupBox> | |||||
#include <QMessageBox> | |||||
#include <QDebug> | |||||
SerialPortManager::SerialPortManager(QWidget *parent) | |||||
: QDialog(parent), m_serial(new QSerialPort(this)) | |||||
{ | |||||
setWindowTitle("串口连接设置"); | |||||
setFixedSize(450,200); | |||||
// 连接串口信号 | |||||
connect(m_serial, &QSerialPort::readyRead, this, &SerialPortManager::handleReadyRead); | |||||
connect(m_serial, &QSerialPort::errorOccurred, this, &SerialPortManager::handleError); | |||||
// 主布局 | |||||
QVBoxLayout *mainLayout = new QVBoxLayout(this); | |||||
// 创建单个GroupBox - 串口参数设置 | |||||
QGroupBox *settingsGroup = new QGroupBox("串口参数设置", this); | |||||
QFormLayout *formLayout = new QFormLayout(settingsGroup); | |||||
// 串口选择行 | |||||
m_portComboBox = new QComboBox(this); | |||||
QPushButton *refreshButton = new QPushButton("刷新", this); | |||||
QHBoxLayout *portComboLayout = new QHBoxLayout; | |||||
portComboLayout->addWidget(m_portComboBox, 5); | |||||
portComboLayout->addWidget(refreshButton, 1); | |||||
formLayout->addRow("串口:", portComboLayout); | |||||
// 波特率选择行 | |||||
m_baudComboBox = new QComboBox(this); | |||||
populateBaudRates(); | |||||
formLayout->addRow("波特率:", m_baudComboBox); | |||||
// 奇偶校验选择行 | |||||
m_parityComboBox = new QComboBox(this); | |||||
populateParities(); | |||||
formLayout->addRow("奇偶校验:", m_parityComboBox); | |||||
// 从站地址行 | |||||
m_stationSpinBox = new QSpinBox(this); | |||||
m_stationSpinBox->setRange(1, 247); | |||||
m_stationSpinBox->setValue(1); | |||||
formLayout->addRow("从站地址:", m_stationSpinBox); | |||||
// 添加到主布局 | |||||
mainLayout->addWidget(settingsGroup); | |||||
// 按钮区域 | |||||
QHBoxLayout *buttonLayout = new QHBoxLayout; | |||||
m_connectBtn = new QPushButton("连接", this); | |||||
QPushButton *cancelButton = new QPushButton("取消", this); | |||||
// 状态标签 | |||||
m_statusLabel = new QLabel("状态: 未连接", this); | |||||
m_statusLabel->setStyleSheet("QLabel { color: red; }"); | |||||
buttonLayout->addWidget(m_statusLabel); | |||||
buttonLayout->addStretch(); | |||||
buttonLayout->addWidget(m_connectBtn); | |||||
buttonLayout->addWidget(cancelButton); | |||||
// 添加到主布局 | |||||
mainLayout->addLayout(buttonLayout); | |||||
// 连接信号 | |||||
connect(refreshButton, &QPushButton::clicked, this, &SerialPortManager::refreshPorts); | |||||
connect(m_connectBtn, &QPushButton::clicked, this, &SerialPortManager::onConnectClicked); | |||||
connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject); | |||||
// 初始刷新端口 | |||||
refreshPorts(); | |||||
} | |||||
SerialPortManager::~SerialPortManager() | |||||
{ | |||||
if (m_serial->isOpen()) { | |||||
m_serial->close(); | |||||
} | |||||
} | |||||
// 串口操作接口实现 | |||||
bool SerialPortManager::isOpen() const | |||||
{ | |||||
return m_serial->isOpen(); | |||||
} | |||||
void SerialPortManager::openSerialPort() | |||||
{ | |||||
if (!m_serial->isOpen()) { | |||||
onConnectClicked(); | |||||
} | |||||
} | |||||
void SerialPortManager::closeSerialPort() | |||||
{ | |||||
if (m_serial->isOpen()) { | |||||
m_serial->close(); | |||||
updateConnectionState(false); | |||||
} | |||||
} | |||||
QByteArray SerialPortManager::readAll() | |||||
{ | |||||
return m_serial->readAll(); | |||||
} | |||||
void SerialPortManager::write(const QByteArray &data) | |||||
{ | |||||
if (m_serial->isOpen()) { | |||||
m_serial->write(data); | |||||
} | |||||
} | |||||
// 连接按钮槽函数 | |||||
void SerialPortManager::onConnectClicked() | |||||
{ | |||||
if (m_serial->isOpen()) { | |||||
// 如果已连接,则断开 | |||||
m_serial->close(); | |||||
updateConnectionState(false); | |||||
return; | |||||
} | |||||
// 获取配置 | |||||
QString portName = m_portComboBox->currentData().toString(); | |||||
int baudRate = m_baudComboBox->currentData().toInt(); | |||||
QSerialPort::Parity parity = static_cast<QSerialPort::Parity>(m_parityComboBox->currentData().toInt()); | |||||
if (portName.isEmpty()) { | |||||
QMessageBox::warning(this, "错误", "请选择有效的串口"); | |||||
return; | |||||
} | |||||
// 配置串口 | |||||
m_serial->setPortName(portName); | |||||
m_serial->setBaudRate(baudRate); | |||||
m_serial->setDataBits(QSerialPort::Data8); | |||||
m_serial->setParity(parity); | |||||
m_serial->setStopBits(QSerialPort::OneStop); | |||||
if (m_serial->open(QIODevice::ReadWrite)) | |||||
{ | |||||
updateConnectionState(true); | |||||
QString logMsg = QString("串口连接成功: %1, %2 bps, 从站地址: %3") | |||||
.arg(portName) | |||||
.arg(baudRate) | |||||
.arg(m_stationSpinBox->value()); | |||||
emit logMessage(logMsg); // 发送统一日志 | |||||
} | |||||
else | |||||
{ | |||||
QString errorMsg = QString("串口连接失败: %1").arg(m_serial->errorString()); | |||||
emit logMessage(errorMsg); // 发送统一日志 | |||||
} | |||||
} | |||||
// 更新连接状态 | |||||
void SerialPortManager::updateConnectionState(bool connected) | |||||
{ | |||||
if (connected) { | |||||
m_connectBtn->setText("断开"); | |||||
m_statusLabel->setText("状态: 已连接"); | |||||
m_statusLabel->setStyleSheet("QLabel { color: green; }"); | |||||
} else { | |||||
m_connectBtn->setText("连接"); | |||||
m_statusLabel->setText("状态: 未连接"); | |||||
m_statusLabel->setStyleSheet("QLabel { color: red; }"); | |||||
} | |||||
} | |||||
// 数据接收处理 | |||||
void SerialPortManager::handleReadyRead() | |||||
{ | |||||
QByteArray data = m_serial->readAll(); | |||||
if (!data.isEmpty()) { | |||||
QString hexString = data.toHex().toUpper(); // 转换为大写的十六进制 | |||||
emit logMessage("接收: " + hexString); // 发送统一日志 | |||||
} | |||||
} | |||||
// 错误处理 | |||||
void SerialPortManager::handleError(QSerialPort::SerialPortError error) | |||||
{ | |||||
if (error != QSerialPort::NoError) | |||||
{ | |||||
QString errorMsg = QString("串口错误: %1").arg(m_serial->errorString()); | |||||
emit logMessage(errorMsg); // 发送统一日志 | |||||
} | |||||
} | |||||
void SerialPortManager::populateBaudRates() | |||||
{ | |||||
m_baudComboBox->clear(); | |||||
m_baudComboBox->addItem("4800", QSerialPort::Baud4800); | |||||
m_baudComboBox->addItem("9600", QSerialPort::Baud9600); | |||||
m_baudComboBox->addItem("19200", QSerialPort::Baud19200); | |||||
m_baudComboBox->addItem("38400", QSerialPort::Baud38400); | |||||
m_baudComboBox->addItem("57600", QSerialPort::Baud57600); | |||||
m_baudComboBox->addItem("115200", QSerialPort::Baud115200); | |||||
m_baudComboBox->setCurrentIndex(1); // 默认9600 | |||||
} | |||||
void SerialPortManager::populateParities() | |||||
{ | |||||
m_parityComboBox->clear(); | |||||
m_parityComboBox->addItem("无校验", QSerialPort::NoParity); | |||||
m_parityComboBox->addItem("奇校验", QSerialPort::OddParity); | |||||
m_parityComboBox->addItem("偶校验", QSerialPort::EvenParity); | |||||
m_parityComboBox->setCurrentIndex(0); // 默认无校验 | |||||
} | |||||
void SerialPortManager::refreshPorts() | |||||
{ | |||||
m_portComboBox->clear(); | |||||
const auto ports = QSerialPortInfo::availablePorts(); | |||||
for (const QSerialPortInfo &port : ports) { | |||||
QString description = port.description(); | |||||
if (description.isEmpty()) description = "未知设备"; | |||||
QString itemText = QString("%1 (%2)") | |||||
.arg(port.portName()) | |||||
.arg(description); | |||||
m_portComboBox->addItem(itemText, port.portName()); | |||||
} | |||||
if (ports.isEmpty()) | |||||
{ | |||||
m_portComboBox->addItem("无可用端口", ""); | |||||
} | |||||
} | |||||
QString SerialPortManager::portName() const | |||||
{ | |||||
return m_portComboBox->currentData().toString(); | |||||
} | |||||
int SerialPortManager::baudRate() const | |||||
{ | |||||
return m_baudComboBox->currentData().toInt(); | |||||
} | |||||
QSerialPort::Parity SerialPortManager::parity() const | |||||
{ | |||||
return static_cast<QSerialPort::Parity>(m_parityComboBox->currentData().toInt()); | |||||
} | |||||
QSerialPort::StopBits SerialPortManager::stopBits() const | |||||
{ | |||||
return QSerialPort::OneStop; | |||||
} | |||||
int SerialPortManager::stationAddress() const | |||||
{ | |||||
return m_stationSpinBox->value(); | |||||
} |
@@ -1,57 +0,0 @@ | |||||
#ifndef SERIALPORTMANAGER_H | |||||
#define SERIALPORTMANAGER_H | |||||
#include <QDialog> | |||||
#include <QSerialPort> | |||||
#include <QComboBox> | |||||
#include <QPushButton> | |||||
#include <QFormLayout> | |||||
#include <QSpinBox> | |||||
#include <QLabel> | |||||
class SerialPortManager : public QDialog | |||||
{ | |||||
Q_OBJECT | |||||
public: | |||||
explicit SerialPortManager(QWidget *parent = nullptr); | |||||
~SerialPortManager(); | |||||
// 获取串口配置 | |||||
QString portName() const; | |||||
int baudRate() const; | |||||
QSerialPort::Parity parity() const; | |||||
QSerialPort::StopBits stopBits() const; | |||||
int stationAddress() const; | |||||
// 新增串口操作接口 | |||||
bool isOpen() const; | |||||
void openSerialPort(); | |||||
void closeSerialPort(); | |||||
QByteArray readAll(); | |||||
void write(const QByteArray &data); | |||||
signals: | |||||
void errorOccurred(const QString &error); | |||||
void logMessage(const QString &message); | |||||
private slots: | |||||
void refreshPorts(); | |||||
void onConnectClicked(); | |||||
void handleReadyRead(); | |||||
void handleError(QSerialPort::SerialPortError error); | |||||
private: | |||||
void populateBaudRates(); | |||||
void populateParities(); | |||||
void updateConnectionState(bool connected); | |||||
QSerialPort *m_serial; // 串口对象 | |||||
QComboBox *m_portComboBox; | |||||
QComboBox *m_baudComboBox; | |||||
QComboBox *m_parityComboBox; | |||||
QSpinBox *m_stationSpinBox; | |||||
QPushButton *m_connectBtn; // 连接/断开按钮 | |||||
QLabel *m_statusLabel; // 状态标签 | |||||
}; | |||||
#endif // SERIALPORTMANAGER_H |
@@ -21,20 +21,16 @@ SOURCES += \ | |||||
hmidocument.cpp \ | hmidocument.cpp \ | ||||
main.cpp \ | main.cpp \ | ||||
mainwindow.cpp \ | mainwindow.cpp \ | ||||
modbusmanager.cpp \ | |||||
plcdocument.cpp \ | plcdocument.cpp \ | ||||
plcitems.cpp \ | |||||
serialportmanager.cpp | |||||
plcitems.cpp | |||||
HEADERS += \ | HEADERS += \ | ||||
basedocument.h \ | basedocument.h \ | ||||
graphicsitems.h \ | graphicsitems.h \ | ||||
hmidocument.h \ | hmidocument.h \ | ||||
mainwindow.h \ | mainwindow.h \ | ||||
modbusmanager.h \ | |||||
plcdocument.h \ | plcdocument.h \ | ||||
plcitems.h \ | |||||
serialportmanager.h | |||||
plcitems.h | |||||
FORMS += \ | FORMS += \ | ||||
mainwindow.ui | mainwindow.ui | ||||