#include "mygraphicsview.h" #include #include #include #include #include #include #include #include MyGraphicsView::ClipInfo MyGraphicsView::clipboard_ = {}; MyGraphicsView::MyGraphicsView(QWidget *parent) : QGraphicsView(parent) { setAcceptDrops(true); setRenderHint(QPainter::Antialiasing); setFocusPolicy(Qt::StrongFocus); setDragMode(QGraphicsView::RubberBandDrag); } void MyGraphicsView::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("application/x-component")) event->acceptProposedAction(); else event->ignore(); } void MyGraphicsView::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasFormat("application/x-component")) event->acceptProposedAction(); else event->ignore(); } void MyGraphicsView::dropEvent(QDropEvent *event) { if (!event->mimeData()->hasFormat("application/x-component")) { event->ignore(); return; } QString type = QString::fromUtf8(event->mimeData()->data("application/x-component")); QPointF scenePos = mapToScene(event->pos()); Item *item = creatItem(type); item->setPos(scenePos); connect(item, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy); connect(item, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete); connect(item, &Item::requestBindRegister, this, &MyGraphicsView::onItemRequestBindRegister); connect(item, &Item::requestCompare, this, &MyGraphicsView::onItemRequestCompare); connect(item, &Item::requestReset, this, &MyGraphicsView::onItemRequestReset); connect(item, &Item::requestSetON,this, &MyGraphicsView::onItemRequestSetON); scene()->addItem(item); event->acceptProposedAction(); } void MyGraphicsView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Delete && scene()) { QList selectedItems = scene()->selectedItems(); for (QGraphicsItem* item : selectedItems) { 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) { conn->from_->removeConnection(conn); conn->to_->removeConnection(conn); scene()->removeItem(conn); delete conn; } scene()->removeItem(node); delete node; } } }else if (event->matches(QKeySequence::Copy)) { // Ctrl+C clipboard_.items.clear(); clipboard_.connections.clear(); QList selectedItems = scene()->selectedItems(); // 过滤出Item类型的元件 QList items; for (QGraphicsItem* gi : selectedItems) { if (auto item = dynamic_cast(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(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)) { // Ctrl+V performPaste(mapToScene(viewport()->rect().center())); } else { QGraphicsView::keyPressEvent(event); } } void MyGraphicsView::contextMenuEvent(QContextMenuEvent *event) { QPointF scenePos = mapToScene(event->pos()); QList itemsAt = scene()->items(scenePos); // 如果有选中的元件,显示元件自己的菜单 if (!itemsAt.isEmpty()) { QGraphicsView::contextMenuEvent(event); return; } // 没有选中元件时,显示粘贴菜单(如果剪贴板有内容) QMenu menu; if (!clipboard_.items.isEmpty()) { QAction* pasteAct = menu.addAction("粘贴"); QAction* sel = menu.exec(event->globalPos()); if (sel == pasteAct) { performPaste(scenePos); // 在鼠标位置粘贴 } } } // ----------- 连线交互 ------------ 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; } QPointF MyGraphicsView::calculateItemsCenter(const QList &items) { QPointF center(0, 0); int count = 0; for (QGraphicsItem* item : items) { if (auto node = dynamic_cast(item)) { center += node->pos(); count++; } } return count > 0 ? center / count : QPointF(0, 0); } void MyGraphicsView::onItemRequestCopy(Item *item) { clipboard_.items.clear(); clipboard_.connections.clear(); CopiedItem copiedItem; copiedItem.type = item->itemType(); clipboard_.items.append(copiedItem); } 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::onItemRequestBindRegister(Item *item) { if (!item) return; bool ok = false; QString reg = QInputDialog::getText( this, "绑定寄存器", "格式: <类型><地址>\n" "类型: D(保持寄存器), M(线圈寄存器), K(常数)\n" "示例: D100, M200, K100\n" "地址范围: 0-4000\n" "常数范围: 0-65535", QLineEdit::Normal, item->registerId(), &ok ); if (ok && !reg.isEmpty()) { // 验证输入格式 QRegularExpression regex("^([DMdm]\\d{1,4}|K\\d{1,5})$"); QRegularExpressionMatch match = regex.match(reg); if (!match.hasMatch()) { QMessageBox::warning(this, "格式错误", "请输入有效的格式\n示例: D100, M200, K100"); return; } // 提取类型和值 QString type = reg.left(1).toUpper(); int value = reg.mid(1).toInt(); // 检查范围 if (type == "K") { if (value > 65535) { QMessageBox::warning(this, "范围错误", "常数必须在0-65535范围内"); return; } } else { if (value > 4000) { QMessageBox::warning(this, "范围错误", "寄存器地址必须在0-4000范围内"); return; } } // 标准化格式 QString newReg = QString("%1%2").arg(type).arg(value); // 对于比较元件,直接设置值 if (item->itemType() == "比较" && type == "K") { // 常数直接设置值 item->setRegisterId(newReg); item->setRegisterValue(newReg, value); item->update(); }else { // 寄存器绑定 if (type == "K") { QMessageBox::warning(this, "提示", "此元件不能绑定常数"); } if (item->setRegisterId(newReg)) { item->update(); emit itemBoundToRegister(item, newReg); } else { QMessageBox::warning(this, "提示", "请先重置再绑定新的寄存器"); } } } } void MyGraphicsView::onItemRequestCompare(Item *item) { if (!item) return; QString compare = QInputDialog::getText( this, "绑定寄存器", "格式:>、<、=、<=、>=", QLineEdit::Normal ); item->setCompare(compare); } void MyGraphicsView::onItemRequestReset(Item *item) { QStringList unboundRegs = item->resetRegister(); for (const QString& reg : unboundRegs) { emit itemResetRegister(item, reg); // 解绑每个寄存器 } } void MyGraphicsView::onItemRequestSetON(Item *item, bool isON) { if (isON) { emit itemSetON(item->registerId(), 1); } else { emit itemSetON(item->registerId(), 0); } } 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); } void MyGraphicsView::wheelEvent(QWheelEvent *event) { // 检查是否按下了 Ctrl 键 if (event->modifiers() & Qt::ControlModifier) { // 计算缩放因子 (每滚动一步缩放 10%) const double zoomFactor = 1.1; double scaleFactor = (event->angleDelta().y() > 0) ? zoomFactor : 1.0 / zoomFactor; // 获取鼠标在场景中的位置 QPointF scenePos = mapToScene(event->position().toPoint()); // 执行缩放 scale(scaleFactor, scaleFactor); // 调整视图中心点,使缩放以鼠标位置为中心 QPointF delta = scenePos - mapToScene(viewport()->rect().center()); centerOn(scenePos - delta * (scaleFactor - 1)); event->accept(); // 表示事件已处理 } else { // 没有按下 Ctrl 键,执行默认的滚动行为 QGraphicsView::wheelEvent(event); } } void MyGraphicsView::performPaste(const QPointF &pasteCenter) { if (clipboard_.items.isEmpty()) return; QList 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); } }