Browse Source

多个元件的复制粘贴

master
鹏鹏 李 2 days ago
parent
commit
a48166da94
10 changed files with 216 additions and 100 deletions
  1. +1
    -3
      button.cpp
  2. +29
    -1
      contact.cpp
  3. +2
    -0
      contact.h
  4. +1
    -1
      editor.pro.user
  5. +0
    -19
      item.cpp
  6. +1
    -1
      light.cpp
  7. +6
    -25
      mainwindow.cpp
  8. +34
    -12
      mainwindow.ui
  9. +114
    -30
      mygraphicsview.cpp
  10. +28
    -8
      mygraphicsview.h

+ 1
- 3
button.cpp View File

@@ -26,6 +26,7 @@ void Button::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW
painter->drawPixmap(0, 0, currentSize_.width(), currentSize_.height(), customPixmap_); painter->drawPixmap(0, 0, currentSize_.width(), currentSize_.height(), customPixmap_);
} }
else{ else{
painter->setBrush(Qt::green);
painter->drawRect(0, 0, currentSize_.width(), currentSize_.height()); painter->drawRect(0, 0, currentSize_.width(), currentSize_.height());
} }
} }
@@ -120,9 +121,6 @@ void Button::loadImage()
{ {
if(!imagePath_.isEmpty()){ if(!imagePath_.isEmpty()){
customPixmap_ = QPixmap(imagePath_); customPixmap_ = QPixmap(imagePath_);
// if (!customPixmap_.isNull()){
// customPixmap_ = customPixmap_.scaled(50, 50, Qt::KeepAspectRatio, Qt::SmoothTransformation);
// }
} }
} }




+ 29
- 1
contact.cpp View File

@@ -1,6 +1,7 @@
#include "contact.h" #include "contact.h"
#include <QPainter> #include <QPainter>
#include <QStyleOptionGraphicsItem> #include <QStyleOptionGraphicsItem>
#include <QMenu>


Contact::Contact(const QString &type) : Item(type) Contact::Contact(const QString &type) : Item(type)
{ {
@@ -45,7 +46,6 @@ void Contact::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q
painter->drawEllipse(QPointF(-18, 0), 4, 4); // 左 painter->drawEllipse(QPointF(-18, 0), 4, 4); // 左
painter->drawEllipse(QPointF(18, 0), 4, 4); // 右 painter->drawEllipse(QPointF(18, 0), 4, 4); // 右
} }

if (!registerId_.isEmpty()) { if (!registerId_.isEmpty()) {
painter->save(); painter->save();
painter->setFont(QFont("Arial", 8)); painter->setFont(QFont("Arial", 8));
@@ -69,3 +69,31 @@ bool Contact::state() const
{ {
return registerValue_ > 0; return registerValue_ > 0;
} }

void Contact::addMenuActions(QMenu *menu)
{
menu->addAction("置ON");
menu->addAction("置OFF");
}

void Contact::handleMenuAction(QAction *action)
{
if (action->text() == "复制") {
emit requestCopy(this);
}
if (action->text() == "删除") {
emit requestDelete(this);
}
if (action->text() == "对象"){
emit requestBindRegister(this);
}
if (action->text() == "重置"){
emit requestReset(this);
}
if (action->text() == "置ON"){
emit requestSetON(this, true);
}
if (action->text() == "置OFF"){
emit requestSetON(this, false);
}
}

+ 2
- 0
contact.h View File

@@ -10,6 +10,8 @@ public:
const QStyleOptionGraphicsItem *option, const QStyleOptionGraphicsItem *option,
QWidget *) override; QWidget *) override;
bool state() const override; bool state() const override;
void addMenuActions(QMenu *menu) override;
void handleMenuAction(QAction *action) override;
}; };


#endif // CONTACT_H #endif // CONTACT_H

+ 1
- 1
editor.pro.user View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.11.1, 2025-08-12T22:16:53. -->
<!-- Written by QtCreator 4.11.1, 2025-08-13T11:12:10. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>


+ 0
- 19
item.cpp View File

@@ -121,25 +121,6 @@ void Item::handleMenuAction(QAction *action)


QVariant Item::itemChange(GraphicsItemChange change, const QVariant &value) QVariant Item::itemChange(GraphicsItemChange change, const QVariant &value)
{ {
// if (change == QGraphicsItem::ItemPositionChange) {
// for (Connection* conn : connections_)
// conn->updatePosition();
// }
// if (change == ItemPositionChange && scene()) {
// // 获取场景(确保是 GridScene)
// if (auto myScene = dynamic_cast<MyScene*>(scene())) {
// if (myScene->getSnapToGrid()) {
// // 获取网格大小
// int gridSize = myScene->getGridSize();

// // 对齐到网格
// QPointF newPos = value.toPointF();
// qreal xV = qRound(newPos.x() / gridSize) * gridSize;
// qreal yV = qRound(newPos.y() / gridSize) * gridSize;
// return QPointF(xV, yV);
// }
// }
// }
// 处理位置即将改变事件(网格对齐) // 处理位置即将改变事件(网格对齐)
if (change == ItemPositionChange && scene()) { if (change == ItemPositionChange && scene()) {
QPointF newPos = value.toPointF(); QPointF newPos = value.toPointF();


+ 1
- 1
light.cpp View File

@@ -29,7 +29,7 @@ void Light::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWi
if (state()) { if (state()) {
painter->setBrush(Qt::green); // 激活状态 painter->setBrush(Qt::green); // 激活状态
} else { } else {
painter->setBrush(Qt::white); // 未激活状态
painter->setBrush(Qt::red); // 未激活状态
} }
painter->drawEllipse(0, 0, currentSize_.width(), currentSize_.height()); painter->drawEllipse(0, 0, currentSize_.width(), currentSize_.height());
} }


+ 6
- 25
mainwindow.cpp View File

@@ -102,6 +102,8 @@ void MainWindow::newPage(bool isPlc)
registerManager, &RegisterManager::bindItem); registerManager, &RegisterManager::bindItem);
connect(newView, &MyGraphicsView::itemResetRegister, connect(newView, &MyGraphicsView::itemResetRegister,
registerManager, &RegisterManager::unbindItem); registerManager, &RegisterManager::unbindItem);
connect(newView, &MyGraphicsView::itemSetON,
modbusManager, &ModbusManager::writeRegister);
} else { } else {
// 创建新场景 // 创建新场景
QGraphicsScene* newScene = new QGraphicsScene(this); QGraphicsScene* newScene = new QGraphicsScene(this);
@@ -437,18 +439,6 @@ void MainWindow::hmiChange()


void MainWindow::newProject() void MainWindow::newProject()
{ {
// // 检查当前tabwidget是否只有一个页面且页面内容为空
// if (currentIsPLC_ && plcScenes.size() == 1 && plcScenes[0]->items().isEmpty()) {
// // 复用第一个页面
// clearScene();
// setWindowTitle("PLC编辑器");
// return;
// }
// if (!currentIsPLC_ && hmiScenes.size() == 1 && hmiScenes[0]->items().isEmpty()) {
// clearScene();
// setWindowTitle("HMI编辑器");
// return;
// }
newPage(currentIsPLC_); newPage(currentIsPLC_);


// 更新窗口标题 // 更新窗口标题
@@ -476,18 +466,9 @@ void MainWindow::openProject()


Project project; Project project;
if (project.loadFromFile(filePath)) { if (project.loadFromFile(filePath)) {
// // 在当前模块新建一个页面
// newPage(currentIsPLC_);
// int newPageIndex = currentPageIndex();
int newPageIndex = -1;
if (currentIsPLC_ && plcScenes.size() == 1 && plcScenes[0]->items().isEmpty()) {
newPageIndex = 0;
} else if (!currentIsPLC_ && hmiScenes.size() == 1 && hmiScenes[0]->items().isEmpty()) {
newPageIndex = 0;
} else {
newPage(currentIsPLC_);
newPageIndex = currentPageIndex();
}
// 在当前模块新建一个页面
newPage(currentIsPLC_);
int newPageIndex = currentPageIndex();


// 将项目应用到新页面 // 将项目应用到新页面
applyProjectToScene(project, newPageIndex); applyProjectToScene(project, newPageIndex);
@@ -553,7 +534,7 @@ void MainWindow::saveProject()
void MainWindow::connection() void MainWindow::connection()
{ {
modbusManager->connectToDevice("COM1",QSerialPort::BaudRate(9600),QSerialPort::DataBits(8),QSerialPort::EvenParity,QSerialPort::StopBits(1)); modbusManager->connectToDevice("COM1",QSerialPort::BaudRate(9600),QSerialPort::DataBits(8),QSerialPort::EvenParity,QSerialPort::StopBits(1));
modbusManager->startSimulation(1000);
modbusManager->startSimulation();
} }


void MainWindow::disconnection() void MainWindow::disconnection()


+ 34
- 12
mainwindow.ui View File

@@ -19,7 +19,7 @@
<rect> <rect>
<x>20</x> <x>20</x>
<y>500</y> <y>500</y>
<width>951</width>
<width>941</width>
<height>111</height> <height>111</height>
</rect> </rect>
</property> </property>
@@ -28,18 +28,18 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>20</x> <x>20</x>
<y>30</y>
<width>181</width>
<height>461</height>
<y>40</y>
<width>91</width>
<height>451</height>
</rect> </rect>
</property> </property>
</widget> </widget>
<widget class="QStackedWidget" name="stackedWidget"> <widget class="QStackedWidget" name="stackedWidget">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>210</x>
<x>110</x>
<y>10</y> <y>10</y>
<width>761</width>
<width>861</width>
<height>481</height> <height>481</height>
</rect> </rect>
</property> </property>
@@ -47,9 +47,9 @@
<widget class="QTabWidget" name="plc_tab_widget"> <widget class="QTabWidget" name="plc_tab_widget">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x>
<x>10</x>
<y>0</y> <y>0</y>
<width>761</width>
<width>841</width>
<height>481</height> <height>481</height>
</rect> </rect>
</property> </property>
@@ -62,9 +62,9 @@
<widget class="QTabWidget" name="hmi_tab_widget"> <widget class="QTabWidget" name="hmi_tab_widget">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x>
<x>10</x>
<y>0</y> <y>0</y>
<width>761</width>
<width>841</width>
<height>481</height> <height>481</height>
</rect> </rect>
</property> </property>
@@ -77,8 +77,8 @@
<widget class="QPushButton" name="btn_insert"> <widget class="QPushButton" name="btn_insert">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>30</x>
<y>0</y>
<x>20</x>
<y>10</y>
<width>93</width> <width>93</width>
<height>28</height> <height>28</height>
</rect> </rect>
@@ -87,6 +87,28 @@
<string>插入</string> <string>插入</string>
</property> </property>
</widget> </widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>2</width>
<height>2</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout"/>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>2</width>
<height>2</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</widget> </widget>
<widget class="QMenuBar" name="menubar"> <widget class="QMenuBar" name="menubar">
<property name="geometry"> <property name="geometry">


+ 114
- 30
mygraphicsview.cpp View File

@@ -84,27 +84,49 @@ void MyGraphicsView::keyPressEvent(QKeyEvent *event)
} }
}else if (event->matches(QKeySequence::Copy)) { }else if (event->matches(QKeySequence::Copy)) {
// Ctrl+C // Ctrl+C
clipboard_.items.clear();
clipboard_.connections.clear();

QList<QGraphicsItem*> selectedItems = scene()->selectedItems(); QList<QGraphicsItem*> selectedItems = scene()->selectedItems();
for (QGraphicsItem* item : selectedItems) {
if (auto node = dynamic_cast<Item*>(item)) {
clipboard_.input = node->itemType();
break;

// 过滤出Item类型的元件
QList<Item*> items;
for (QGraphicsItem* gi : selectedItems) {
if (auto item = dynamic_cast<Item*>(gi)) {
items.append(item);
}
}

// 计算中心点
clipboard_.center = calculateItemsCenter(selectedItems);

// 保存元件信息
for (Item* item : items) {
CopiedItem copiedItem;
copiedItem.type = item->itemType();
copiedItem.pos = item->pos() - clipboard_.center;
clipboard_.items.append(copiedItem);
}

// 保存连接关系
for (QGraphicsItem* gi : selectedItems) {
if (auto conn = dynamic_cast<Connection*>(gi)) {
int fromIdx = items.indexOf(conn->from_);
int toIdx = items.indexOf(conn->to_);

if (fromIdx >= 0 && toIdx >= 0) {
CopiedConnection copiedConn;
copiedConn.fromIndex = fromIdx;
copiedConn.toIndex = toIdx;
copiedConn.fromType = conn->fromType_;
copiedConn.toType = conn->toType_;
clipboard_.connections.append(copiedConn);
}
} }
} }
} else if (event->matches(QKeySequence::Paste)) { } else if (event->matches(QKeySequence::Paste)) {
// Ctrl+V // Ctrl+V
if (!clipboard_.input.isEmpty()) {
QPointF center = mapToScene(viewport()->rect().center());
Item* newItem = creatItem(clipboard_.input);
newItem->setPos(center);
connect(newItem, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy);
connect(newItem, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete);
connect(newItem, &Item::requestBindRegister, this, &MyGraphicsView::onItemRequestBindRegister);
connect(newItem, &Item::requestCompare, this, &MyGraphicsView::onItemRequestCompare);
connect(newItem, &Item::requestReset, this, &MyGraphicsView::onItemRequestReset);
connect(newItem, &Item::requestSetON,this, &MyGraphicsView::onItemRequestSetON);
scene()->addItem(newItem);
}
performPaste(mapToScene(viewport()->rect().center()));
} else { } else {
QGraphicsView::keyPressEvent(event); QGraphicsView::keyPressEvent(event);
} }
@@ -114,23 +136,21 @@ void MyGraphicsView::contextMenuEvent(QContextMenuEvent *event)
{ {
QPointF scenePos = mapToScene(event->pos()); QPointF scenePos = mapToScene(event->pos());
QList<QGraphicsItem*> itemsAt = scene()->items(scenePos); QList<QGraphicsItem*> itemsAt = scene()->items(scenePos);
if (itemsAt.isEmpty() && !clipboard_.input.isEmpty()) {
QMenu menu;

// 如果有选中的元件,显示元件自己的菜单
if (!itemsAt.isEmpty()) {
QGraphicsView::contextMenuEvent(event);
return;
}

// 没有选中元件时,显示粘贴菜单(如果剪贴板有内容)
QMenu menu;
if (!clipboard_.items.isEmpty()) {
QAction* pasteAct = menu.addAction("粘贴"); QAction* pasteAct = menu.addAction("粘贴");
QAction* sel = menu.exec(event->globalPos()); QAction* sel = menu.exec(event->globalPos());
if (sel == pasteAct) { if (sel == pasteAct) {
Item* newItem = creatItem(clipboard_.input);
newItem->setPos(scenePos);
connect(newItem, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy);
connect(newItem, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete);
connect(newItem, &Item::requestBindRegister, this, &MyGraphicsView::onItemRequestBindRegister);
connect(newItem, &Item::requestCompare, this, &MyGraphicsView::onItemRequestCompare);
connect(newItem, &Item::requestReset, this, &MyGraphicsView::onItemRequestReset);
connect(newItem, &Item::requestSetON,this, &MyGraphicsView::onItemRequestSetON);
scene()->addItem(newItem);
performPaste(scenePos); // 在鼠标位置粘贴
} }
} else {
QGraphicsView::contextMenuEvent(event); // 让Item自己弹出菜单
} }
} }


@@ -156,9 +176,29 @@ Item* MyGraphicsView::anchorItemAt(const QPoint& viewPos, Item::AnchorType& anch
return nullptr; return nullptr;
} }


QPointF MyGraphicsView::calculateItemsCenter(const QList<QGraphicsItem *> &items)
{
QPointF center(0, 0);
int count = 0;

for (QGraphicsItem* item : items) {
if (auto node = dynamic_cast<Item*>(item)) {
center += node->pos();
count++;
}
}

return count > 0 ? center / count : QPointF(0, 0);
}

void MyGraphicsView::onItemRequestCopy(Item *item) void MyGraphicsView::onItemRequestCopy(Item *item)
{ {
clipboard_.input = item->itemType();
clipboard_.items.clear();
clipboard_.connections.clear();

CopiedItem copiedItem;
copiedItem.type = item->itemType();
clipboard_.items.append(copiedItem);
} }


void MyGraphicsView::onItemRequestDelete(Item *item) void MyGraphicsView::onItemRequestDelete(Item *item)
@@ -360,3 +400,47 @@ void MyGraphicsView::wheelEvent(QWheelEvent *event)
QGraphicsView::wheelEvent(event); QGraphicsView::wheelEvent(event);
} }
} }

void MyGraphicsView::performPaste(const QPointF &pasteCenter)
{
if (clipboard_.items.isEmpty()) return;

QList<Item*> newItems;

// 创建新元件
for (const CopiedItem& copiedItem : clipboard_.items) {
Item* newItem = creatItem(copiedItem.type);
if (!newItem) continue;

// 设置位置(相对于粘贴中心)
QPointF newPos = pasteCenter + copiedItem.pos;
newItem->setPos(newPos);

// 连接信号
connect(newItem, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy);
connect(newItem, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete);
connect(newItem, &Item::requestBindRegister, this, &MyGraphicsView::onItemRequestBindRegister);
connect(newItem, &Item::requestCompare, this, &MyGraphicsView::onItemRequestCompare);
connect(newItem, &Item::requestReset, this, &MyGraphicsView::onItemRequestReset);
connect(newItem, &Item::requestSetON, this, &MyGraphicsView::onItemRequestSetON);

scene()->addItem(newItem);
newItems.append(newItem);
}

// 创建新连接
for (const CopiedConnection& copiedConn : clipboard_.connections) {
if (copiedConn.fromIndex < newItems.size() && copiedConn.toIndex < newItems.size()) {
Connection* conn = new Connection(
newItems[copiedConn.fromIndex], copiedConn.fromType,
newItems[copiedConn.toIndex], copiedConn.toType);
scene()->addItem(conn);
}
}

// 选中新粘贴的元件
scene()->clearSelection();
for (Item* item : newItems) {
item->setSelected(true);
}
}

+ 28
- 8
mygraphicsview.h View File

@@ -26,12 +26,21 @@ public:
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override; void wheelEvent(QWheelEvent *event) override;
void performPaste(const QPointF& pasteCenter);


signals: signals:
void itemBoundToRegister(Item*, QString); void itemBoundToRegister(Item*, QString);
void itemResetRegister(Item*, QString); void itemResetRegister(Item*, QString);
void itemSetON(QString, quint16); void itemSetON(QString, quint16);


public slots:
void onItemRequestCopy(Item*);
void onItemRequestDelete(Item*);
void onItemRequestBindRegister(Item*);
void onItemRequestCompare(Item*);
void onItemRequestReset(Item*);
void onItemRequestSetON(Item*, bool isON);

private: private:
Item* anchorItemAt(const QPoint& viewPos, Item::AnchorType& anchorType); Item* anchorItemAt(const QPoint& viewPos, Item::AnchorType& anchorType);


@@ -40,18 +49,29 @@ private:
Item::AnchorType startAnchor_; Item::AnchorType startAnchor_;
QGraphicsLineItem* tempLine_ = nullptr; QGraphicsLineItem* tempLine_ = nullptr;


// struct ClipInfo {
// QString input;
// };
struct CopiedItem {
QString type;
QPointF pos;
};

struct CopiedConnection {
int fromIndex;
int toIndex;
Item::AnchorType fromType;
Item::AnchorType toType;
};

struct ClipInfo { struct ClipInfo {
QString input;
QList<CopiedItem> items;
QList<CopiedConnection> connections;
QPointF center;
}; };
static ClipInfo clipboard_; static ClipInfo clipboard_;
QPointF calculateItemsCenter(const QList<QGraphicsItem*>& items);


public slots:
void onItemRequestCopy(Item*);
void onItemRequestDelete(Item*);
void onItemRequestBindRegister(Item*);
void onItemRequestCompare(Item*);
void onItemRequestReset(Item*);
void onItemRequestSetON(Item*, bool isON);
}; };


#endif // MYGRAPHICSVIEW_H #endif // MYGRAPHICSVIEW_H

Loading…
Cancel
Save