瀏覽代碼

增加了HMI编辑界面

master
鹏鹏 李 2 天之前
父節點
當前提交
1b532108e1
共有 19 個檔案被更改,包括 657 行新增262 行删除
  1. +30
    -0
      button.cpp
  2. +15
    -0
      button.h
  3. +2
    -0
      creatitem.cpp
  4. +2
    -0
      creatitem.h
  5. +13
    -3
      editor.pro
  6. +1
    -1
      editor.pro.user
  7. +106
    -0
      hmi.cpp
  8. +32
    -0
      hmi.h
  9. +49
    -0
      hmi.ui
  10. +56
    -72
      item.cpp
  11. +4
    -4
      item.h
  12. +30
    -0
      light.cpp
  13. +15
    -0
      light.h
  14. +13
    -127
      mainwindow.cpp
  15. +6
    -10
      mainwindow.h
  16. +30
    -45
      mainwindow.ui
  17. +158
    -0
      plc.cpp
  18. +33
    -0
      plc.h
  19. +62
    -0
      plc.ui

+ 30
- 0
button.cpp 查看文件

@@ -0,0 +1,30 @@
#include "button.h"
#include <QPainter>
#include <QStyleOptionGraphicsItem>

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());
}
}

+ 15
- 0
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

+ 2
- 0
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;
}

+ 2
- 0
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);



+ 13
- 3
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


+ 1
- 1
editor.pro.user 查看文件

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.11.1, 2025-08-06T20:12:05. -->
<!-- Written by QtCreator 4.11.1, 2025-08-07T09:52:05. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>


+ 106
- 0
hmi.cpp 查看文件

@@ -0,0 +1,106 @@
#include "hmi.h"
#include "ui_hmi.h"
#include <QListWidgetItem>
#include <QMimeData>
#include <QDrag>
#include <QPainter>
#include <QMouseEvent>
#include <QDropEvent>
#include <QGraphicsView>
#include <QMessageBox>
#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<QMouseEvent*>(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<QMouseEvent*>(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<Comp> 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;
}

+ 32
- 0
hmi.h 查看文件

@@ -0,0 +1,32 @@
#ifndef HMI_H
#define HMI_H

#include <QGraphicsScene>
#include <QWidget>

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 &currentText);

private:
Ui::HMI *ui;
QGraphicsScene *m_scene;
QString selectedComponentType;
};

#endif // HMI_H

+ 49
- 0
hmi.ui 查看文件

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HMI</class>
<widget class="QWidget" name="HMI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>964</width>
<height>550</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="MyGraphicsView" name="graphicsView">
<property name="geometry">
<rect>
<x>200</x>
<y>30</y>
<width>761</width>
<height>451</height>
</rect>
</property>
<property name="dragMode">
<enum>QGraphicsView::NoDrag</enum>
</property>
</widget>
<widget class="QListWidget" name="listWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>171</width>
<height>451</height>
</rect>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>MyGraphicsView</class>
<extends>QGraphicsView</extends>
<header>mygraphicsview.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

+ 56
- 72
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<qreal>::quiet_NaN(),
std::numeric_limits<qreal>::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);
}
}

+ 4
- 4
item.h 查看文件

@@ -4,6 +4,7 @@
#include <QGraphicsObject>
#include <QColor>
#include <QList>
#include <QAction>

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<Connection*> connections();
QString itemType();
virtual void MenuActions(QMenu *menu); // 添加基本菜单项
virtual void addMenuActions(QMenu *menu); // 添加额外菜单项
virtual void handleMenuAction(QAction *action); // 处理菜单动作

signals:
void requestCopy(Item*);


+ 30
- 0
light.cpp 查看文件

@@ -0,0 +1,30 @@
#include "light.h"
#include <QPainter>
#include <QStyleOptionGraphicsItem>

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());
}
}

+ 15
- 0
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

+ 13
- 127
mainwindow.cpp 查看文件

@@ -10,6 +10,7 @@
#include <QDropEvent>
#include <QGraphicsView>
#include <QMessageBox>
#include <QAction>
#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<QMouseEvent*>(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<QMouseEvent*>(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<Comp> 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<QGraphicsItem*> selectedItems = ui->graphicsView->scene()->selectedItems();
Connection* selectedConn = nullptr;
for (QGraphicsItem* item : selectedItems) {
selectedConn = dynamic_cast<Connection*>(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);
}

+ 6
- 10
mainwindow.h 查看文件

@@ -2,6 +2,8 @@
#define MAINWINDOW_H

#include <QMainWindow>
#include <plc.h>
#include <hmi.h>

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 &currentText);

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

+ 30
- 45
mainwindow.ui 查看文件

@@ -7,49 +7,23 @@
<x>0</x>
<y>0</y>
<width>1012</width>
<height>656</height>
<height>678</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QListWidget" name="listWidget">
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>171</width>
<height>491</height>
<x>10</x>
<y>0</y>
<width>981</width>
<height>561</height>
</rect>
</property>
</widget>
<widget class="MyGraphicsView" name="graphicsView">
<property name="geometry">
<rect>
<x>210</x>
<y>10</y>
<width>761</width>
<height>491</height>
</rect>
</property>
<property name="dragMode">
<enum>QGraphicsView::NoDrag</enum>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>30</x>
<y>510</y>
<width>93</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>插入</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
@@ -64,36 +38,47 @@
<property name="title">
<string>文件</string>
</property>
<addaction name="actionnew"/>
<addaction name="actionOpen"/>
<addaction name="actionSave"/>
<addaction name="action_new"/>
<addaction name="action_open"/>
<addaction name="action_save"/>
</widget>
<widget class="QMenu" name="menu_2">
<property name="title">
<string>页面切换</string>
</property>
<addaction name="action_plc"/>
<addaction name="action_hmi"/>
</widget>
<addaction name="menu"/>
<addaction name="menu_2"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionnew">
<action name="action_new">
<property name="text">
<string>New</string>
</property>
</action>
<action name="actionOpen">
<action name="action_open">
<property name="text">
<string>Open</string>
</property>
</action>
<action name="actionSave">
<action name="action_save">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="action_plc">
<property name="text">
<string>PLC</string>
</property>
</action>
<action name="action_hmi">
<property name="text">
<string>HMI</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>MyGraphicsView</class>
<extends>QGraphicsView</extends>
<header>mygraphicsview.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

+ 158
- 0
plc.cpp 查看文件

@@ -0,0 +1,158 @@
#include "plc.h"
#include "ui_plc.h"
#include "item.h"

#include <QListWidgetItem>
#include <QMimeData>
#include <QDrag>
#include <QPainter>
#include <QMouseEvent>
#include <QDropEvent>
#include <QGraphicsView>
#include <QMessageBox>
#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<QMouseEvent*>(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<QMouseEvent*>(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<Comp> 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<QGraphicsItem*> selectedItems = ui->graphicsView->scene()->selectedItems();
Connection* selectedConn = nullptr;
for (QGraphicsItem* item : selectedItems) {
selectedConn = dynamic_cast<Connection*>(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);
}

+ 33
- 0
plc.h 查看文件

@@ -0,0 +1,33 @@
#ifndef PLC_H
#define PLC_H

#include <QGraphicsScene>
#include <QWidget>

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 &currentText);
void btnInsertClicked();

private:
Ui::PLC *ui;
QGraphicsScene *m_scene;
QString selectedComponentType;
};

#endif // PLC_H

+ 62
- 0
plc.ui 查看文件

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PLC</class>
<widget class="QWidget" name="PLC">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>974</width>
<height>536</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QListWidget" name="listWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>171</width>
<height>451</height>
</rect>
</property>
</widget>
<widget class="MyGraphicsView" name="graphicsView">
<property name="geometry">
<rect>
<x>200</x>
<y>30</y>
<width>761</width>
<height>451</height>
</rect>
</property>
<property name="dragMode">
<enum>QGraphicsView::NoDrag</enum>
</property>
</widget>
<widget class="QPushButton" name="btn_insert">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>93</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>插入</string>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>MyGraphicsView</class>
<extends>QGraphicsView</extends>
<header>mygraphicsview.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

Loading…
取消
儲存