#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 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 = 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); } } 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 = 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); } } else { QGraphicsView::contextMenuEvent(event); // 让Item自己弹出菜单 } } // ----------- 连线交互 ------------ 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::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::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); } }