|
- #include "mygraphicsview.h"
- #include <QMimeData>
- #include <QCursor>
- #include <QGraphicsScene>
- #include <QDebug>
- #include <QMenu>
- #include <QInputDialog>
- #include <QMessageBox>
- #include <QRegularExpression>
-
- 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<QGraphicsItem*> selectedItems = scene()->selectedItems();
- for (QGraphicsItem* item : selectedItems) {
- if (auto conn = dynamic_cast<Connection*>(item)) {
- // 先通知两端节点断开
- conn->from_->removeConnection(conn);
- conn->to_->removeConnection(conn);
- scene()->removeItem(conn);
- delete conn;
- } else if (auto node = dynamic_cast<Item*>(item)) {
- // 删除节点的所有连线
- QList<Connection*> 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<QGraphicsItem*> selectedItems = scene()->selectedItems();
-
- // 过滤出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)) {
- // Ctrl+V
- performPaste(mapToScene(viewport()->rect().center()));
- } else {
- QGraphicsView::keyPressEvent(event);
- }
- }
-
- void MyGraphicsView::contextMenuEvent(QContextMenuEvent *event)
- {
- QPointF scenePos = mapToScene(event->pos());
- QList<QGraphicsItem*> 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*>(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<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)
- {
- clipboard_.items.clear();
- clipboard_.connections.clear();
-
- CopiedItem copiedItem;
- copiedItem.type = item->itemType();
- clipboard_.items.append(copiedItem);
- }
-
- void MyGraphicsView::onItemRequestDelete(Item *item)
- {
- QList<Connection*> 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*>(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<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);
- }
- }
|