Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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