diff --git a/connection.cpp b/connection.cpp index d0dbc69..305c18c 100644 --- a/connection.cpp +++ b/connection.cpp @@ -9,6 +9,7 @@ Connection::Connection(Item* from, Item::AnchorType fromType, { setZValue(-1); // 在图元之下 setPen(QPen(Qt::black, 2)); + setFlags(QGraphicsItem::ItemIsSelectable); from_->addConnection(this); to_->addConnection(this); updatePosition(); diff --git a/editor.pro.user b/editor.pro.user index 4eb1275..0834988 100644 --- a/editor.pro.user +++ b/editor.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/item.cpp b/item.cpp index f14282a..01ca08d 100644 --- a/item.cpp +++ b/item.cpp @@ -1,16 +1,13 @@ #include "item.h" #include "connection.h" +#include #include #include +#include Item::Item(const QString &type, QGraphicsItem *parent) : QGraphicsObject(parent), type_(type) { - if (type == "常开") color_ = Qt::blue; - else if (type == "常闭") color_ = Qt::red; - else if (type == "比较指令") color_ = Qt::green; - else color_ = Qt::darkYellow; - setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges); @@ -18,36 +15,76 @@ Item::Item(const QString &type, QGraphicsItem *parent) QRectF Item::boundingRect() const { - return QRectF(-5, -5, 90, 50); + return QRectF(-22, -15, 44, 30); } void Item::paint(QPainter *painter, - const QStyleOptionGraphicsItem *, + const QStyleOptionGraphicsItem * option, QWidget *) { - QRectF r = boundingRect().adjusted(5, 5, -5, -5); // 图元主体 - painter->setBrush(color_.lighter(150)); - painter->setPen(QPen(color_, 2)); - painter->drawRoundedRect(r, 5, 5); + 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(r.left(), r.center().y()), 5, 5); // 左锚点 - painter->drawEllipse(QPointF(r.right(), r.center().y()), 5, 5); // 右锚点 + // 画锚点 + 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->setPen(Qt::black); - painter->drawText(r, Qt::AlignCenter, type_); + // 锚点 + 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 anc) const +QPointF Item::anchorPos(AnchorType type) 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(); + return mapToScene(type == Left ? QPointF(-18, 0) : QPointF(18, 0)); } void Item::addConnection(Connection* conn) @@ -66,6 +103,11 @@ QList Item::connections() return connections_; } +QString Item::itemType() +{ + return type_; +} + QVariant Item::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == QGraphicsItem::ItemPositionChange) { @@ -74,3 +116,17 @@ QVariant Item::itemChange(GraphicsItemChange change, const QVariant &value) } return QGraphicsObject::itemChange(change, 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); + } +} diff --git a/item.h b/item.h index 4447ea9..fa9b2ca 100644 --- a/item.h +++ b/item.h @@ -23,15 +23,20 @@ public: void addConnection(Connection* conn); void removeConnection(Connection* conn); QList connections(); + QString itemType(); + +signals: + void requestCopy(Item*); + void requestDelete(Item*); protected: QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; private: QString type_; QColor color_; QList connections_; - }; #endif // ITEM_H diff --git a/mainwindow.cpp b/mainwindow.cpp index d30cc38..83e85f7 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -20,6 +20,7 @@ MainWindow::MainWindow(QWidget *parent) 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); @@ -81,7 +82,7 @@ void MainWindow::createComponents() const QVector comps = { {"常开", Qt::blue}, {"常闭", Qt::red}, - {"比较指令", Qt::green}, + {"比较", Qt::green}, {"线圈", Qt::darkYellow} }; diff --git a/mainwindow.ui b/mainwindow.ui index 5aa6002..971f2e0 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -6,7 +6,7 @@ 0 0 - 1101 + 1012 656 @@ -29,7 +29,7 @@ 210 10 - 871 + 761 491 @@ -43,7 +43,7 @@ 0 0 - 1101 + 1012 26 diff --git a/mygraphicsview.cpp b/mygraphicsview.cpp index 9a50c6a..86f6499 100644 --- a/mygraphicsview.cpp +++ b/mygraphicsview.cpp @@ -3,14 +3,17 @@ #include #include #include +#include + +MyGraphicsView::ClipInfo MyGraphicsView::clipboard_ = {}; MyGraphicsView::MyGraphicsView(QWidget *parent) : QGraphicsView(parent) { setAcceptDrops(true); - viewport()->setAcceptDrops(true); setRenderHint(QPainter::Antialiasing); setFocusPolicy(Qt::StrongFocus); + setDragMode(QGraphicsView::RubberBandDrag); } void MyGraphicsView::dragEnterEvent(QDragEnterEvent *event) @@ -41,9 +44,9 @@ void MyGraphicsView::dropEvent(QDropEvent *event) QPointF scenePos = mapToScene(event->pos()); Item *item = new Item(type); item->setPos(scenePos); - - if (scene()) - scene()->addItem(item); + connect(item, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy); + connect(item, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete); + scene()->addItem(item); event->acceptProposedAction(); } @@ -53,24 +56,69 @@ 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)) { + if (auto conn = dynamic_cast(item)) { + // 先通知两端节点断开 + conn->from_->removeConnection(conn); + conn->to_->removeConnection(conn); + scene()->removeItem(conn); + delete conn; + } else 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); + scene()->removeItem(conn); delete conn; } + scene()->removeItem(node); + delete node; } - scene()->removeItem(item); - delete item; + } + }else if (event->matches(QKeySequence::Copy)) { + // Ctrl+C + QList selectedItems = scene()->selectedItems(); + for (QGraphicsItem* item : selectedItems) { + if (auto node = dynamic_cast(item)) { + clipboard_.input = node->itemType(); + break; + } + } + } else if (event->matches(QKeySequence::Paste)) { + // Ctrl+V + if (!clipboard_.input.isEmpty()) { + QPointF center = mapToScene(viewport()->rect().center()); + Item* newItem = new Item(clipboard_.input); + newItem->setPos(center); + connect(newItem, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy); + connect(newItem, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete); + scene()->addItem(newItem); } } else { QGraphicsView::keyPressEvent(event); } } +void MyGraphicsView::contextMenuEvent(QContextMenuEvent *event) +{ + QPointF scenePos = mapToScene(event->pos()); + QList itemsAt = scene()->items(scenePos); + if (itemsAt.isEmpty() && !clipboard_.input.isEmpty()) { + QMenu menu; + QAction* pasteAct = menu.addAction("粘贴"); + QAction* sel = menu.exec(event->globalPos()); + if (sel == pasteAct) { + Item* newItem = new Item(clipboard_.input); + newItem->setPos(scenePos); + connect(newItem, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy); + connect(newItem, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete); + scene()->addItem(newItem); + } + } else { + QGraphicsView::contextMenuEvent(event); // 让Item自己弹出菜单 + } +} + // ----------- 连线交互 ------------ Item* MyGraphicsView::anchorItemAt(const QPoint& viewPos, Item::AnchorType& anchorType) @@ -93,6 +141,24 @@ Item* MyGraphicsView::anchorItemAt(const QPoint& viewPos, Item::AnchorType& anch return nullptr; } +void MyGraphicsView::onItemRequestCopy(Item *item) +{ + clipboard_.input = item->itemType(); +} + +void MyGraphicsView::onItemRequestDelete(Item *item) +{ + QList conns = item->connections(); + for (Connection* conn : conns) { + conn->from_->removeConnection(conn); + conn->to_->removeConnection(conn); + scene()->removeItem(conn); + delete conn; + } + scene()->removeItem(item); + delete item; +} + void MyGraphicsView::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { diff --git a/mygraphicsview.h b/mygraphicsview.h index a0082b1..81817a6 100644 --- a/mygraphicsview.h +++ b/mygraphicsview.h @@ -21,6 +21,7 @@ protected: void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; void keyPressEvent(QKeyEvent *event) override; + void contextMenuEvent(QContextMenuEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; @@ -33,6 +34,15 @@ private: Item* startItem_ = nullptr; Item::AnchorType startAnchor_; QGraphicsLineItem* tempLine_ = nullptr; + + struct ClipInfo { + QString input; + }; + static ClipInfo clipboard_; + +private slots: + void onItemRequestCopy(Item*); + void onItemRequestDelete(Item*); }; #endif // MYGRAPHICSVIEW_H diff --git a/mylistwidget.cpp b/mylistwidget.cpp deleted file mode 100644 index 31a4439..0000000 --- a/mylistwidget.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "mylistwidget.h" - -MyListWidget::MyListWidget() -{ - -} diff --git a/mylistwidget.h b/mylistwidget.h deleted file mode 100644 index 5e5f1ea..0000000 --- a/mylistwidget.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef MYLISTWIDGET_H -#define MYLISTWIDGET_H -#include -#include -#include - -class MyListWidget : public QListWidget { -public: - using QListWidget::QListWidget; - -protected: - void startDrag(Qt::DropActions supportedActions) override - { - QListWidgetItem *item = currentItem(); - if (!item) return; - - QDrag *drag = new QDrag(this); - QMimeData *mimeData = new QMimeData; - - QString type = item->data(Qt::UserRole).toString(); - mimeData->setData("application/x-component", type.toUtf8()); - drag->setMimeData(mimeData); - - drag->setPixmap(item->icon().pixmap(iconSize())); - drag->setHotSpot(drag->pixmap().rect().center()); - - drag->exec(Qt::CopyAction); - } -}; - -#endif // MYLISTWIDGET_H