综合平台编辑器
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.

594 lines
21 KiB

  1. #include "hmimodule.h"
  2. #include <QGraphicsScene>
  3. #include <QGraphicsView>
  4. #include <QDateTime>
  5. #include <QTabWidget>
  6. #include <QMessageBox>
  7. #include <QVBoxLayout>
  8. #include <algorithm>
  9. #include <QColorDialog>
  10. #include <QFile>
  11. #include <QJsonDocument>
  12. #include <QJsonArray>
  13. #include <QJsonObject>
  14. #include <QDebug>
  15. HMIModule::HMIModule(Ui::MainWindow* ui, QObject *parent)
  16. : QObject{parent}, ui_(ui), m_scene(nullptr)
  17. {
  18. // 初始化 m_pageOrder
  19. m_pageOrder.append(1); // 初始页面
  20. }
  21. void HMIModule::setButtonIcon(QAbstractButton *button, const QString &iconPath)
  22. {
  23. button->setIcon(QIcon(iconPath));
  24. button->setIconSize(QSize(30, 30));
  25. }
  26. void HMIModule::init() {
  27. setButtonIcon(ui_->button, ":/resource/image/button.png");
  28. setButtonIcon(ui_->lamp, ":/resource/image/lamp.png");
  29. setButtonIcon(ui_->addPage, ":/resource/image/add.png");
  30. setButtonIcon(ui_->deletePage, ":/resource/image/delete.png");
  31. setButtonIcon(ui_->prePage, ":/resource/image/pre.png");
  32. setButtonIcon(ui_->nextPage, ":/resource/image/next.png");
  33. if (!ui_->hmiGraphicsView->scene()) {
  34. m_scene = new CustomGraphicsScene(ui_->hmiGraphicsView, this);
  35. m_scene->setSceneRect(0, 0, 800, 600);
  36. ui_->hmiGraphicsView->setScene(m_scene);
  37. } else {
  38. m_scene = qobject_cast<CustomGraphicsScene*>(ui_->hmiGraphicsView->scene());
  39. Q_ASSERT(m_scene);
  40. }
  41. connect(ui_->button, &QPushButton::clicked, this, &HMIModule::prepareToCreateButton);
  42. connect(ui_->lamp, &QPushButton::clicked, this, &HMIModule::prepareToCreateIndicator);
  43. connect(m_scene, &CustomGraphicsScene::componentCreated, this, &HMIModule::onComponentCreated);
  44. connect(m_scene, &CustomGraphicsScene::copyRequestFromScene, this, &HMIModule::onCopyRequested);
  45. connect(m_scene, &CustomGraphicsScene::pasteRequestFromScene, this, &HMIModule::onPasteRequested);
  46. connect(m_scene, &CustomGraphicsScene::deleteRequestFromScene, this, &HMIModule::onDeleteRequested);
  47. // 连接新增的槽函数
  48. connect(ui_->addPage, &QPushButton::clicked, this, &HMIModule::addPage);
  49. connect(ui_->deletePage, &QPushButton::clicked, this, &HMIModule::deletePage);
  50. connect(ui_->prePage, &QPushButton::clicked, this, &HMIModule::prePage);
  51. connect(ui_->nextPage, &QPushButton::clicked, this, &HMIModule::nextPage);
  52. // 连接信号和槽
  53. connect(this, &HMIModule::pageAdded, this, &HMIModule::onPageAdded);
  54. connect(this, &HMIModule::pageRemoved, this, &HMIModule::onPageRemoved);
  55. }
  56. bool HMIModule::saveToFile(const QString& filePath)
  57. {
  58. QTabWidget* tabWidget = ui_->tabWidget_2;
  59. QJsonArray pagesArray;
  60. for (int i = 0; i < tabWidget->count(); ++i) {
  61. QWidget* page = tabWidget->widget(i);
  62. QString pageName = tabWidget->tabText(i);
  63. QGraphicsView* graphicsView = page->findChild<QGraphicsView*>();
  64. if (!graphicsView) continue;
  65. QGraphicsScene* scene = graphicsView->scene();
  66. if (!scene) continue;
  67. QJsonObject pageObject;
  68. pageObject["pageName"] = pageName;
  69. QJsonArray componentsArray;
  70. for (QGraphicsItem* item : scene->items()) {
  71. HmiComponent* component = dynamic_cast<HmiComponent*>(item);
  72. if (!component)
  73. continue;
  74. QJsonObject compObj;
  75. // 组件类型
  76. if (dynamic_cast<HmiButton*>(component)) {
  77. compObj["type"] = "Button";
  78. } else if (dynamic_cast<HmiIndicator*>(component)) {
  79. compObj["type"] = "Indicator";
  80. } else {
  81. continue; // 忽略未知类型
  82. }
  83. // 位置
  84. QPointF pos = component->pos();
  85. compObj["x"] = pos.x();
  86. compObj["y"] = pos.y();
  87. // 颜色 (保存在HEX)
  88. compObj["color"] = component->color().name();
  89. // On / Off颜色
  90. compObj["onColor"] = component->onColor().name();
  91. compObj["offColor"] = component->offColor().name();
  92. // 组件名称
  93. compObj["componentName"] = component->componentName();
  94. // 新增地址属性
  95. compObj["address"] = component->address();
  96. componentsArray.append(compObj);
  97. }
  98. pageObject["components"] = componentsArray;
  99. pagesArray.append(pageObject);
  100. }
  101. QJsonObject rootObject;
  102. rootObject["pages"] = pagesArray;
  103. QJsonDocument doc(rootObject);
  104. QFile file(filePath);
  105. if (!file.open(QIODevice::WriteOnly)) {
  106. emit logMessageGenerated(QString("无法打开文件进行保存: %1").arg(filePath));
  107. return false;
  108. }
  109. file.write(doc.toJson(QJsonDocument::Indented));
  110. file.close();
  111. emit logMessageGenerated(QString("[%1] 保存成功: %2")
  112. .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
  113. .arg(filePath));
  114. return true;
  115. }
  116. // 清空所有页面,重建页面并加载组件
  117. bool HMIModule::openFromFile(const QString& filePath)
  118. {
  119. QFile file(filePath);
  120. if (!file.open(QIODevice::ReadOnly)) {
  121. emit logMessageGenerated(QString("无法打开文件进行加载: %1").arg(filePath));
  122. return false;
  123. }
  124. QByteArray data = file.readAll();
  125. file.close();
  126. QJsonParseError parseError;
  127. QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
  128. if (parseError.error != QJsonParseError::NoError) {
  129. emit logMessageGenerated(QString("JSON解析错误: %1").arg(parseError.errorString()));
  130. return false;
  131. }
  132. if (!doc.isObject()) {
  133. emit logMessageGenerated("JSON格式错误,不是对象");
  134. return false;
  135. }
  136. QJsonObject rootObj = doc.object();
  137. if (!rootObj.contains("pages") || !rootObj["pages"].isArray()) {
  138. emit logMessageGenerated("JSON缺少pages数组");
  139. return false;
  140. }
  141. QJsonArray pagesArray = rootObj["pages"].toArray();
  142. QTabWidget* tabWidget = ui_->tabWidget_2;
  143. // 清空所有现有页面
  144. while (tabWidget->count() > 0) {
  145. QWidget* page = tabWidget->widget(0);
  146. tabWidget->removeTab(0);
  147. delete page;
  148. }
  149. m_availablePageNumbers.clear();
  150. m_pageOrder.clear();
  151. for (const QJsonValue& pageVal : pagesArray) {
  152. if (!pageVal.isObject())
  153. continue;
  154. QJsonObject pageObj = pageVal.toObject();
  155. QString pageName = pageObj["pageName"].toString("Unnamed");
  156. int pageNumber = 0;
  157. if (pageName.startsWith("Page ")) {
  158. pageNumber = pageName.mid(5).toInt();
  159. }
  160. // 新建页面
  161. QWidget* newPage = new QWidget(tabWidget);
  162. newPage->setObjectName(pageName);
  163. QGraphicsView* newGraphicsView = new QGraphicsView(newPage);
  164. CustomGraphicsScene* newScene = new CustomGraphicsScene(newGraphicsView, this);
  165. // 设置大小,与默认页面相同
  166. QSize defaultSize(691, 381);
  167. newGraphicsView->resize(defaultSize);
  168. newScene->setSceneRect(0, 0, 800, 600);
  169. newGraphicsView->setScene(newScene);
  170. QVBoxLayout* layout = new QVBoxLayout(newPage);
  171. layout->addWidget(newGraphicsView);
  172. newPage->setLayout(layout);
  173. int tabIndex = tabWidget->addTab(newPage, pageName);
  174. // 连接信号
  175. connect(newScene, &CustomGraphicsScene::componentCreated, this, &HMIModule::onComponentCreated);
  176. connect(newScene, &CustomGraphicsScene::copyRequestFromScene, this, &HMIModule::onCopyRequested);
  177. connect(newScene, &CustomGraphicsScene::pasteRequestFromScene, this, &HMIModule::onPasteRequested);
  178. connect(newScene, &CustomGraphicsScene::deleteRequestFromScene, this, &HMIModule::onDeleteRequested);
  179. m_pageOrder.append(pageNumber);
  180. QJsonArray compArray = pageObj["components"].toArray();
  181. for (const QJsonValue& compVal : compArray) {
  182. if (!compVal.isObject())
  183. continue;
  184. QJsonObject compObj = compVal.toObject();
  185. QString typeStr = compObj["type"].toString();
  186. double x = compObj["x"].toDouble();
  187. double y = compObj["y"].toDouble();
  188. QString colorName = compObj["color"].toString();
  189. QString onColorName = compObj["onColor"].toString();
  190. QString offColorName = compObj["offColor"].toString();
  191. QString componentName = compObj["componentName"].toString();
  192. int address = compObj["address"].toInt(0);
  193. HmiComponent* newItem = nullptr;
  194. if (typeStr == "Button") {
  195. newItem = new HmiButton();
  196. } else if (typeStr == "Indicator") {
  197. newItem = new HmiIndicator();
  198. } else {
  199. continue;
  200. }
  201. if (newItem) {
  202. newItem->setPos(QPointF(x, y));
  203. QColor color(colorName);
  204. if (color.isValid()) {
  205. newItem->setColor(color);
  206. }
  207. QColor onColor(onColorName);
  208. if (onColor.isValid())
  209. newItem->setOnColor(onColor);
  210. QColor offColor(offColorName);
  211. if (offColor.isValid())
  212. newItem->setOffColor(offColor);
  213. QColor currentColor(colorName);
  214. if (currentColor.isValid()) {
  215. // 可以根据实际业务逻辑判断,是否恢复为onColor或offColor
  216. // 这里直接恢复当前颜色
  217. newItem->setColor(currentColor);
  218. } else {
  219. // 如果当前颜色无效,则使用 offColor 作为默认颜色
  220. newItem->setColor(newItem->offColor());
  221. }
  222. newItem->setComponentName(componentName);
  223. // 设置地址
  224. newItem->setAddress(address);
  225. newScene->addItem(newItem);
  226. setupNewComponent(newItem);
  227. }
  228. }
  229. }
  230. // 自动切换到第一页
  231. if (tabWidget->count() > 0) {
  232. tabWidget->setCurrentIndex(0);
  233. }
  234. emit logMessageGenerated(QString("[%1] 打开文件成功: %2")
  235. .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
  236. .arg(filePath));
  237. return true;
  238. }
  239. void HMIModule::setupNewComponent(HmiComponent* item)
  240. {
  241. if (!item) return;
  242. connect(item, &HmiComponent::selected, this, &HMIModule::onComponentSelected);
  243. connect(item, &HmiComponent::copyRequested, this, &HMIModule::onCopyRequested);
  244. connect(item, &HmiComponent::deleteRequested, this, &HMIModule::onDeleteRequested);
  245. connect(item, &HmiComponent::changeOnColorRequested, this, &HMIModule::onChangeOnColorRequested);
  246. connect(item, &HmiComponent::changeOffColorRequested, this, &HMIModule::onChangeOffColorRequested);
  247. QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
  248. QString log = QString("[%1] 创建 %2 组件").arg(currentTime).arg(item->componentName());
  249. emit logMessageGenerated(log);
  250. }
  251. void HMIModule::prepareToCreateButton()
  252. {
  253. // 获取当前 TabWidget 中选中的 GraphicsView
  254. QTabWidget* tabWidget = ui_->tabWidget_2;
  255. QWidget* currentPage = tabWidget->currentWidget();
  256. QGraphicsView* currentGraphicsView = currentPage->findChild<QGraphicsView*>();
  257. CustomGraphicsScene* currentScene = qobject_cast<CustomGraphicsScene*>(currentGraphicsView->scene());
  258. if (currentScene) {
  259. currentScene->setComponentTypeToCreate(ComponentType::Button);
  260. currentScene->setMode(CustomGraphicsScene::Mode::CreateItem);
  261. }
  262. }
  263. void HMIModule::prepareToCreateIndicator()
  264. {
  265. // 获取当前 TabWidget 中选中的 GraphicsView
  266. QTabWidget* tabWidget = ui_->tabWidget_2;
  267. QWidget* currentPage = tabWidget->currentWidget();
  268. QGraphicsView* currentGraphicsView = currentPage->findChild<QGraphicsView*>();
  269. CustomGraphicsScene* currentScene = qobject_cast<CustomGraphicsScene*>(currentGraphicsView->scene());
  270. if (currentScene) {
  271. currentScene->setComponentTypeToCreate(ComponentType::Indicator);
  272. currentScene->setMode(CustomGraphicsScene::Mode::CreateItem);
  273. }
  274. }
  275. void HMIModule::onComponentCreated(HmiComponent* item)
  276. {
  277. setupNewComponent(item);
  278. }
  279. void HMIModule::onCopyRequested()
  280. {
  281. // 获取当前 TabWidget 中选中的 GraphicsView
  282. QTabWidget* tabWidget = ui_->tabWidget_2;
  283. QWidget* currentPage = tabWidget->currentWidget();
  284. QGraphicsView* currentGraphicsView = currentPage->findChild<QGraphicsView*>();
  285. CustomGraphicsScene* currentScene = qobject_cast<CustomGraphicsScene*>(currentGraphicsView->scene());
  286. QList<QGraphicsItem*> selected = currentScene->selectedItems();
  287. if (selected.isEmpty()) return;
  288. HmiComponent* itemToCopy = qgraphicsitem_cast<HmiComponent*>(selected.first());
  289. if (!itemToCopy) return;
  290. if (dynamic_cast<HmiButton*>(itemToCopy)) {
  291. m_copiedType = ComponentType::Button;
  292. } else if (dynamic_cast<HmiIndicator*>(itemToCopy)) {
  293. m_copiedType = ComponentType::Indicator;
  294. }
  295. m_copiedColor = itemToCopy->color();
  296. m_hasCopiedItem = true;
  297. emit logMessageGenerated(QString("[%1] 复制组件: %2")
  298. .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
  299. .arg(itemToCopy->componentName()));
  300. }
  301. void HMIModule::onPasteRequested(const QPointF& scenePos)
  302. {
  303. if (!m_hasCopiedItem) return;
  304. // 获取当前 TabWidget 中选中的 GraphicsView
  305. QTabWidget* tabWidget = ui_->tabWidget_2;
  306. QWidget* currentPage = tabWidget->currentWidget();
  307. QGraphicsView* currentGraphicsView = currentPage->findChild<QGraphicsView*>();
  308. CustomGraphicsScene* currentScene = qobject_cast<CustomGraphicsScene*>(currentGraphicsView->scene());
  309. HmiComponent* newItem = nullptr;
  310. if (m_copiedType == ComponentType::Button) {
  311. newItem = new HmiButton();
  312. } else {
  313. newItem = new HmiIndicator();
  314. }
  315. if (newItem) {
  316. newItem->setColor(m_copiedColor);
  317. newItem->setPos(scenePos);
  318. currentScene->addItem(newItem); // 使用当前页面的 scene
  319. setupNewComponent(newItem);
  320. }
  321. }
  322. void HMIModule::onDeleteRequested()
  323. {
  324. // 获取当前 TabWidget 中选中的 GraphicsView
  325. QTabWidget* tabWidget = ui_->tabWidget_2;
  326. QWidget* currentPage = tabWidget->currentWidget();
  327. QGraphicsView* currentGraphicsView = currentPage->findChild<QGraphicsView*>();
  328. CustomGraphicsScene* currentScene = qobject_cast<CustomGraphicsScene*>(currentGraphicsView->scene());
  329. QList<QGraphicsItem*> selected = currentScene->selectedItems();
  330. if (selected.isEmpty()) return;
  331. emit logMessageGenerated(QString("[%1] 删除 %2 个组件")
  332. .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
  333. .arg(selected.count()));
  334. for (int i = selected.count() - 1; i >= 0; --i) {
  335. QGraphicsItem* item = selected.at(i);
  336. currentScene->removeItem(item); // 使用当前页面的 scene
  337. delete item;
  338. }
  339. }
  340. // 添加页面后的槽函数
  341. void HMIModule::onPageAdded(int pageNumber, int index)
  342. {
  343. if (index < m_pageOrder.size()) {
  344. m_pageOrder[index] = pageNumber;
  345. } else {
  346. m_pageOrder.append(pageNumber);
  347. }
  348. }
  349. // 删除页面后的槽函数
  350. void HMIModule::onPageRemoved(int pageNumber, int index)
  351. {
  352. if (index < m_pageOrder.size()) {
  353. m_pageOrder[index] = -1;
  354. }
  355. }
  356. void HMIModule::onChangeOnColorRequested(HmiComponent* item)
  357. {
  358. QColor color = QColorDialog::getColor(item->onColor(), nullptr, "选择 ON 状态颜色");
  359. if (color.isValid()) {
  360. item->setOnColor(color);
  361. emit logMessageGenerated(QString("[%1] 设置 %2 的 ON 状态颜色为 %3")
  362. .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
  363. .arg(item->componentName())
  364. .arg(color.name()));
  365. }
  366. }
  367. void HMIModule::onChangeOffColorRequested(HmiComponent* item)
  368. {
  369. QColor color = QColorDialog::getColor(item->offColor(), nullptr, "选择 OFF 状态颜色");
  370. if (color.isValid()) {
  371. item->setOffColor(color); // 同时更新当前显示颜色
  372. emit logMessageGenerated(QString("[%1] 设置 %2 的 OFF 状态颜色为 %3")
  373. .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
  374. .arg(item->componentName())
  375. .arg(color.name()));
  376. }
  377. }
  378. void HMIModule::onComponentSelected(HmiComponent* item)
  379. {
  380. // 不需要任何操作,可以留空
  381. }
  382. // 新增页面的槽函数
  383. void HMIModule::addPage()
  384. {
  385. QTabWidget* tabWidget = ui_->tabWidget_2;
  386. int pageNumber;
  387. int insertIndex = -1;
  388. int tabIndex; // 在此处声明 tabIndex
  389. // 检查是否有可用的页面编号
  390. if (!m_availablePageNumbers.isEmpty()) {
  391. pageNumber = m_availablePageNumbers.first();
  392. m_availablePageNumbers.removeFirst();
  393. // 查找可用的插入位置
  394. for (int i = 0; i < m_pageOrder.size(); ++i) {
  395. if (m_pageOrder[i] == -1) {
  396. insertIndex = i;
  397. break;
  398. }
  399. }
  400. } else {
  401. pageNumber = ++m_pageCount;
  402. }
  403. // 获取 Page 1 的 Scene
  404. CustomGraphicsScene* firstScene = m_scene;
  405. // 创建新的页面部件
  406. QWidget* newPage = new QWidget(tabWidget);
  407. QString pageName = QString("Page %1").arg(pageNumber);
  408. newPage->setObjectName(pageName);
  409. // 创建新的 GraphicsView 和 Scene
  410. QGraphicsView* newGraphicsView = new QGraphicsView(newPage);
  411. CustomGraphicsScene* newScene = new CustomGraphicsScene(newGraphicsView, this);
  412. // 设置 GraphicsView 的大小
  413. QSize newViewSize(691, 381); // 设置为指定的宽度和高度
  414. newGraphicsView->resize(newViewSize);
  415. // 设置 GraphicsScene 的大小与 Page 1 相同
  416. QRectF newSceneRect = firstScene->sceneRect();
  417. newScene->setSceneRect(newSceneRect);
  418. newGraphicsView->setScene(newScene);
  419. // 布局
  420. QVBoxLayout* layout = new QVBoxLayout(newPage);
  421. layout->addWidget(newGraphicsView);
  422. newPage->setLayout(layout);
  423. // 添加到 TabWidget
  424. if (insertIndex != -1) {
  425. tabIndex = insertIndex;
  426. tabWidget->insertTab(tabIndex, newPage, pageName);
  427. } else {
  428. tabIndex = tabWidget->addTab(newPage, pageName);
  429. }
  430. // 连接信号和槽
  431. connect(newScene, &CustomGraphicsScene::componentCreated, this, &HMIModule::onComponentCreated);
  432. connect(newScene, &CustomGraphicsScene::copyRequestFromScene, this, &HMIModule::onCopyRequested);
  433. connect(newScene, &CustomGraphicsScene::pasteRequestFromScene, this, &HMIModule::onPasteRequested);
  434. connect(newScene, &CustomGraphicsScene::deleteRequestFromScene, this, &HMIModule::onDeleteRequested);
  435. // 发送 pageAdded 信号
  436. emit pageAdded(pageNumber, tabIndex);
  437. tabWidget->setCurrentWidget(newPage);
  438. // 记录日志
  439. emit logMessageGenerated(QString("[%1] 添加页面: %2").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(pageName));
  440. }
  441. // 删除页面的槽函数
  442. void HMIModule::deletePage()
  443. {
  444. QTabWidget* tabWidget = ui_->tabWidget_2;
  445. int currentIndex = tabWidget->currentIndex();
  446. if (tabWidget->count() == 1) {
  447. QMessageBox::information(nullptr, "提示", "这是最后一页,不能删除!");
  448. return;
  449. }
  450. if (currentIndex >= 0) {
  451. // 获取要删除的页面
  452. QWidget* currentPage = tabWidget->widget(currentIndex);
  453. QString pageName = tabWidget->tabText(currentIndex);
  454. // 从页面名称中提取页面编号
  455. int pageNumber = pageName.remove("Page ").toInt();
  456. // 将删除的页面编号添加到可用列表中
  457. m_availablePageNumbers.append(pageNumber);
  458. std::sort(m_availablePageNumbers.begin(), m_availablePageNumbers.end());
  459. // 从 TabWidget 中移除页面
  460. tabWidget->removeTab(currentIndex);
  461. // 删除页面
  462. delete currentPage;
  463. // 发送 pageRemoved 信号
  464. emit pageRemoved(pageNumber, currentIndex);
  465. // 记录日志
  466. emit logMessageGenerated(QString("[%1] 删除页面: %2").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(pageName));
  467. }
  468. }
  469. // 前一页的槽函数
  470. void HMIModule::prePage()
  471. {
  472. QTabWidget* tabWidget = ui_->tabWidget_2;
  473. int currentIndex = tabWidget->currentIndex();
  474. if (currentIndex > 0) {
  475. tabWidget->setCurrentIndex(currentIndex - 1);
  476. }
  477. }
  478. // 后一页的槽函数
  479. void HMIModule::nextPage()
  480. {
  481. QTabWidget* tabWidget = ui_->tabWidget_2;
  482. int currentIndex = tabWidget->currentIndex();
  483. if (currentIndex < tabWidget->count() - 1) {
  484. tabWidget->setCurrentIndex(currentIndex + 1);
  485. }
  486. }