#include "mainwindow.h" #include "ui_mainwindow.h" #include "item.h" #include #include #include #include #include #include #include #include #include #include #include "creatitem.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , registerManager(new RegisterManager(this)) , modbusManager(new ModbusManager(registerManager, this)) { ui->setupUi(this); // registerManager = new RegisterManager(this); // modbusManager = new ModbusManager(registerManager, this); // 初始化PLC标签页 ui->plc_tab_widget->setTabsClosable(true); ui->plc_tab_widget->setMovable(true); // 初始化HMI标签页 ui->hmi_tab_widget->setTabsClosable(true); ui->hmi_tab_widget->setMovable(true); /* 2. 列表 */ ui->listWidget->setViewMode(QListView::IconMode); ui->listWidget->setIconSize(QSize(60, 30)); ui->listWidget->setDragEnabled(false); ui->listWidget->viewport()->installEventFilter(this); newPage(true); newPage(false); createComponents(); connect(ui->plc_tab_widget, &QTabWidget::tabCloseRequested,this,&MainWindow::onTabCloseRequested); connect(ui->hmi_tab_widget, &QTabWidget::tabCloseRequested,this,&MainWindow::onTabCloseRequested); connect(ui->listWidget,&QListWidget::currentTextChanged,this,&MainWindow::onListwidgetCurrenttextchanged); connect(ui->action_plc,&QAction::triggered,this,&MainWindow::plcChange); connect(ui->action_hmi,&QAction::triggered,this,&MainWindow::hmiChange); connect(ui->action_new, &QAction::triggered, this, &MainWindow::newProject); connect(ui->action_save, &QAction::triggered, this, &MainWindow::saveProject); connect(ui->action_open, &QAction::triggered, this, &MainWindow::openProject); connect(ui->action_connect, &QAction::triggered, this, &MainWindow::connection); connect(ui->action_disconnect, &QAction::triggered, this, &MainWindow::disconnection); connect(ui->btn_insert, &QPushButton::clicked, this, &MainWindow::btnInsertClicked); connect(modbusManager, &ModbusManager::connectionStatusChanged, this, &MainWindow::updateConnectionStatus); // connect(modbusManager, &ModbusManager::connectionStatusChanged, // this, &PLC::updateConnectionStatus); // connect(modbusManager, &ModbusManager::errorOccurred, // this, &PLC::handleModbusError); ui->stackedWidget->setCurrentIndex(0); setWindowTitle("未命名项目 - PLC编辑器"); } MainWindow::~MainWindow() { qDeleteAll(plcScenes); qDeleteAll(plcViews); qDeleteAll(hmiScenes); qDeleteAll(hmiViews); delete ui; } void MainWindow::newPage(bool isPlc) { // 创建新场景 QGraphicsScene* newScene = new QGraphicsScene(this); // 创建新视图 MyGraphicsView* newView = new MyGraphicsView(this); newView->setScene(newScene); newView->setSceneRect(0, 0, 800, 600); newView->setDragMode(QGraphicsView::RubberBandDrag); if (isPlc) { // 添加到PLC标签页 int newIndex = ui->plc_tab_widget->addTab(newView, QString("页面 %1").arg(plcScenes.size() + 1)); ui->plc_tab_widget->setCurrentIndex(newIndex); // 保存场景和视图 plcScenes.append(newScene); plcViews.append(newView); connect(newView, &MyGraphicsView::itemBoundToRegister, registerManager, &RegisterManager::bindItem); connect(newView, &MyGraphicsView::itemResetRegister, registerManager, &RegisterManager::unbindItem); } else { // 添加到HMI标签页 int newIndex = ui->hmi_tab_widget->addTab(newView, QString("页面 %1").arg(hmiScenes.size() + 1)); ui->hmi_tab_widget->setCurrentIndex(newIndex); // 保存场景和视图 hmiScenes.append(newScene); hmiViews.append(newView); connect(newView, &MyGraphicsView::itemBoundToRegister, registerManager, &RegisterManager::bindItem); connect(newView, &MyGraphicsView::itemResetRegister, registerManager, &RegisterManager::unbindItem); connect(newView, &MyGraphicsView::itemSetON, modbusManager, &ModbusManager::writeRegister); } } void MainWindow::createComponents() { ui->listWidget->clear(); if (currentIsPLC_) { struct Comp { QString name; QColor color; }; const QVector comps = { {"常开", Qt::blue}, {"常闭", Qt::red}, {"比较", Qt::green}, {"线圈", Qt::darkYellow} }; for (const Comp &c : comps) { QListWidgetItem *it = new QListWidgetItem(c.name, ui->listWidget); QPixmap pix(60, 30); pix.fill(Qt::white); QPainter p(&pix); p.setRenderHint(QPainter::Antialiasing); p.setBrush(c.color.lighter(150)); p.setPen(QPen(c.color, 2)); p.drawRoundedRect(QRect(5, 5, 50, 20), 3, 3); p.setPen(Qt::black); p.drawText(QRect(5, 5, 50, 20), Qt::AlignCenter, c.name); it->setIcon(QIcon(pix)); it->setData(Qt::UserRole, c.name); } } else { struct Comp { QString name; QColor color; }; const QVector comps = { {"按钮", Qt::blue}, {"指示灯", Qt::red} }; for (const Comp &c : comps) { QListWidgetItem *it = new QListWidgetItem(c.name, ui->listWidget); QPixmap pix(60, 30); pix.fill(Qt::white); QPainter p(&pix); p.setRenderHint(QPainter::Antialiasing); p.setBrush(c.color.lighter(150)); p.setPen(QPen(c.color, 2)); p.drawRoundedRect(QRect(5, 5, 50, 20), 3, 3); p.setPen(Qt::black); p.drawText(QRect(5, 5, 50, 20), Qt::AlignCenter, c.name); it->setIcon(QIcon(pix)); it->setData(Qt::UserRole, c.name); } } } void MainWindow::clearScene() { if(currentIsPLC_) { if (currentPageIndex() >= 0 && currentPageIndex() < plcScenes.size()) { plcScenes[currentPageIndex()]->clear(); } } else { if (currentPageIndex() >= 0 && currentPageIndex() < hmiScenes.size()) { hmiScenes[currentPageIndex()]->clear(); } } } void MainWindow::applyProjectToScene(const Project &proj, int pageIndex) { if (currentIsPLC_) { if (pageIndex < 0 || pageIndex >= plcScenes.size()) return; plcScenes[pageIndex]->clear(); QVector itemObjs; for (const auto& d : proj.items_) { Item* item = creatItem(d.type); if (!item) continue; item->setPos(d.x, d.y); item->setRegisterId(d.registerId); connect(item, &Item::requestCopy, plcViews[pageIndex], &MyGraphicsView::onItemRequestCopy); connect(item, &Item::requestDelete, plcViews[pageIndex], &MyGraphicsView::onItemRequestDelete); connect(item, &Item::requestBindRegister, plcViews[pageIndex], &MyGraphicsView::onItemRequestBindRegister); connect(item, &Item::requestCompare, plcViews[pageIndex], &MyGraphicsView::onItemRequestCompare); connect(item, &Item::requestReset, plcViews[pageIndex], &MyGraphicsView::onItemRequestReset); connect(item, &Item::requestSetON,plcViews[pageIndex], &MyGraphicsView::onItemRequestSetON); plcScenes[pageIndex]->addItem(item); registerManager->bindItem(item,d.registerId); itemObjs.append(item); } for (const auto& c : proj.connections_) { if (c.from >= 0 && c.from < itemObjs.size() && c.to >= 0 && c.to < itemObjs.size()) { Connection* conn = new Connection( itemObjs[c.from], static_cast(c.fromType), itemObjs[c.to], static_cast(c.toType)); plcScenes[pageIndex]->addItem(conn); } } } else { if (pageIndex < 0 || pageIndex >= hmiScenes.size()) return; hmiScenes[pageIndex]->clear(); QVector itemObjs; for (const auto& d : proj.items_) { Item* item = creatItem(d.type); if (!item) continue; item->setPos(d.x, d.y); item->setRegisterId(d.registerId); connect(item, &Item::requestCopy, hmiViews[pageIndex], &MyGraphicsView::onItemRequestCopy); connect(item, &Item::requestDelete, hmiViews[pageIndex], &MyGraphicsView::onItemRequestDelete); connect(item, &Item::requestBindRegister, hmiViews[pageIndex], &MyGraphicsView::onItemRequestBindRegister); connect(item, &Item::requestCompare, hmiViews[pageIndex], &MyGraphicsView::onItemRequestCompare); connect(item, &Item::requestReset, hmiViews[pageIndex], &MyGraphicsView::onItemRequestReset); connect(item, &Item::requestSetON,hmiViews[pageIndex], &MyGraphicsView::onItemRequestSetON); hmiScenes[pageIndex]->addItem(item); registerManager->bindItem(item,d.registerId); itemObjs.append(item); } } } void MainWindow::extractSceneToProject(Project &proj, int pageIndex) { if (currentIsPLC_) { if (pageIndex < 0 || pageIndex >= plcScenes.size()) return; proj.clear(); QList items; for (QGraphicsItem* gi : plcScenes[pageIndex]->items()){ if (Item* it = dynamic_cast(gi)){ items.append(it); } } for (Item* it : items) { Project::ItemData d; d.type = it->itemType(); d.x = it->pos().x(); d.y = it->pos().y(); d.registerId = it->registerId(); proj.items_.append(d); } for (QGraphicsItem* gi : plcScenes[pageIndex]->items()) { if (Connection* conn = dynamic_cast(gi)) { int fromIdx = items.indexOf(conn->from_); int toIdx = items.indexOf(conn->to_); if (fromIdx >= 0 && toIdx >= 0) { Project::ConnectionData c; c.from = fromIdx; c.to = toIdx; c.fromType = static_cast(conn->fromType_); c.toType = static_cast(conn->toType_); proj.connections_.append(c); } } } } else { if (pageIndex < 0 || pageIndex >= hmiScenes.size()) return; proj.clear(); QList items; for (QGraphicsItem* gi : hmiScenes[pageIndex]->items()){ if (Item* it = dynamic_cast(gi)){ items.append(it); } } for (Item* it : items) { Project::ItemData d; d.type = it->itemType(); d.x = it->pos().x(); d.y = it->pos().y(); d.registerId = it->registerId(); proj.items_.append(d); } } } int MainWindow::currentPageIndex() const { if (currentIsPLC_) { return ui->plc_tab_widget->currentIndex(); } else { return ui->hmi_tab_widget->currentIndex(); } } bool MainWindow::eventFilter(QObject *obj, QEvent *event) { if (obj == ui->listWidget->viewport()) { static QListWidgetItem *dragItem = nullptr; static QPoint startPos; if (event->type() == QEvent::MouseButtonPress) { auto *me = static_cast(event); if (me->button() == Qt::LeftButton) { dragItem = ui->listWidget->itemAt(me->pos()); startPos = me->pos(); } } else if (event->type() == QEvent::MouseMove && dragItem) { auto *me = static_cast(event); if ((me->pos() - startPos).manhattanLength() >= QApplication::startDragDistance()) { QString type = dragItem->data(Qt::UserRole).toString(); QMimeData *mime = new QMimeData; mime->setData("application/x-component", type.toUtf8()); QDrag *drag = new QDrag(this); drag->setMimeData(mime); drag->setPixmap(dragItem->icon().pixmap(ui->listWidget->iconSize())); drag->setHotSpot(drag->pixmap().rect().center()); drag->exec(Qt::CopyAction); dragItem = nullptr; } } else if (event->type() == QEvent::MouseButtonRelease) { dragItem = nullptr; } } return QWidget::eventFilter(obj, event); } void MainWindow::plcChange() { currentIsPLC_ = true; createComponents(); ui->stackedWidget->setCurrentIndex(0); // 显示PLC页面 // setWindowTitle((plcFilePath_.isEmpty() ? "未命名项目" : QFileInfo(plcFilePath_).fileName()) + " - PLC编辑器"); } void MainWindow::hmiChange() { currentIsPLC_ = false; createComponents(); ui->stackedWidget->setCurrentIndex(1); // 显示HMI页面 // setWindowTitle((hmiFilePath_.isEmpty() ? "未命名项目" : QFileInfo(hmiFilePath_).fileName()) + " - HMI编辑器"); } void MainWindow::newProject() { newPage(currentIsPLC_); // 更新窗口标题 if (currentIsPLC_) { setWindowTitle("未命名项目 - PLC编辑器"); } else { setWindowTitle("未命名项目 - HMI编辑器"); } } void MainWindow::openProject() { QString filePath; QString filter; if (currentIsPLC_) { filter = "PLC项目文件 (*.plcproj)"; filePath = QFileDialog::getOpenFileName(this, "打开PLC项目", "", filter); } else { filter = "HMI项目文件 (*.hmiproj)"; filePath = QFileDialog::getOpenFileName(this, "打开HMI项目", "", filter); } if (filePath.isEmpty()) return; Project project; if (project.loadFromFile(filePath)) { // 在当前模块新建一个页面 newPage(currentIsPLC_); int newPageIndex = currentPageIndex(); // 将项目应用到新页面 applyProjectToScene(project, newPageIndex); // 更新窗口标题 setWindowTitle(QFileInfo(filePath).fileName() + (currentIsPLC_ ? " - PLC编辑器" : " - HMI编辑器")); } else { QMessageBox::critical(this, "错误", "无法打开项目文件"); } } void MainWindow::saveProject() { QString filePath; QString filter; QString defaultSuffix; if (currentIsPLC_) { filter = "PLC项目文件 (*.plcproj)"; defaultSuffix = "plcproj"; filePath = QFileDialog::getSaveFileName(this, "保存PLC项目", "", filter); } else { filter = "HMI项目文件 (*.hmiproj)"; defaultSuffix = "hmiproj"; filePath = QFileDialog::getSaveFileName(this, "保存HMI项目", "", filter); } if (filePath.isEmpty()) return; // 确保文件有正确的扩展名 if (!filePath.endsWith("." + defaultSuffix, Qt::CaseInsensitive)) { filePath += "." + defaultSuffix; } Project project; // 从当前页面提取项目 extractSceneToProject(project, currentPageIndex()); if (project.saveToFile(filePath)) { // 更新窗口标题 setWindowTitle(QFileInfo(filePath).fileName() + (currentIsPLC_ ? " - PLC编辑器" : " - HMI编辑器")); } else { QMessageBox::critical(this, "错误", "保存项目失败"); } } void MainWindow::connection() { modbusManager->connectToDevice("COM1",QSerialPort::BaudRate(9600),QSerialPort::DataBits(8),QSerialPort::EvenParity,QSerialPort::StopBits(1)); modbusManager->startSimulation(1000); } void MainWindow::disconnection() { modbusManager->disconnectDevice(); modbusManager->stopSimulation(); } void MainWindow::updateConnectionStatus(bool connection) { if (connection) { ui->textEdit->append("连接"); } else { ui->textEdit->append("断开"); } } void MainWindow::onListwidgetCurrenttextchanged(const QString ¤tText) { selectedComponentType = currentText; } void MainWindow::onTabCloseRequested(int index) { if (currentIsPLC_) { if (plcScenes.size() > 1) { delete plcScenes.takeAt(index); delete plcViews.takeAt(index); ui->plc_tab_widget->removeTab(index); } else { QMessageBox::information(this, "提示", "至少保留一个PLC页面"); } } else { if (hmiScenes.size() > 1) { delete hmiScenes.takeAt(index); delete hmiViews.takeAt(index); ui->hmi_tab_widget->removeTab(index); } else { QMessageBox::information(this, "提示", "至少保留一个HMI页面"); } } } void MainWindow::btnInsertClicked() { // 1. 找场景中被选中的连线 QList selectedItems = plcScenes[currentPageIndex()]->selectedItems(); Connection* selectedConn = nullptr; for (QGraphicsItem* item : selectedItems) { selectedConn = dynamic_cast(item); if (selectedConn) break; } if (!selectedConn) { QMessageBox::warning(this, "提示", "请先选中一条连线"); return; } if (selectedComponentType.isEmpty()) { QMessageBox::warning(this, "提示", "请先在列表中选择要插入的组件"); return; } // 2. 计算插入点(中点) QLineF lf = selectedConn->line(); QPointF insertPos = (lf.p1() + lf.p2()) / 2; // 3. 删除原连线 Item* from = selectedConn->from_; Item* to = selectedConn->to_; Item::AnchorType fromType = selectedConn->fromType_; Item::AnchorType toType = selectedConn->toType_; from->removeConnection(selectedConn); to->removeConnection(selectedConn); plcScenes[currentPageIndex()]->removeItem(selectedConn); delete selectedConn; // 4. 插入新元件 Item* newItem = creatItem(selectedComponentType); newItem->setPos(insertPos); connect(newItem, &Item::requestCopy, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestCopy); connect(newItem, &Item::requestDelete, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestDelete); connect(newItem, &Item::requestBindRegister, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestBindRegister); connect(newItem, &Item::requestCompare, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestCompare); connect(newItem, &Item::requestReset, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestReset); connect(newItem, &Item::requestSetON,plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestSetON); plcScenes[currentPageIndex()]->addItem(newItem); // 5. 新建两条连线 Connection* c1 = new Connection(from, fromType, newItem, Item::Left); plcScenes[currentPageIndex()]->addItem(c1); Connection* c2 = new Connection(newItem, Item::Right, to, toType); plcScenes[currentPageIndex()]->addItem(c2); }