From 71aeff0c6853b3e761692b824673df22e2933ce9 Mon Sep 17 00:00:00 2001 From: lipengpeng Date: Wed, 6 Aug 2025 10:19:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9C=A8=E4=B8=A4=E4=B8=AA=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E4=B9=8B=E9=97=B4=E7=94=9F=E6=88=90=E8=BF=9E=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connection.cpp | 20 ++++++++++ connection.h | 19 ++++++++++ editor.pro | 2 + item.cpp | 49 ++++++++++++++++++++++-- item.h | 15 ++++++++ mainwindow.cpp | 3 -- mainwindow.ui | 8 ++-- mygraphicsview.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++++- mygraphicsview.h | 16 +++++++- 9 files changed, 212 insertions(+), 13 deletions(-) create mode 100644 connection.cpp create mode 100644 connection.h diff --git a/connection.cpp b/connection.cpp new file mode 100644 index 0000000..d0dbc69 --- /dev/null +++ b/connection.cpp @@ -0,0 +1,20 @@ +#include "connection.h" + +#include + +Connection::Connection(Item* from, Item::AnchorType fromType, + Item* to, Item::AnchorType toType, QGraphicsItem* parent) + : QGraphicsLineItem(parent), + from_(from), to_(to), fromType_(fromType), toType_(toType) +{ + setZValue(-1); // 在图元之下 + setPen(QPen(Qt::black, 2)); + from_->addConnection(this); + to_->addConnection(this); + updatePosition(); +} + +void Connection::updatePosition() +{ + setLine(QLineF(from_->anchorPos(fromType_), to_->anchorPos(toType_))); +} diff --git a/connection.h b/connection.h new file mode 100644 index 0000000..e171bfa --- /dev/null +++ b/connection.h @@ -0,0 +1,19 @@ +#ifndef CONNECTION_H +#define CONNECTION_H + +#include +#include "item.h" + +class Connection : public QGraphicsLineItem +{ +public: + Connection(Item* from, Item::AnchorType fromType, + Item* to, Item::AnchorType toType, QGraphicsItem* parent = nullptr); + + void updatePosition(); + + Item *from_, *to_; + Item::AnchorType fromType_, toType_; +}; + +#endif // CONNECTION_H diff --git a/editor.pro b/editor.pro index 1357d6c..40acd73 100644 --- a/editor.pro +++ b/editor.pro @@ -16,12 +16,14 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ + connection.cpp \ item.cpp \ main.cpp \ mainwindow.cpp \ mygraphicsview.cpp HEADERS += \ + connection.h \ item.h \ mainwindow.h \ mygraphicsview.h diff --git a/item.cpp b/item.cpp index e807c91..f14282a 100644 --- a/item.cpp +++ b/item.cpp @@ -1,4 +1,5 @@ #include "item.h" +#include "connection.h" #include #include @@ -17,17 +18,59 @@ Item::Item(const QString &type, QGraphicsItem *parent) QRectF Item::boundingRect() const { - return QRectF(0, 0, 80, 40); + return QRectF(-5, -5, 90, 50); } void Item::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { + QRectF r = boundingRect().adjusted(5, 5, -5, -5); // 图元主体 painter->setBrush(color_.lighter(150)); painter->setPen(QPen(color_, 2)); - painter->drawRoundedRect(boundingRect(), 5, 5); + painter->drawRoundedRect(r, 5, 5); + + // 画锚点 + painter->setBrush(Qt::darkGray); + painter->setPen(Qt::NoPen); + painter->drawEllipse(QPointF(r.left(), r.center().y()), 5, 5); // 左锚点 + painter->drawEllipse(QPointF(r.right(), r.center().y()), 5, 5); // 右锚点 painter->setPen(Qt::black); - painter->drawText(boundingRect(), Qt::AlignCenter, type_); + painter->drawText(r, Qt::AlignCenter, type_); +} + +QPointF Item::anchorPos(AnchorType anc) const +{ + QRectF r = boundingRect(); + switch (anc) { + case Left: return mapToScene(QPointF(r.left(), r.center().y())); + case Right: return mapToScene(QPointF(r.right(), r.center().y())); + } + return QPointF(); +} + +void Item::addConnection(Connection* conn) +{ + if (!connections_.contains(conn)) + connections_.append(conn); +} + +void Item::removeConnection(Connection* conn) +{ + connections_.removeAll(conn); +} + +QList Item::connections() +{ + return connections_; +} + +QVariant Item::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == QGraphicsItem::ItemPositionChange) { + for (Connection* conn : connections_) + conn->updatePosition(); + } + return QGraphicsObject::itemChange(change, value); } diff --git a/item.h b/item.h index aac8d68..4447ea9 100644 --- a/item.h +++ b/item.h @@ -2,11 +2,16 @@ #define ITEM_H #include +#include +#include + +class Connection; class Item : public QGraphicsObject { Q_OBJECT public: + enum AnchorType { Left, Right }; explicit Item(const QString &type, QGraphicsItem *parent = nullptr); QRectF boundingRect() const override; @@ -14,9 +19,19 @@ public: const QStyleOptionGraphicsItem *option, QWidget *widget) override; + QPointF anchorPos(AnchorType type) const; + void addConnection(Connection* conn); + void removeConnection(Connection* conn); + QList connections(); + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + private: QString type_; QColor color_; + QList connections_; + }; #endif // ITEM_H diff --git a/mainwindow.cpp b/mainwindow.cpp index 03118be..d30cc38 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -19,10 +19,7 @@ MainWindow::MainWindow(QWidget *parent) /* 1. 场景 */ m_scene = new QGraphicsScene(this); ui->graphicsView->setScene(m_scene); - ui->graphicsView->setRenderHint(QPainter::Antialiasing); ui->graphicsView->setSceneRect(0, 0, 800, 600); - ui->graphicsView->setAcceptDrops(true); - ui->graphicsView->setFocus(); /* 2. 列表 */ ui->listWidget->setViewMode(QListView::IconMode); diff --git a/mainwindow.ui b/mainwindow.ui index c59adba..5aa6002 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -19,8 +19,8 @@ 20 10 - 161 - 381 + 171 + 491 @@ -29,8 +29,8 @@ 210 10 - 791 - 381 + 871 + 491 diff --git a/mygraphicsview.cpp b/mygraphicsview.cpp index b3584df..9a50c6a 100644 --- a/mygraphicsview.cpp +++ b/mygraphicsview.cpp @@ -10,7 +10,7 @@ MyGraphicsView::MyGraphicsView(QWidget *parent) setAcceptDrops(true); viewport()->setAcceptDrops(true); setRenderHint(QPainter::Antialiasing); - setFocusPolicy(Qt::StrongFocus); // 让view能接收键盘事件 + setFocusPolicy(Qt::StrongFocus); } void MyGraphicsView::dragEnterEvent(QDragEnterEvent *event) @@ -51,9 +51,18 @@ void MyGraphicsView::dropEvent(QDropEvent *event) void MyGraphicsView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Delete && scene()) { - // 删除所有被选中的图元 QList selectedItems = scene()->selectedItems(); for (QGraphicsItem* item : selectedItems) { + // 如果是Item,顺便删除其连线 + if (auto node = dynamic_cast(item)) { + QList conns = node->connections(); + for (Connection* conn : conns) { + scene()->removeItem(conn); + conn->from_->removeConnection(conn); + conn->to_->removeConnection(conn); + delete conn; + } + } scene()->removeItem(item); delete item; } @@ -61,3 +70,83 @@ void MyGraphicsView::keyPressEvent(QKeyEvent *event) QGraphicsView::keyPressEvent(event); } } + +// ----------- 连线交互 ------------ + +Item* MyGraphicsView::anchorItemAt(const QPoint& viewPos, Item::AnchorType& anchorType) +{ + QPointF scenePos = mapToScene(viewPos); + for (QGraphicsItem* item : scene()->items(scenePos)) { + if (Item* node = dynamic_cast(item)) { + QRectF r = node->boundingRect(); + QPointF local = node->mapFromScene(scenePos); + if (QLineF(local, QPointF(r.left(), r.center().y())).length() < 10) { + anchorType = Item::Left; + return node; + } + if (QLineF(local, QPointF(r.right(), r.center().y())).length() < 10) { + anchorType = Item::Right; + return node; + } + } + } + return nullptr; +} + +void MyGraphicsView::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + Item::AnchorType anchor; + Item* node = anchorItemAt(event->pos(), anchor); + if (node) { + drawingConnection_ = true; + startItem_ = node; + startAnchor_ = anchor; + tempLine_ = scene()->addLine(QLineF(node->anchorPos(anchor), mapToScene(event->pos())), + QPen(Qt::darkGray, 2, Qt::DashLine)); + return; + } + } + QGraphicsView::mousePressEvent(event); +} + +void MyGraphicsView::mouseMoveEvent(QMouseEvent *event) +{ + if (drawingConnection_ && tempLine_) { + QLineF l = tempLine_->line(); + l.setP2(mapToScene(event->pos())); + tempLine_->setLine(l); + return; + } + QGraphicsView::mouseMoveEvent(event); +} + +void MyGraphicsView::mouseReleaseEvent(QMouseEvent *event) +{ + if (drawingConnection_ && tempLine_) { + // 检查鼠标释放点是否在某个Item boundingRect内 + QPointF scenePos = mapToScene(event->pos()); + for (QGraphicsItem* item : scene()->items(scenePos)) { + if (Item* node = dynamic_cast(item)) { + QRectF r = node->boundingRect(); + QPointF local = node->mapFromScene(scenePos); + // 计算落点距离左右端点 + double distLeft = QLineF(local, QPointF(r.left(), r.center().y())).length(); + double distRight = QLineF(local, QPointF(r.right(), r.center().y())).length(); + Item::AnchorType anchor = (distLeft < distRight) ? Item::Left : Item::Right; + // 生成连线 + Connection* conn = new Connection(startItem_, startAnchor_, node, anchor); + scene()->addItem(conn); + break; + } + } + // 清除临时线 + scene()->removeItem(tempLine_); + delete tempLine_; + tempLine_ = nullptr; + drawingConnection_ = false; + startItem_ = nullptr; + return; + } + QGraphicsView::mouseReleaseEvent(event); +} diff --git a/mygraphicsview.h b/mygraphicsview.h index e245c5a..a0082b1 100644 --- a/mygraphicsview.h +++ b/mygraphicsview.h @@ -6,7 +6,9 @@ #include #include #include +#include #include "item.h" +#include "connection.h" class MyGraphicsView : public QGraphicsView { @@ -16,9 +18,21 @@ public: protected: void dragEnterEvent(QDragEnterEvent *event) override; - void dropEvent(QDropEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; + void dropEvent(QDropEvent *event) override; void keyPressEvent(QKeyEvent *event) override; + + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + +private: + Item* anchorItemAt(const QPoint& viewPos, Item::AnchorType& anchorType); + + bool drawingConnection_ = false; + Item* startItem_ = nullptr; + Item::AnchorType startAnchor_; + QGraphicsLineItem* tempLine_ = nullptr; }; #endif // MYGRAPHICSVIEW_H