From 1b532108e12c60fb59c5d2539fbd1c8686dd595f Mon Sep 17 00:00:00 2001 From: lipengpeng Date: Thu, 7 Aug 2025 13:09:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86HMI=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- button.cpp | 30 +++++++++ button.h | 15 +++++ creatitem.cpp | 2 + creatitem.h | 2 + editor.pro | 16 ++++- editor.pro.user | 2 +- hmi.cpp | 106 ++++++++++++++++++++++++++++++++ hmi.h | 32 ++++++++++ hmi.ui | 49 +++++++++++++++ item.cpp | 128 +++++++++++++++++---------------------- item.h | 8 +-- light.cpp | 30 +++++++++ light.h | 15 +++++ mainwindow.cpp | 140 ++++-------------------------------------- mainwindow.h | 16 ++--- mainwindow.ui | 75 +++++++++-------------- plc.cpp | 158 ++++++++++++++++++++++++++++++++++++++++++++++++ plc.h | 33 ++++++++++ plc.ui | 62 +++++++++++++++++++ 19 files changed, 657 insertions(+), 262 deletions(-) create mode 100644 button.cpp create mode 100644 button.h create mode 100644 hmi.cpp create mode 100644 hmi.h create mode 100644 hmi.ui create mode 100644 light.cpp create mode 100644 light.h create mode 100644 plc.cpp create mode 100644 plc.h create mode 100644 plc.ui diff --git a/button.cpp b/button.cpp new file mode 100644 index 0000000..7f68bc0 --- /dev/null +++ b/button.cpp @@ -0,0 +1,30 @@ +#include "button.h" +#include +#include + +Button::Button(const QString &type) : Item(type) +{ + +} + +QRectF Button::boundingRect() const +{ + return QRectF(0, 0, 50, 50); +} + +void Button::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) +{ + painter->setRenderHint(QPainter::Antialiasing); + + if (type_ == "按钮") { + painter->drawRect(0,0,50,50); + } + if (option->state & QStyle::State_Selected) { + QPen pen(Qt::DashLine); + pen.setColor(Qt::blue); + pen.setWidth(2); + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + painter->drawRect(boundingRect()); + } +} diff --git a/button.h b/button.h new file mode 100644 index 0000000..083a98f --- /dev/null +++ b/button.h @@ -0,0 +1,15 @@ +#ifndef BUTTON_H +#define BUTTON_H +#include "item.h" + +class Button : public Item +{ +public: + Button(const QString &type); + QRectF boundingRect() const override; + void paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *) override; +}; + +#endif // BUTTON_H diff --git a/creatitem.cpp b/creatitem.cpp index c9725ab..01eb617 100644 --- a/creatitem.cpp +++ b/creatitem.cpp @@ -6,5 +6,7 @@ Item *creatItem(const QString &type) if (type == "常闭") return new Contact(type); if (type == "线圈") return new Coil(type); if (type == "比较") return new Comparator(type); + if (type == "按钮") return new Button(type); + if (type == "指示灯") return new Light(type); return nullptr; } diff --git a/creatitem.h b/creatitem.h index d85eaea..990d8ac 100644 --- a/creatitem.h +++ b/creatitem.h @@ -4,6 +4,8 @@ #include "coil.h" #include "contact.h" #include "comparator.h" +#include "button.h" +#include "light.h" Item* creatItem(const QString& type); diff --git a/editor.pro b/editor.pro index 7793fcc..dcadb17 100644 --- a/editor.pro +++ b/editor.pro @@ -16,28 +16,38 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ + button.cpp \ coil.cpp \ comparator.cpp \ connection.cpp \ contact.cpp \ creatitem.cpp \ + hmi.cpp \ item.cpp \ + light.cpp \ main.cpp \ mainwindow.cpp \ - mygraphicsview.cpp + mygraphicsview.cpp \ + plc.cpp HEADERS += \ + button.h \ coil.h \ comparator.h \ connection.h \ contact.h \ creatitem.h \ + hmi.h \ item.h \ + light.h \ mainwindow.h \ - mygraphicsview.h + mygraphicsview.h \ + plc.h FORMS += \ - mainwindow.ui + hmi.ui \ + mainwindow.ui \ + plc.ui # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin diff --git a/editor.pro.user b/editor.pro.user index 0791f7a..6b8b30b 100644 --- a/editor.pro.user +++ b/editor.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/hmi.cpp b/hmi.cpp new file mode 100644 index 0000000..b48b907 --- /dev/null +++ b/hmi.cpp @@ -0,0 +1,106 @@ +#include "hmi.h" +#include "ui_hmi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "creatitem.h" + +HMI::HMI(QWidget *parent) : + QWidget(parent), + ui(new Ui::HMI) +{ + ui->setupUi(this); + /* 1. 场景 */ + m_scene = new QGraphicsScene(this); + ui->graphicsView->setScene(m_scene); + ui->graphicsView->setSceneRect(0, 0, 800, 600); + ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag); + + /* 2. 列表 */ + ui->listWidget->setViewMode(QListView::IconMode); + ui->listWidget->setIconSize(QSize(60, 30)); + ui->listWidget->setDragEnabled(false); + ui->listWidget->viewport()->installEventFilter(this); + + /* 3. 填充列表 */ + createComponents(); + connect(ui->listWidget,&QListWidget::currentTextChanged,this,&HMI::onListwidgetCurrenttextchanged); +} + +HMI::~HMI() +{ + delete ui; +} + +bool HMI::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 HMI::createComponents() +{ + 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 HMI::onListwidgetCurrenttextchanged(const QString &text) +{ + selectedComponentType = text; +} diff --git a/hmi.h b/hmi.h new file mode 100644 index 0000000..9f831ee --- /dev/null +++ b/hmi.h @@ -0,0 +1,32 @@ +#ifndef HMI_H +#define HMI_H + +#include +#include + +namespace Ui { +class HMI; +} + +class HMI : public QWidget +{ + Q_OBJECT + +public: + explicit HMI(QWidget *parent = nullptr); + ~HMI(); + void createComponents(); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private slots: + void onListwidgetCurrenttextchanged(const QString ¤tText); + +private: + Ui::HMI *ui; + QGraphicsScene *m_scene; + QString selectedComponentType; +}; + +#endif // HMI_H diff --git a/hmi.ui b/hmi.ui new file mode 100644 index 0000000..93b29ca --- /dev/null +++ b/hmi.ui @@ -0,0 +1,49 @@ + + + HMI + + + + 0 + 0 + 964 + 550 + + + + Form + + + + + 200 + 30 + 761 + 451 + + + + QGraphicsView::NoDrag + + + + + + 10 + 30 + 171 + 451 + + + + + + + MyGraphicsView + QGraphicsView +
mygraphicsview.h
+
+
+ + +
diff --git a/item.cpp b/item.cpp index 6c493e4..ffd4192 100644 --- a/item.cpp +++ b/item.cpp @@ -18,79 +18,29 @@ QRectF Item::boundingRect() const return QRectF(-22, -15, 44, 30); } -//void Item::paint(QPainter *painter, -// const QStyleOptionGraphicsItem * option, -// QWidget *) -//{ -// painter->setRenderHint(QPainter::Antialiasing); - -// if (type_ == "线圈") { -// // 绘制线圈样式: 两边线段+中间椭圆 -// painter->drawLine(-12, 0, -5, 0); -// painter->drawEllipse(QRectF(-5, -8, 10, 16)); -// painter->drawLine(5, 0, 12, 0); - -// // 画锚点 -// painter->setBrush(Qt::darkGray); -// painter->setPen(Qt::NoPen); -// painter->drawEllipse(QPointF(-12, 0), 4, 4); // 左锚点 -// painter->drawEllipse(QPointF(12, 0), 4, 4); // 右锚点 -// } -// else if (type_ == "常开") { -// painter->drawLine(-12, 0, -4, 0); -// painter->drawLine(-4, -8, -4, 8); -// painter->drawLine(4, -8, 4, 8); -// painter->drawLine(4, 0, 12, 0); - -// // 锚点 -// painter->setBrush(Qt::darkGray); -// painter->setPen(Qt::NoPen); -// painter->drawEllipse(QPointF(-18, 0), 4, 4); // 左 -// painter->drawEllipse(QPointF(18, 0), 4, 4); // 右 -// } -// else if (type_ == "常闭") { -// painter->drawLine(-15, -10, 15, 10); // 对角线 -// painter->drawLine(-12, 0, -4, 0); -// painter->drawLine(-4, -8, -4, 8); -// painter->drawLine(4, -8, 4, 8); -// painter->drawLine(4, 0, 12, 0); - -// // 锚点 -// painter->setBrush(Qt::darkGray); -// painter->setPen(Qt::NoPen); -// painter->drawEllipse(QPointF(-18, 0), 4, 4); // 左 -// painter->drawEllipse(QPointF(18, 0), 4, 4); // 右 -// } -// else if (type_ == "比较") { -// painter->drawRect(QRectF(-12, -8, 24, 16)); -// painter->setFont(QFont("Arial", 8)); -// painter->drawText(QRectF(-10, -8, 20, 16), Qt::AlignCenter, "CP"); - -// // 锚点 -// painter->setBrush(Qt::darkGray); -// painter->setPen(Qt::NoPen); -// painter->drawEllipse(QPointF(-18, 0), 4, 4); -// painter->drawEllipse(QPointF(18, 0), 4, 4); -// } -// if (option->state & QStyle::State_Selected) { -// QPen pen(Qt::DashLine); -// pen.setColor(Qt::blue); -// pen.setWidth(2); -// painter->setPen(pen); -// painter->setBrush(Qt::NoBrush); -// painter->drawRect(boundingRect()); -// } -//} - QPointF Item::anchorPos(AnchorType type) const { + // 对于"按钮"和"指示灯"类型,返回无效坐标 + if (type_ == "按钮" || type_ == "指示灯") { + return QPointF(std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN()); + } + + // 其他类型正常返回连接点位置 return mapToScene(type == Left ? QPointF(-18, 0) : QPointF(18, 0)); } void Item::addConnection(Connection* conn) { + // 对于"按钮"和"指示灯"类型,不允许添加连接 + if (type_ == "按钮" || type_ == "指示灯") + { + return; + } if (!connections_.contains(conn)) + { connections_.append(conn); + } } void Item::removeConnection(Connection* conn) @@ -108,6 +58,27 @@ QString Item::itemType() return type_; } +void Item::MenuActions(QMenu *menu) +{ + menu->addAction("复制"); + menu->addAction("删除"); +} + +void Item::addMenuActions(QMenu *menu) +{ + Q_UNUSED(menu); +} + +void Item::handleMenuAction(QAction *action) +{ + if (action->text() == "复制") { + emit requestCopy(this); + } + else if (action->text() == "删除") { + emit requestDelete(this); + } +} + QVariant Item::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == QGraphicsItem::ItemPositionChange) { @@ -119,14 +90,27 @@ QVariant Item::itemChange(GraphicsItemChange change, const QVariant &value) void Item::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { +// QMenu menu; +// QAction* copyAct = menu.addAction("复制"); +// QAction* deleteAct = menu.addAction("删除"); +// QAction* selected = menu.exec(event->screenPos()); +// if (selected == copyAct) { +// emit requestCopy(this); +// } +// if (selected == deleteAct) { +// emit requestDelete(this); +// } QMenu menu; - QAction* copyAct = menu.addAction("复制"); - QAction* deleteAct = menu.addAction("删除"); - QAction* selected = menu.exec(event->screenPos()); - if (selected == copyAct) { - emit requestCopy(this); - } - if (selected == deleteAct) { - emit requestDelete(this); + + // 创建菜单 + MenuActions(&menu); + addMenuActions(&menu); + + // 执行菜单 + QAction *selected = menu.exec(event->screenPos()); + + // 处理菜单动作 + if (selected) { + handleMenuAction(selected); } } diff --git a/item.h b/item.h index b33acae..34fdce8 100644 --- a/item.h +++ b/item.h @@ -4,6 +4,7 @@ #include #include #include +#include class Connection; @@ -15,15 +16,14 @@ public: explicit Item(const QString &type, QGraphicsItem *parent = nullptr); QRectF boundingRect() const override; -// void paint(QPainter *painter, -// const QStyleOptionGraphicsItem *option, -// QWidget *widget) override; - QPointF anchorPos(AnchorType type) const; void addConnection(Connection* conn); void removeConnection(Connection* conn); QList connections(); QString itemType(); + virtual void MenuActions(QMenu *menu); // 添加基本菜单项 + virtual void addMenuActions(QMenu *menu); // 添加额外菜单项 + virtual void handleMenuAction(QAction *action); // 处理菜单动作 signals: void requestCopy(Item*); diff --git a/light.cpp b/light.cpp new file mode 100644 index 0000000..633838e --- /dev/null +++ b/light.cpp @@ -0,0 +1,30 @@ +#include "light.h" +#include +#include + +Light::Light(const QString &type) : Item(type) +{ + +} + +QRectF Light::boundingRect() const +{ + return QRectF(0, 0, 50, 50); +} + +void Light::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) +{ + painter->setRenderHint(QPainter::Antialiasing); + + if (type_ == "指示灯") { + painter->drawEllipse(0,0,50,50); + } + if (option->state & QStyle::State_Selected) { + QPen pen(Qt::DashLine); + pen.setColor(Qt::blue); + pen.setWidth(2); + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + painter->drawRect(boundingRect()); + } +} diff --git a/light.h b/light.h new file mode 100644 index 0000000..110a5e0 --- /dev/null +++ b/light.h @@ -0,0 +1,15 @@ +#ifndef LIGHT_H +#define LIGHT_H +#include "item.h" + +class Light : public Item +{ +public: + Light(const QString &type); + QRectF boundingRect() const override; + void paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *) override; +}; + +#endif // LIGHT_H diff --git a/mainwindow.cpp b/mainwindow.cpp index 86f7f65..c102b1e 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "creatitem.h" MainWindow::MainWindow(QWidget *parent) @@ -17,21 +18,12 @@ MainWindow::MainWindow(QWidget *parent) , ui(new Ui::MainWindow) { ui->setupUi(this); - - /* 1. 场景 */ - m_scene = new QGraphicsScene(this); - ui->graphicsView->setScene(m_scene); - ui->graphicsView->setSceneRect(0, 0, 800, 600); - ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag); - - /* 2. 列表 */ - ui->listWidget->setViewMode(QListView::IconMode); - ui->listWidget->setIconSize(QSize(60, 30)); - ui->listWidget->setDragEnabled(false); - ui->listWidget->viewport()->installEventFilter(this); - - /* 3. 填充列表 */ - createComponents(); + plc_ = new PLC(ui->widget); + hmi_ = new HMI(ui->widget); + plc_->show(); + hmi_->hide(); + connect(ui->action_plc,&QAction::triggered,this,&MainWindow::plcChange); + connect(ui->action_hmi,&QAction::triggered,this,&MainWindow::hmiChange); } MainWindow::~MainWindow() @@ -39,121 +31,15 @@ MainWindow::~MainWindow() delete ui; } -/* ---------- 拖拽核心 ---------- */ -bool MainWindow::eventFilter(QObject *obj, QEvent *event) +void MainWindow::plcChange() { - 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 QMainWindow::eventFilter(obj, event); + plc_->show(); + hmi_->hide(); } -/* ---------- 填充列表 ---------- */ -void MainWindow::createComponents() +void MainWindow::hmiChange() { - 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); - } + hmi_->show(); + plc_->hide(); } -void MainWindow::on_listWidget_currentTextChanged(const QString &text) -{ - selectedComponentType = text; -} - -void MainWindow::on_pushButton_clicked() -{ - // 1. 找场景中被选中的连线 - QList selectedItems = ui->graphicsView->scene()->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); - ui->graphicsView->scene()->removeItem(selectedConn); - delete selectedConn; - - // 4. 插入新元件 - Item* newItem = creatItem(selectedComponentType); - newItem->setPos(insertPos); - connect(newItem, &Item::requestCopy, ui->graphicsView, &MyGraphicsView::onItemRequestCopy); - connect(newItem, &Item::requestDelete, ui->graphicsView, &MyGraphicsView::onItemRequestDelete); - ui->graphicsView->scene()->addItem(newItem); - - // 5. 新建两条连线 - Connection* c1 = new Connection(from, fromType, newItem, Item::Left); - ui->graphicsView->scene()->addItem(c1); - Connection* c2 = new Connection(newItem, Item::Right, to, toType); - ui->graphicsView->scene()->addItem(c2); -} diff --git a/mainwindow.h b/mainwindow.h index e473d57..d357df0 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -2,6 +2,8 @@ #define MAINWINDOW_H #include +#include +#include QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -16,20 +18,14 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); -protected: - bool eventFilter(QObject *obj, QEvent *event) override; - private slots: - void on_listWidget_currentTextChanged(const QString ¤tText); - - void on_pushButton_clicked(); + void plcChange(); + void hmiChange(); private: - void createComponents(); - Ui::MainWindow *ui; - QGraphicsScene *m_scene; - QString selectedComponentType; + PLC *plc_; + HMI *hmi_; }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index aef3cbb..73fda7d 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -7,49 +7,23 @@ 0 0 1012 - 656 + 678 MainWindow - + - 20 - 10 - 171 - 491 + 10 + 0 + 981 + 561 - - - - 210 - 10 - 761 - 491 - - - - QGraphicsView::NoDrag - - - - - - 30 - 510 - 93 - 28 - - - - 插入 - - @@ -64,36 +38,47 @@ 文件 - - - + + + + + + + 页面切换 + + + + - + New - + Open - + Save + + + PLC + + + + + HMI + + - - - MyGraphicsView - QGraphicsView -
mygraphicsview.h
-
-
diff --git a/plc.cpp b/plc.cpp new file mode 100644 index 0000000..0be8e03 --- /dev/null +++ b/plc.cpp @@ -0,0 +1,158 @@ +#include "plc.h" +#include "ui_plc.h" +#include "item.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "creatitem.h" + +PLC::PLC(QWidget *parent) : + QWidget(parent), + ui(new Ui::PLC) +{ + ui->setupUi(this); + /* 1. 场景 */ + m_scene = new QGraphicsScene(this); + ui->graphicsView->setScene(m_scene); + ui->graphicsView->setSceneRect(0, 0, 800, 600); + ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag); + + /* 2. 列表 */ + ui->listWidget->setViewMode(QListView::IconMode); + ui->listWidget->setIconSize(QSize(60, 30)); + ui->listWidget->setDragEnabled(false); + ui->listWidget->viewport()->installEventFilter(this); + + /* 3. 填充列表 */ + createComponents(); + connect(ui->listWidget,&QListWidget::currentTextChanged,this,&PLC::onListwidgetCurrenttextchanged); + connect(ui->btn_insert,&QPushButton::clicked,this,&PLC::btnInsertClicked); +} + +PLC::~PLC() +{ + delete ui; +} + +bool PLC::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 PLC::createComponents() +{ + 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); + } +} + +void PLC::onListwidgetCurrenttextchanged(const QString &text) +{ + selectedComponentType = text; +} + +void PLC::btnInsertClicked() +{ + // 1. 找场景中被选中的连线 + QList selectedItems = ui->graphicsView->scene()->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); + ui->graphicsView->scene()->removeItem(selectedConn); + delete selectedConn; + + // 4. 插入新元件 + Item* newItem = creatItem(selectedComponentType); + newItem->setPos(insertPos); + connect(newItem, &Item::requestCopy, ui->graphicsView, &MyGraphicsView::onItemRequestCopy); + connect(newItem, &Item::requestDelete, ui->graphicsView, &MyGraphicsView::onItemRequestDelete); + ui->graphicsView->scene()->addItem(newItem); + + // 5. 新建两条连线 + Connection* c1 = new Connection(from, fromType, newItem, Item::Left); + ui->graphicsView->scene()->addItem(c1); + Connection* c2 = new Connection(newItem, Item::Right, to, toType); + ui->graphicsView->scene()->addItem(c2); +} diff --git a/plc.h b/plc.h new file mode 100644 index 0000000..c56b766 --- /dev/null +++ b/plc.h @@ -0,0 +1,33 @@ +#ifndef PLC_H +#define PLC_H + +#include +#include + +namespace Ui { +class PLC; +} + +class PLC : public QWidget +{ + Q_OBJECT + +public: + explicit PLC(QWidget *parent = nullptr); + ~PLC(); + void createComponents(); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private slots: + void onListwidgetCurrenttextchanged(const QString ¤tText); + void btnInsertClicked(); + +private: + Ui::PLC *ui; + QGraphicsScene *m_scene; + QString selectedComponentType; +}; + +#endif // PLC_H diff --git a/plc.ui b/plc.ui new file mode 100644 index 0000000..33a7d94 --- /dev/null +++ b/plc.ui @@ -0,0 +1,62 @@ + + + PLC + + + + 0 + 0 + 974 + 536 + + + + Form + + + + + 10 + 30 + 171 + 451 + + + + + + + 200 + 30 + 761 + 451 + + + + QGraphicsView::NoDrag + + + + + + 10 + 0 + 93 + 28 + + + + 插入 + + + + + + MyGraphicsView + QGraphicsView +
mygraphicsview.h
+
+
+ + +