選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

269 行
9.0 KiB

  1. #include "mygraphicsview.h"
  2. #include <QMimeData>
  3. #include <QCursor>
  4. #include <QGraphicsScene>
  5. #include <QDebug>
  6. #include <QMenu>
  7. #include <QInputDialog>
  8. #include <QMessageBox>
  9. #include <QRegularExpression>
  10. MyGraphicsView::ClipInfo MyGraphicsView::clipboard_ = {};
  11. MyGraphicsView::MyGraphicsView(QWidget *parent)
  12. : QGraphicsView(parent)
  13. {
  14. setAcceptDrops(true);
  15. setRenderHint(QPainter::Antialiasing);
  16. setFocusPolicy(Qt::StrongFocus);
  17. setDragMode(QGraphicsView::RubberBandDrag);
  18. }
  19. void MyGraphicsView::dragEnterEvent(QDragEnterEvent *event)
  20. {
  21. if (event->mimeData()->hasFormat("application/x-component"))
  22. event->acceptProposedAction();
  23. else
  24. event->ignore();
  25. }
  26. void MyGraphicsView::dragMoveEvent(QDragMoveEvent *event)
  27. {
  28. if (event->mimeData()->hasFormat("application/x-component"))
  29. event->acceptProposedAction();
  30. else
  31. event->ignore();
  32. }
  33. void MyGraphicsView::dropEvent(QDropEvent *event)
  34. {
  35. if (!event->mimeData()->hasFormat("application/x-component")) {
  36. event->ignore();
  37. return;
  38. }
  39. QString type = QString::fromUtf8(event->mimeData()->data("application/x-component"));
  40. QPointF scenePos = mapToScene(event->pos());
  41. Item *item = creatItem(type);
  42. item->setPos(scenePos);
  43. connect(item, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy);
  44. connect(item, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete);
  45. connect(item, &Item::requestBindRegister, this, &MyGraphicsView::onItemRequestBindRegister);
  46. scene()->addItem(item);
  47. event->acceptProposedAction();
  48. }
  49. void MyGraphicsView::keyPressEvent(QKeyEvent *event)
  50. {
  51. if (event->key() == Qt::Key_Delete && scene()) {
  52. QList<QGraphicsItem*> selectedItems = scene()->selectedItems();
  53. for (QGraphicsItem* item : selectedItems) {
  54. if (auto conn = dynamic_cast<Connection*>(item)) {
  55. // 先通知两端节点断开
  56. conn->from_->removeConnection(conn);
  57. conn->to_->removeConnection(conn);
  58. scene()->removeItem(conn);
  59. delete conn;
  60. } else if (auto node = dynamic_cast<Item*>(item)) {
  61. // 删除节点的所有连线
  62. QList<Connection*> conns = node->connections();
  63. for (Connection* conn : conns) {
  64. conn->from_->removeConnection(conn);
  65. conn->to_->removeConnection(conn);
  66. scene()->removeItem(conn);
  67. delete conn;
  68. }
  69. scene()->removeItem(node);
  70. delete node;
  71. }
  72. }
  73. }else if (event->matches(QKeySequence::Copy)) {
  74. // Ctrl+C
  75. QList<QGraphicsItem*> selectedItems = scene()->selectedItems();
  76. for (QGraphicsItem* item : selectedItems) {
  77. if (auto node = dynamic_cast<Item*>(item)) {
  78. clipboard_.input = node->itemType();
  79. break;
  80. }
  81. }
  82. } else if (event->matches(QKeySequence::Paste)) {
  83. // Ctrl+V
  84. if (!clipboard_.input.isEmpty()) {
  85. QPointF center = mapToScene(viewport()->rect().center());
  86. Item* newItem = creatItem(clipboard_.input);
  87. newItem->setPos(center);
  88. connect(newItem, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy);
  89. connect(newItem, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete);
  90. connect(newItem, &Item::requestBindRegister, this, &MyGraphicsView::onItemRequestBindRegister);
  91. scene()->addItem(newItem);
  92. }
  93. } else {
  94. QGraphicsView::keyPressEvent(event);
  95. }
  96. }
  97. void MyGraphicsView::contextMenuEvent(QContextMenuEvent *event)
  98. {
  99. QPointF scenePos = mapToScene(event->pos());
  100. QList<QGraphicsItem*> itemsAt = scene()->items(scenePos);
  101. if (itemsAt.isEmpty() && !clipboard_.input.isEmpty()) {
  102. QMenu menu;
  103. QAction* pasteAct = menu.addAction("粘贴");
  104. QAction* sel = menu.exec(event->globalPos());
  105. if (sel == pasteAct) {
  106. Item* newItem = creatItem(clipboard_.input);
  107. newItem->setPos(scenePos);
  108. connect(newItem, &Item::requestCopy, this, &MyGraphicsView::onItemRequestCopy);
  109. connect(newItem, &Item::requestDelete, this, &MyGraphicsView::onItemRequestDelete);
  110. connect(newItem, &Item::requestBindRegister, this, &MyGraphicsView::onItemRequestBindRegister);
  111. scene()->addItem(newItem);
  112. }
  113. } else {
  114. QGraphicsView::contextMenuEvent(event); // 让Item自己弹出菜单
  115. }
  116. }
  117. // ----------- 连线交互 ------------
  118. Item* MyGraphicsView::anchorItemAt(const QPoint& viewPos, Item::AnchorType& anchorType)
  119. {
  120. QPointF scenePos = mapToScene(viewPos);
  121. for (QGraphicsItem* item : scene()->items(scenePos)) {
  122. if (Item* node = dynamic_cast<Item*>(item)) {
  123. QRectF r = node->boundingRect();
  124. QPointF local = node->mapFromScene(scenePos);
  125. if (QLineF(local, QPointF(r.left(), r.center().y())).length() < 10) {
  126. anchorType = Item::Left;
  127. return node;
  128. }
  129. if (QLineF(local, QPointF(r.right(), r.center().y())).length() < 10) {
  130. anchorType = Item::Right;
  131. return node;
  132. }
  133. }
  134. }
  135. return nullptr;
  136. }
  137. void MyGraphicsView::onItemRequestCopy(Item *item)
  138. {
  139. clipboard_.input = item->itemType();
  140. }
  141. void MyGraphicsView::onItemRequestDelete(Item *item)
  142. {
  143. QList<Connection*> conns = item->connections();
  144. for (Connection* conn : conns) {
  145. conn->from_->removeConnection(conn);
  146. conn->to_->removeConnection(conn);
  147. scene()->removeItem(conn);
  148. delete conn;
  149. }
  150. scene()->removeItem(item);
  151. delete item;
  152. }
  153. void MyGraphicsView::onItemRequestBindRegister(Item *item)
  154. {
  155. if (!item) return;
  156. bool ok = false;
  157. QString reg = QInputDialog::getText(
  158. this,
  159. "绑定寄存器",
  160. "格式: <类型><地址>\n"
  161. "类型: D(保持寄存器), M(线圈寄存器)\n"
  162. "示例: D100, M200\n"
  163. "地址范围: 0-4000",
  164. QLineEdit::Normal,
  165. item->registerId(),
  166. &ok
  167. );
  168. if (ok && !reg.isEmpty()) {
  169. // 验证寄存器格式
  170. QRegularExpression regex("^[DMdm]\\d{1,4}$");
  171. QRegularExpressionMatch match = regex.match(reg);
  172. if (!match.hasMatch()) {
  173. QMessageBox::warning(this, "格式错误", "请输入有效的寄存器格式\n示例: D100, M200");
  174. return;
  175. }
  176. int address = reg.mid(1).toInt();
  177. if (address > 4000) {
  178. QMessageBox::warning(this, "范围错误", "寄存器地址必须在0-4000范围内");
  179. return;
  180. }
  181. // 标准化格式 (D100)
  182. QString newReg = QString("%1%2").arg(reg[0].toUpper()).arg(address);
  183. item->setRegisterId(newReg);
  184. item->update();
  185. // 发出绑定信号
  186. emit itemBoundToRegister(item, newReg);
  187. }
  188. }
  189. void MyGraphicsView::mousePressEvent(QMouseEvent *event)
  190. {
  191. if (event->button() == Qt::LeftButton) {
  192. Item::AnchorType anchor;
  193. Item* node = anchorItemAt(event->pos(), anchor);
  194. if (node) {
  195. drawingConnection_ = true;
  196. startItem_ = node;
  197. startAnchor_ = anchor;
  198. tempLine_ = scene()->addLine(QLineF(node->anchorPos(anchor), mapToScene(event->pos())),
  199. QPen(Qt::darkGray, 2, Qt::DashLine));
  200. return;
  201. }
  202. }
  203. QGraphicsView::mousePressEvent(event);
  204. }
  205. void MyGraphicsView::mouseMoveEvent(QMouseEvent *event)
  206. {
  207. if (drawingConnection_ && tempLine_) {
  208. QLineF l = tempLine_->line();
  209. l.setP2(mapToScene(event->pos()));
  210. tempLine_->setLine(l);
  211. return;
  212. }
  213. QGraphicsView::mouseMoveEvent(event);
  214. }
  215. void MyGraphicsView::mouseReleaseEvent(QMouseEvent *event)
  216. {
  217. if (drawingConnection_ && tempLine_) {
  218. // 检查鼠标释放点是否在某个Item boundingRect内
  219. QPointF scenePos = mapToScene(event->pos());
  220. for (QGraphicsItem* item : scene()->items(scenePos)) {
  221. if (Item* node = dynamic_cast<Item*>(item)) {
  222. QRectF r = node->boundingRect();
  223. QPointF local = node->mapFromScene(scenePos);
  224. // 计算落点距离左右端点
  225. double distLeft = QLineF(local, QPointF(r.left(), r.center().y())).length();
  226. double distRight = QLineF(local, QPointF(r.right(), r.center().y())).length();
  227. Item::AnchorType anchor = (distLeft < distRight) ? Item::Left : Item::Right;
  228. // 生成连线
  229. Connection* conn = new Connection(startItem_, startAnchor_, node, anchor);
  230. scene()->addItem(conn);
  231. break;
  232. }
  233. }
  234. // 清除临时线
  235. scene()->removeItem(tempLine_);
  236. delete tempLine_;
  237. tempLine_ = nullptr;
  238. drawingConnection_ = false;
  239. startItem_ = nullptr;
  240. return;
  241. }
  242. QGraphicsView::mouseReleaseEvent(event);
  243. }