You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

774 lines
25 KiB

  1. #include "hmidocument.h"
  2. #include "graphicsitems.h"
  3. #include <QGraphicsItem>
  4. #include <QGraphicsSceneHoverEvent>
  5. #include <QMouseEvent>
  6. #include <QMenu>
  7. #include <QAction>
  8. #include <QDataStream>
  9. #include <QColorDialog>
  10. #include <QDialog>
  11. #include <QFormLayout>
  12. #include <QLineEdit>
  13. #include <QPushButton>
  14. #include <QDialogButtonBox>
  15. #include <QMimeData>
  16. #include <QDragEnterEvent>
  17. #include <QDropEvent>
  18. #include <QDebug>
  19. #include <QFileInfo>
  20. #include <QVBoxLayout>
  21. #include <QJsonArray>
  22. #include <QJsonDocument>
  23. #include <QJsonObject>
  24. #include<QSpinBox>
  25. #include<QMessageBox>
  26. HMIDocument::HMIDocument(QWidget *parent)
  27. : BaseDocument(HMI, parent),
  28. m_title("未命名HMI"),
  29. m_canDrawEllipse(false),
  30. m_canDrawRectangle(false),
  31. m_lastPastePos(0, 0)
  32. {
  33. // 创建绘图场景 - 固定大小
  34. m_scene = new QGraphicsScene(this);
  35. m_scene->setSceneRect(0, 0, 1000, 600); // 固定画布尺寸
  36. m_scene->setBackgroundBrush(QBrush(Qt::lightGray));
  37. // 创建视图 - 关键修改:使用滚动区域包装
  38. m_scrollArea = new QScrollArea(this);
  39. m_scrollArea->setWidgetResizable(false); // 禁止内容自动调整大小
  40. //m_scrollArea->setFrameShape(QFrame::NoFrame); // 无边框
  41. // 创建真正的视图部件
  42. m_view = new QGraphicsView(m_scene, this);
  43. m_view->setRenderHint(QPainter::Antialiasing);
  44. m_view->setDragMode(QGraphicsView::RubberBandDrag);
  45. m_view->setAcceptDrops(true);
  46. m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 隐藏自身滚动条
  47. m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  48. // 关键:视图固定大小与场景匹配
  49. m_view->setMinimumSize(1000, 600);
  50. m_view->setMaximumSize(1000, 600);
  51. m_view->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
  52. m_view->viewport()->installEventFilter(this);
  53. // 添加视图到滚动区域
  54. m_scrollArea->setWidget(m_view);
  55. m_scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  56. // 设置主布局
  57. auto layout = new QVBoxLayout(this);
  58. layout->setContentsMargins(0, 0, 0, 0);
  59. layout->addWidget(m_scrollArea);
  60. setLayout(layout);
  61. }
  62. HMIDocument::~HMIDocument()
  63. {
  64. stopSimulation();
  65. }
  66. void HMIDocument::startSimulation()
  67. {
  68. qDebug() <<"123";
  69. if (m_modbusSimulator && m_modbusSimulator->isRunning()) return;
  70. // 收集所有线圈地址
  71. QSet<int> addresses;
  72. foreach (QGraphicsItem *item, m_scene->items()) {
  73. if (auto namedItem = dynamic_cast<NamedItem*>(item)) {
  74. bool ok;
  75. int address = namedItem->name().toInt(&ok);
  76. if (ok && address >= 0 && address <= 4000) { // 限制有效范围
  77. // 只收集指示灯(ResizableEllipse)的地址
  78. if (auto ellipseItem = dynamic_cast<ResizableEllipse*>(item)) {
  79. addresses.insert(address);
  80. // 遍历代码结束后
  81. qDebug() << "收集到的指示灯地址:" << addresses; // 检查是否正确包含地址(如123)
  82. } else {
  83. // 对按钮(ResizableRectangle)进行处理,但不收集地址
  84. qDebug() << "按钮不收集地址: " << item->type();
  85. }
  86. } else {
  87. // 记录无效地址,方便调试
  88. qDebug() << "无效的线圈地址:" << namedItem->name()
  89. << "(图形项类型:" << item->type() << ")";
  90. }
  91. }
  92. }
  93. if (addresses.isEmpty()) {
  94. QMessageBox::warning(nullptr, "仿真启动", "没有找到有效的线圈地址绑定");
  95. return;
  96. }
  97. if (!m_modbusSimulator) {
  98. m_modbusSimulator = new ModbusSimulator(this);
  99. connect(m_modbusSimulator, &ModbusSimulator::coilStatusRead,
  100. this, &HMIDocument::onCoilStatusRead);
  101. connect(m_modbusSimulator, &ModbusSimulator::errorOccurred,
  102. this, &HMIDocument::onSimulationError);
  103. }
  104. // 遍历代码结束后
  105. qDebug() << "zhxing";
  106. m_modbusSimulator->setCoilAddresses(addresses);
  107. m_modbusSimulator->startSimulation();
  108. }
  109. void HMIDocument::stopSimulation()
  110. {
  111. if (m_modbusSimulator) {
  112. m_modbusSimulator->stopSimulation();
  113. }
  114. }
  115. bool HMIDocument::isSimulationRunning() const
  116. {
  117. return m_modbusSimulator && m_modbusSimulator->isRunning();
  118. }
  119. void HMIDocument::onCoilStatusRead(int address, bool state)
  120. {
  121. foreach (QGraphicsItem *item, m_scene->items()) {
  122. if (auto namedItem = dynamic_cast<NamedItem*>(item)) {
  123. bool ok;
  124. int itemAddress = namedItem->name().toInt(&ok);
  125. if (ok && itemAddress == address) {
  126. if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) {
  127. ellipse->setBrush(state ? ellipse->onColor() : ellipse->offColor());
  128. ellipse->update();
  129. }
  130. }
  131. }
  132. }
  133. }
  134. void HMIDocument::onSimulationError(const QString &message)
  135. {
  136. QMessageBox::critical(nullptr, "仿真错误", message);
  137. }
  138. void HMIDocument::writeCoil(int address, bool state)
  139. {
  140. if (m_modbusSimulator) {
  141. m_modbusSimulator->writeCoil(address, state);
  142. }
  143. }
  144. // 事件过滤器(处理绘图、拖拽等事件)
  145. bool HMIDocument::eventFilter(QObject *obj, QEvent *event)
  146. {
  147. if (obj != m_view->viewport())
  148. {
  149. return QWidget::eventFilter(obj, event);
  150. }
  151. // 鼠标按下事件(绘制图形)
  152. if (event->type() == QEvent::MouseButtonPress)
  153. {
  154. QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
  155. setModified(true);
  156. if (mouseEvent->button() == Qt::RightButton)
  157. {
  158. showContextMenu(mouseEvent->globalPos());// 右键菜单
  159. return true;
  160. }
  161. if (mouseEvent->button() == Qt::LeftButton)
  162. {
  163. QPointF scenePos = m_view->mapToScene(mouseEvent->pos());
  164. QGraphicsItem *item = m_scene->itemAt(scenePos, m_view->transform());
  165. // 如果点击空白区域且有绘制标志,创建图形
  166. if (!item) {
  167. if (m_canDrawEllipse)
  168. {
  169. createShape("ellipse", scenePos);
  170. resetDrawFlags();
  171. }
  172. else if (m_canDrawRectangle)
  173. {
  174. createShape("rectangle", scenePos);
  175. resetDrawFlags();
  176. }
  177. }
  178. }
  179. }
  180. // 拖拽事件(从工具栏拖拽绘制)
  181. if (event->type() == QEvent::DragEnter)
  182. {
  183. QDragEnterEvent *dragEvent = static_cast<QDragEnterEvent*>(event);
  184. dragEvent->acceptProposedAction();
  185. return true;
  186. }
  187. if (event->type() == QEvent::DragMove)
  188. {
  189. static_cast<QDragMoveEvent*>(event)->acceptProposedAction();
  190. return true;
  191. }
  192. if (event->type() == QEvent::Drop)
  193. {
  194. QDropEvent *dropEvent = static_cast<QDropEvent*>(event);
  195. const QMimeData *mimeData = dropEvent->mimeData();
  196. QPointF scenePos = m_view->mapToScene(dropEvent->pos());
  197. // 使用QMimeData
  198. if (mimeData && mimeData->hasText())
  199. {
  200. createShape(mimeData->text(), scenePos);
  201. resetDrawFlags();//重置防止拖拽后再次点击生成
  202. }
  203. dropEvent->acceptProposedAction();
  204. return true;
  205. }
  206. return QWidget::eventFilter(obj, event);
  207. }
  208. void HMIDocument::createShape(const QString& type, const QPointF &pos)
  209. {
  210. m_scene->clearSelection();
  211. if (type == "指示灯" || type == "ellipse")
  212. {
  213. ResizableEllipse *ellipse = new ResizableEllipse(pos.x(), pos.y(), 50, 50);
  214. ellipse->setBrush(QBrush(Qt::red));
  215. ellipse->setPen(QPen(Qt::black, 1));
  216. m_scene->addItem(ellipse);
  217. ellipse->setSelected(true);
  218. }
  219. else if (type == "按钮" || type == "rectangle")
  220. {
  221. ResizableRectangle *rect = new ResizableRectangle(pos.x(), pos.y(), 100, 50);
  222. rect->setBrush(QBrush(Qt::yellow));
  223. rect->setPen(QPen(Qt::black, 1));
  224. m_scene->addItem(rect);
  225. rect->setSelected(true);
  226. }
  227. setModified(true);
  228. }
  229. // 重置绘制标志
  230. void HMIDocument::resetDrawFlags()
  231. {
  232. m_canDrawEllipse = false;
  233. m_canDrawRectangle = false;
  234. }
  235. // 右键菜单
  236. void HMIDocument::showContextMenu(QPoint globalPos)
  237. {
  238. QPoint viewportPos = m_view->mapFromGlobal(globalPos);
  239. QPointF scenePos = m_view->mapToScene(viewportPos);
  240. QGraphicsItem *clickedItem = m_scene->itemAt(scenePos, m_view->transform());
  241. QMenu menu(this);
  242. if (!clickedItem)
  243. {
  244. // 空白区域:仅显示粘贴
  245. QAction *pasteAction = menu.addAction("粘贴");
  246. pasteAction->setEnabled(!m_copiedItemsData.isEmpty());
  247. connect(pasteAction, &QAction::triggered, this, &HMIDocument::pasteItems);
  248. }
  249. else
  250. {
  251. // 选中图形:显示基础菜单
  252. QAction *propAction = menu.addAction("属性");
  253. QAction *copyAction = menu.addAction("复制");
  254. QAction *pasteAction = menu.addAction("粘贴");
  255. QAction *deleteAction = menu.addAction("删除");
  256. pasteAction->setEnabled(!m_copiedItemsData.isEmpty());
  257. ResizableRectangle *rectItem = dynamic_cast<ResizableRectangle*>(clickedItem);
  258. connect(propAction, &QAction::triggered, this, &HMIDocument::showItemProperties);
  259. connect(copyAction, &QAction::triggered, this, &HMIDocument::copySelectedItems);
  260. connect(pasteAction, &QAction::triggered, this, &HMIDocument::pasteItems);
  261. connect(deleteAction, &QAction::triggered, this, &HMIDocument::deleteSelectedItems);
  262. // 仅对按钮(矩形)显示ON/OFF选项,指示灯(椭圆)不显示
  263. if (dynamic_cast<ResizableRectangle*>(clickedItem)) {
  264. QAction *onAction = menu.addAction("置为ON");
  265. QAction *offAction = menu.addAction("置为OFF");
  266. connect(onAction, &QAction::triggered, [=]() {
  267. rectItem->setBrush(rectItem->pressedColor()); // 使用 rectItem
  268. // 获取线圈地址并写入
  269. bool ok;
  270. int address = rectItem->name().toInt(&ok); // 使用 rectItem
  271. if (ok && address > 0) {
  272. writeCoil(address, true); // 写入ON状态
  273. }
  274. });
  275. connect(offAction, &QAction::triggered, [=]() {
  276. rectItem->setBrush(rectItem->releasedColor()); // 使用 rectItem
  277. // 获取线圈地址并写入
  278. bool ok;
  279. int address = rectItem->name().toInt(&ok); // 使用 rectItem
  280. if (ok && address > 0) {
  281. writeCoil(address, false); // 写入OFF状态
  282. }
  283. });
  284. }
  285. }
  286. menu.exec(globalPos + QPoint(10, 10));
  287. }
  288. // 复制选中项
  289. void HMIDocument::copySelectedItems()
  290. {
  291. m_copiedItemsData.clear();
  292. QList<QGraphicsItem*> selectedItems = m_scene->selectedItems();
  293. if (selectedItems.isEmpty()) return;
  294. foreach (QGraphicsItem *item, selectedItems)
  295. {
  296. m_copiedItemsData.append(serializeItem(item));
  297. }
  298. m_lastPastePos = selectedItems.first()->pos();
  299. setModified(true);
  300. }
  301. // 粘贴项
  302. void HMIDocument::pasteItems()
  303. {
  304. if (m_copiedItemsData.isEmpty()) return;
  305. QPointF offset(20, 20);
  306. m_lastPastePos += offset;
  307. m_scene->clearSelection();
  308. foreach (const QByteArray &itemData, m_copiedItemsData)
  309. {
  310. QGraphicsItem *newItem = deserializeItem(itemData);
  311. if (newItem)
  312. {
  313. m_scene->addItem(newItem);
  314. newItem->setPos(m_lastPastePos);
  315. newItem->setSelected(true);
  316. }
  317. }
  318. setModified(true);
  319. }
  320. // 删除选中项
  321. void HMIDocument::deleteSelectedItems()
  322. {
  323. QList<QGraphicsItem*> selectedItems = m_scene->selectedItems();
  324. foreach (QGraphicsItem *item, selectedItems) {
  325. m_scene->removeItem(item);
  326. delete item;
  327. }
  328. setModified(true);
  329. }
  330. // 显示属性对话框
  331. // 在HMIDocument::showItemProperties()方法中替换相关代码
  332. void HMIDocument::showItemProperties()
  333. {
  334. QList<QGraphicsItem*> selectedItems = m_scene->selectedItems();
  335. if (selectedItems.isEmpty()) return;
  336. QGraphicsItem *item = selectedItems.first();
  337. // 将QGraphicsItem转换为NamedItem
  338. NamedItem *namedItem = dynamic_cast<NamedItem*>(item);
  339. if (!namedItem) return;
  340. QDialog dialog(this);
  341. dialog.setWindowTitle("属性设置");
  342. QFormLayout *form = new QFormLayout(&dialog);
  343. // 线圈地址输入 - 使用SpinBox替代LineEdit
  344. QSpinBox *coilAddrSpin = new QSpinBox();
  345. coilAddrSpin->setRange(0, 4000); // 设置PLC线圈地址常见范围
  346. // 尝试从名称解析现有地址(如果之前用数字作为名称)
  347. bool isNumber;
  348. int addr = namedItem->name().toInt(&isNumber);
  349. if (isNumber) {
  350. coilAddrSpin->setValue(addr);
  351. }
  352. // 颜色选择(保持不变)
  353. QColor tempColor1, tempColor2;
  354. QPushButton *colorBtn1 = new QPushButton;
  355. QPushButton *colorBtn2 = new QPushButton;
  356. if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) {
  357. tempColor1 = ellipse->onColor();
  358. tempColor2 = ellipse->offColor();
  359. form->addRow("ON颜色:", colorBtn1);
  360. form->addRow("OFF颜色:", colorBtn2);
  361. } else if (auto rect = dynamic_cast<ResizableRectangle*>(item)) {
  362. tempColor1 = rect->pressedColor();
  363. tempColor2 = rect->releasedColor();
  364. form->addRow("按下颜色:", colorBtn1);
  365. form->addRow("释放颜色:", colorBtn2);
  366. }
  367. // 初始化颜色按钮(保持不变)
  368. colorBtn1->setStyleSheet("background-color:" + tempColor1.name());
  369. colorBtn2->setStyleSheet("background-color:" + tempColor2.name());
  370. // 颜色选择对话框(保持不变)
  371. connect(colorBtn1, &QPushButton::clicked, [&]() {
  372. QColor c = QColorDialog::getColor(tempColor1, &dialog);
  373. if (c.isValid()) {
  374. tempColor1 = c;
  375. colorBtn1->setStyleSheet("background-color:" + c.name());
  376. }
  377. });
  378. connect(colorBtn2, &QPushButton::clicked, [&]() {
  379. QColor c = QColorDialog::getColor(tempColor2, &dialog);
  380. if (c.isValid()) {
  381. tempColor2 = c;
  382. colorBtn2->setStyleSheet("background-color:" + c.name());
  383. }
  384. });
  385. // 布局组装(将名称输入替换为线圈地址)
  386. form->addRow("线圈地址:", coilAddrSpin); // 这里是关键修改
  387. QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
  388. form->addRow(btnBox);
  389. connect(btnBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
  390. connect(btnBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
  391. // 应用属性(使用SpinBox的值作为地址)
  392. if (dialog.exec() == QDialog::Accepted) {
  393. setModified(true);
  394. // 将SpinBox的值转换为字符串保存到名称属性
  395. namedItem->setName(QString::number(coilAddrSpin->value()));
  396. if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) {
  397. ellipse->setOnColor(tempColor1);
  398. ellipse->setOffColor(tempColor2);
  399. } else if (auto rect = dynamic_cast<ResizableRectangle*>(item)) {
  400. rect->setPressedColor(tempColor1);
  401. rect->setReleasedColor(tempColor2);
  402. }
  403. item->update();
  404. }
  405. }
  406. // 序列化图形项(用于复制粘贴)
  407. QByteArray HMIDocument::serializeItem(QGraphicsItem *item)
  408. {
  409. QByteArray data;
  410. QDataStream stream(&data, QIODevice::WriteOnly);
  411. // 基础信息
  412. stream << item->type();
  413. stream << item->pos();
  414. stream << item->flags();
  415. stream << item->transform();
  416. stream << item->isVisible();
  417. // 形状信息
  418. if (auto shape = dynamic_cast<QAbstractGraphicsShapeItem*>(item))
  419. {
  420. stream << shape->pen();
  421. stream << shape->brush();
  422. stream << shape->boundingRect();
  423. }
  424. // 具体图形信息
  425. if (auto rect = dynamic_cast<ResizableRectangle*>(item))
  426. {
  427. stream << rect->rect();
  428. stream << rect->pressedColor();
  429. stream << rect->releasedColor();
  430. stream << rect->name();
  431. } else if (auto ellipse = dynamic_cast<ResizableEllipse*>(item))
  432. {
  433. stream << ellipse->rect();
  434. stream << ellipse->onColor();
  435. stream << ellipse->offColor();
  436. stream << ellipse->name();
  437. }
  438. return data;
  439. }
  440. // 反序列化图形项(用于粘贴)
  441. QGraphicsItem *HMIDocument::deserializeItem(const QByteArray &data)
  442. {
  443. QDataStream stream(data);
  444. int type;
  445. QPointF pos;
  446. QGraphicsItem::GraphicsItemFlags flags;
  447. QTransform transform;
  448. bool visible;
  449. stream >> type >> pos >> flags >> transform >> visible;
  450. QGraphicsItem *item = nullptr;
  451. if (type == QGraphicsRectItem::Type) {
  452. QPen pen;
  453. QBrush brush;
  454. QRectF boundRect;
  455. QRectF rect;
  456. QColor pressedColor, releasedColor;
  457. QString name;
  458. stream >> pen >> brush >> boundRect >> rect >> pressedColor >> releasedColor >> name;
  459. ResizableRectangle *rectItem = new ResizableRectangle(0, 0, rect.width(), rect.height());
  460. rectItem->setPen(pen);
  461. rectItem->setBrush(brush);
  462. rectItem->setPressedColor(pressedColor);
  463. rectItem->setReleasedColor(releasedColor);
  464. rectItem->setName(name);
  465. item = rectItem;
  466. } else if (type == QGraphicsEllipseItem::Type) {
  467. QPen pen;
  468. QBrush brush;
  469. QRectF boundRect;
  470. QRectF rect;
  471. QColor onColor, offColor;
  472. QString name;
  473. stream >> pen >> brush >> boundRect >> rect >> onColor >> offColor >> name;
  474. ResizableEllipse *ellipseItem = new ResizableEllipse(0, 0, rect.width(), rect.height());
  475. ellipseItem->setPen(pen);
  476. ellipseItem->setBrush(brush);
  477. ellipseItem->setOnColor(onColor);
  478. ellipseItem->setOffColor(offColor);
  479. ellipseItem->setName(name);
  480. item = ellipseItem;
  481. }
  482. if (item) {
  483. item->setFlags(flags);
  484. item->setTransform(transform);
  485. item->setVisible(visible);
  486. }
  487. return item;
  488. }
  489. void HMIDocument::startDrawingEllipse()
  490. {
  491. m_canDrawEllipse = true;
  492. m_canDrawRectangle = false;
  493. }
  494. void HMIDocument::startDrawingRectangle()
  495. {
  496. m_canDrawEllipse = false;
  497. m_canDrawRectangle = true;
  498. }
  499. bool HMIDocument::saveToFile(const QString &filePath)
  500. {
  501. QFile file(filePath);
  502. if (!file.open(QIODevice::WriteOnly)) {
  503. qWarning() << "无法打开文件进行写入:" << filePath;
  504. return false;
  505. }
  506. // 创建JSON文档结构
  507. QJsonObject docObject;
  508. docObject["title"] = m_title;
  509. docObject["sceneRect"] = QJsonArray({
  510. m_scene->sceneRect().x(),
  511. m_scene->sceneRect().y(),
  512. m_scene->sceneRect().width(),
  513. m_scene->sceneRect().height()
  514. });
  515. docObject["backgroundColor"] = m_scene->backgroundBrush().color().name();
  516. // 序列化所有图形项
  517. QJsonArray itemsArray;
  518. foreach (QGraphicsItem *item, m_scene->items()) {
  519. QJsonObject itemJson = itemToJson(item);
  520. if (!itemJson.isEmpty()) {
  521. itemsArray.append(itemJson);
  522. }
  523. }
  524. docObject["items"] = itemsArray;
  525. // 写入文件
  526. QJsonDocument doc(docObject);
  527. file.write(doc.toJson());
  528. file.close();
  529. // 更新文档状态
  530. setFilePath(filePath);
  531. setModified(false);
  532. QFileInfo fileInfo(filePath);
  533. setTitle(fileInfo.baseName());
  534. return true;
  535. }
  536. // 从文件加载文档
  537. bool HMIDocument::loadFromFile(const QString &filePath)
  538. {
  539. QFile file(filePath);
  540. if (!file.open(QIODevice::ReadOnly)) {
  541. qWarning() << "无法打开文件进行读取:" << filePath;
  542. return false;
  543. }
  544. // 读取JSON文档
  545. QByteArray data = file.readAll();
  546. QJsonDocument doc = QJsonDocument::fromJson(data);
  547. if (doc.isNull()) {
  548. qWarning() << "无效的JSON文档:" << filePath;
  549. return false;
  550. }
  551. // 解析文档
  552. QJsonObject docObject = doc.object();
  553. m_title = docObject["title"].toString();
  554. // 设置场景属性
  555. QJsonArray sceneRectArray = docObject["sceneRect"].toArray();
  556. if (sceneRectArray.size() == 4) {
  557. QRectF sceneRect(
  558. sceneRectArray[0].toDouble(),
  559. sceneRectArray[1].toDouble(),
  560. sceneRectArray[2].toDouble(),
  561. sceneRectArray[3].toDouble()
  562. );
  563. m_scene->setSceneRect(sceneRect);
  564. }
  565. if (docObject.contains("backgroundColor")) {
  566. QColor bgColor(docObject["backgroundColor"].toString());
  567. m_scene->setBackgroundBrush(QBrush(bgColor));
  568. }
  569. // 清除现有内容
  570. m_scene->clear();
  571. // 加载所有图形项
  572. QJsonArray itemsArray = docObject["items"].toArray();
  573. foreach (QJsonValue itemValue, itemsArray) {
  574. QJsonObject itemJson = itemValue.toObject();
  575. QGraphicsItem *item = jsonToItem(itemJson);
  576. if (item) {
  577. m_scene->addItem(item);
  578. }
  579. }
  580. // 更新文档状态
  581. setFilePath(filePath);
  582. setModified(false);
  583. return true;
  584. }
  585. // 将图形项转换为JSON对象
  586. QJsonObject HMIDocument::itemToJson(QGraphicsItem *item)
  587. {
  588. QJsonObject json;
  589. // 基础属性
  590. json["type"] = item->type();
  591. json["x"] = item->x();
  592. json["y"] = item->y();
  593. json["zValue"] = item->zValue();
  594. json["visible"] = item->isVisible();
  595. // 具体图形项属性
  596. if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) {
  597. json["itemType"] = "ellipse";
  598. json["rect"] = QJsonArray({
  599. ellipse->rect().x(),
  600. ellipse->rect().y(),
  601. ellipse->rect().width(),
  602. ellipse->rect().height()
  603. });
  604. json["onColor"] = ellipse->onColor().name();
  605. json["offColor"] = ellipse->offColor().name();
  606. json["currentColor"] = ellipse->brush().color().name();
  607. json["penColor"] = ellipse->pen().color().name();
  608. json["penWidth"] = ellipse->pen().width();
  609. json["name"] = ellipse->name();
  610. }
  611. else if (auto rect = dynamic_cast<ResizableRectangle*>(item)) {
  612. json["itemType"] = "rectangle";
  613. json["rect"] = QJsonArray({
  614. rect->rect().x(),
  615. rect->rect().y(),
  616. rect->rect().width(),
  617. rect->rect().height()
  618. });
  619. json["pressedColor"] = rect->pressedColor().name();
  620. json["releasedColor"] = rect->releasedColor().name();
  621. json["currentColor"] = rect->brush().color().name();
  622. json["penColor"] = rect->pen().color().name();
  623. json["penWidth"] = rect->pen().width();
  624. json["name"] = rect->name();
  625. }
  626. return json;
  627. }
  628. // 从JSON对象创建图形项
  629. QGraphicsItem *HMIDocument::jsonToItem(const QJsonObject &json)
  630. {
  631. QString itemType = json["itemType"].toString();
  632. QGraphicsItem *item = nullptr;
  633. if (itemType == "ellipse") {
  634. QJsonArray rectArray = json["rect"].toArray();
  635. if (rectArray.size() != 4) return nullptr;
  636. ResizableEllipse *ellipse = new ResizableEllipse(
  637. rectArray[0].toDouble(),
  638. rectArray[1].toDouble(),
  639. rectArray[2].toDouble(),
  640. rectArray[3].toDouble()
  641. );
  642. ellipse->setOnColor(QColor(json["onColor"].toString()));
  643. ellipse->setOffColor(QColor(json["offColor"].toString()));
  644. ellipse->setBrush(QColor(json["currentColor"].toString()));
  645. ellipse->setPen(QPen(
  646. QColor(json["penColor"].toString()),
  647. json["penWidth"].toDouble()
  648. ));
  649. ellipse->setName(json["name"].toString());
  650. item = ellipse;
  651. }
  652. else if (itemType == "rectangle") {
  653. QJsonArray rectArray = json["rect"].toArray();
  654. if (rectArray.size() != 4) return nullptr;
  655. ResizableRectangle *rect = new ResizableRectangle(
  656. rectArray[0].toDouble(),
  657. rectArray[1].toDouble(),
  658. rectArray[2].toDouble(),
  659. rectArray[3].toDouble()
  660. );
  661. rect->setPressedColor(QColor(json["pressedColor"].toString()));
  662. rect->setReleasedColor(QColor(json["releasedColor"].toString()));
  663. rect->setBrush(QColor(json["currentColor"].toString()));
  664. rect->setPen(QPen(
  665. QColor(json["penColor"].toString()),
  666. json["penWidth"].toDouble()
  667. ));
  668. rect->setName(json["name"].toString());
  669. item = rect;
  670. }
  671. // 设置基础属性
  672. if (item) {
  673. item->setX(json["x"].toDouble());
  674. item->setY(json["y"].toDouble());
  675. item->setZValue(json["zValue"].toDouble());
  676. item->setVisible(json["visible"].toBool());
  677. }
  678. return item;
  679. }
  680. QString HMIDocument::title() const { return m_title; }
  681. void HMIDocument::setTitle(const QString &title) { m_title = title; }
  682. QGraphicsView *HMIDocument::view() const { return m_view; }
  683. QGraphicsScene *HMIDocument::scene() const { return m_scene; }
  684. void HMIDocument::setDrawEllipse(bool enable) { m_canDrawEllipse = enable; }
  685. void HMIDocument::setDrawRectangle(bool enable) { m_canDrawRectangle = enable; }
  686. void HMIDocument::markModified() { setModified(true); }