Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

541 righe
18 KiB

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include "item.h"
  4. #include <QListWidgetItem>
  5. #include <QMimeData>
  6. #include <QDrag>
  7. #include <QPainter>
  8. #include <QMouseEvent>
  9. #include <QDropEvent>
  10. #include <QGraphicsView>
  11. #include <QMessageBox>
  12. #include <QAction>
  13. #include <QFileDialog>
  14. #include "creatitem.h"
  15. MainWindow::MainWindow(QWidget *parent)
  16. : QMainWindow(parent)
  17. , ui(new Ui::MainWindow)
  18. {
  19. ui->setupUi(this);
  20. registerManager = new RegisterManager(this);
  21. modbusManager = new ModbusManager(registerManager, this);
  22. // 初始化PLC标签页
  23. ui->plc_tab_widget->setTabsClosable(true);
  24. ui->plc_tab_widget->setMovable(true);
  25. // 初始化HMI标签页
  26. ui->hmi_tab_widget->setTabsClosable(true);
  27. ui->hmi_tab_widget->setMovable(true);
  28. /* 2. 列表 */
  29. ui->listWidget->setViewMode(QListView::IconMode);
  30. ui->listWidget->setIconSize(QSize(60, 30));
  31. ui->listWidget->setDragEnabled(false);
  32. ui->listWidget->viewport()->installEventFilter(this);
  33. newPage(true);
  34. newPage(false);
  35. createComponents();
  36. connect(ui->plc_tab_widget, &QTabWidget::tabCloseRequested,this,&MainWindow::onTabCloseRequested);
  37. connect(ui->hmi_tab_widget, &QTabWidget::tabCloseRequested,this,&MainWindow::onTabCloseRequested);
  38. connect(ui->listWidget,&QListWidget::currentTextChanged,this,&MainWindow::onListwidgetCurrenttextchanged);
  39. connect(ui->action_plc,&QAction::triggered,this,&MainWindow::plcChange);
  40. connect(ui->action_hmi,&QAction::triggered,this,&MainWindow::hmiChange);
  41. connect(ui->action_new, &QAction::triggered, this, &MainWindow::newProject);
  42. connect(ui->action_save, &QAction::triggered, this, &MainWindow::saveProject);
  43. connect(ui->action_open, &QAction::triggered, this, &MainWindow::openProject);
  44. connect(ui->action_connect, &QAction::triggered, this, &MainWindow::connection);
  45. connect(ui->action_disconnect, &QAction::triggered, this, &MainWindow::disconnection);
  46. connect(ui->btn_insert, &QPushButton::clicked, this, &MainWindow::btnInsertClicked);
  47. connect(modbusManager, &ModbusManager::connectionStatusChanged,
  48. this, &MainWindow::updateConnectionStatus);
  49. // connect(modbusManager, &ModbusManager::connectionStatusChanged,
  50. // this, &PLC::updateConnectionStatus);
  51. // connect(modbusManager, &ModbusManager::errorOccurred,
  52. // this, &PLC::handleModbusError);
  53. // connect(this, &PLC::requestWriteRegister,
  54. // modbusManager, &ModbusManager::writeRegister);
  55. ui->stackedWidget->setCurrentIndex(0);
  56. setWindowTitle("未命名项目 - PLC编辑器");
  57. }
  58. MainWindow::~MainWindow()
  59. {
  60. qDeleteAll(plcScenes);
  61. qDeleteAll(plcViews);
  62. qDeleteAll(hmiScenes);
  63. qDeleteAll(hmiViews);
  64. delete ui;
  65. }
  66. void MainWindow::newPage(bool isPlc)
  67. {
  68. // 创建新场景
  69. QGraphicsScene* newScene = new QGraphicsScene(this);
  70. // 创建新视图
  71. MyGraphicsView* newView = new MyGraphicsView(this);
  72. newView->setScene(newScene);
  73. newView->setSceneRect(0, 0, 800, 600);
  74. newView->setDragMode(QGraphicsView::RubberBandDrag);
  75. if (isPlc) {
  76. // 添加到PLC标签页
  77. int newIndex = ui->plc_tab_widget->addTab(newView, QString("页面 %1").arg(plcScenes.size() + 1));
  78. ui->plc_tab_widget->setCurrentIndex(newIndex);
  79. // 保存场景和视图
  80. plcScenes.append(newScene);
  81. plcViews.append(newView);
  82. connect(newView, &MyGraphicsView::itemBoundToRegister,
  83. registerManager, &RegisterManager::bindItem);
  84. connect(newView, &MyGraphicsView::itemResetRegister,
  85. registerManager, &RegisterManager::unbindItem);
  86. } else {
  87. // 添加到HMI标签页
  88. int newIndex = ui->hmi_tab_widget->addTab(newView, QString("页面 %1").arg(hmiScenes.size() + 1));
  89. ui->hmi_tab_widget->setCurrentIndex(newIndex);
  90. // 保存场景和视图
  91. hmiScenes.append(newScene);
  92. hmiViews.append(newView);
  93. connect(newView, &MyGraphicsView::itemBoundToRegister,
  94. registerManager, &RegisterManager::bindItem);
  95. connect(newView, &MyGraphicsView::itemResetRegister,
  96. registerManager, &RegisterManager::unbindItem);
  97. }
  98. }
  99. void MainWindow::createComponents()
  100. {
  101. ui->listWidget->clear();
  102. if (currentIsPLC_)
  103. {
  104. struct Comp { QString name; QColor color; };
  105. const QVector<Comp> comps = {
  106. {"常开", Qt::blue},
  107. {"常闭", Qt::red},
  108. {"比较", Qt::green},
  109. {"线圈", Qt::darkYellow}
  110. };
  111. for (const Comp &c : comps) {
  112. QListWidgetItem *it = new QListWidgetItem(c.name, ui->listWidget);
  113. QPixmap pix(60, 30);
  114. pix.fill(Qt::white);
  115. QPainter p(&pix);
  116. p.setRenderHint(QPainter::Antialiasing);
  117. p.setBrush(c.color.lighter(150));
  118. p.setPen(QPen(c.color, 2));
  119. p.drawRoundedRect(QRect(5, 5, 50, 20), 3, 3);
  120. p.setPen(Qt::black);
  121. p.drawText(QRect(5, 5, 50, 20), Qt::AlignCenter, c.name);
  122. it->setIcon(QIcon(pix));
  123. it->setData(Qt::UserRole, c.name);
  124. }
  125. }
  126. else
  127. {
  128. struct Comp { QString name; QColor color; };
  129. const QVector<Comp> comps = {
  130. {"按钮", Qt::blue},
  131. {"指示灯", Qt::red}
  132. };
  133. for (const Comp &c : comps) {
  134. QListWidgetItem *it = new QListWidgetItem(c.name, ui->listWidget);
  135. QPixmap pix(60, 30);
  136. pix.fill(Qt::white);
  137. QPainter p(&pix);
  138. p.setRenderHint(QPainter::Antialiasing);
  139. p.setBrush(c.color.lighter(150));
  140. p.setPen(QPen(c.color, 2));
  141. p.drawRoundedRect(QRect(5, 5, 50, 20), 3, 3);
  142. p.setPen(Qt::black);
  143. p.drawText(QRect(5, 5, 50, 20), Qt::AlignCenter, c.name);
  144. it->setIcon(QIcon(pix));
  145. it->setData(Qt::UserRole, c.name);
  146. }
  147. }
  148. }
  149. void MainWindow::clearScene()
  150. {
  151. if(currentIsPLC_)
  152. {
  153. if (currentPageIndex() >= 0 && currentPageIndex() < plcScenes.size()) {
  154. plcScenes[currentPageIndex()]->clear();
  155. }
  156. }
  157. else
  158. {
  159. if (currentPageIndex() >= 0 && currentPageIndex() < hmiScenes.size()) {
  160. hmiScenes[currentPageIndex()]->clear();
  161. }
  162. }
  163. }
  164. void MainWindow::applyProjectToScene(const Project &proj, int pageIndex)
  165. {
  166. if (currentIsPLC_)
  167. {
  168. if (pageIndex < 0 || pageIndex >= plcScenes.size())
  169. return;
  170. plcScenes[pageIndex]->clear();
  171. QVector<Item*> itemObjs;
  172. for (const auto& d : proj.items_) {
  173. Item* item = creatItem(d.type);
  174. if (!item) continue;
  175. item->setPos(d.x, d.y);
  176. item->setRegisterId(d.registerId);
  177. connect(item, &Item::requestCopy, plcViews[pageIndex], &MyGraphicsView::onItemRequestCopy);
  178. connect(item, &Item::requestDelete, plcViews[pageIndex], &MyGraphicsView::onItemRequestDelete);
  179. connect(item, &Item::requestBindRegister, plcViews[pageIndex], &MyGraphicsView::onItemRequestBindRegister);
  180. plcScenes[pageIndex]->addItem(item);
  181. registerManager->bindItem(item,d.registerId);
  182. itemObjs.append(item);
  183. }
  184. for (const auto& c : proj.connections_) {
  185. if (c.from >= 0 && c.from < itemObjs.size() && c.to >= 0 && c.to < itemObjs.size()) {
  186. Connection* conn = new Connection(
  187. itemObjs[c.from], static_cast<Item::AnchorType>(c.fromType),
  188. itemObjs[c.to], static_cast<Item::AnchorType>(c.toType));
  189. plcScenes[pageIndex]->addItem(conn);
  190. }
  191. }
  192. }
  193. else
  194. {
  195. if (pageIndex < 0 || pageIndex >= hmiScenes.size())
  196. return;
  197. hmiScenes[pageIndex]->clear();
  198. QVector<Item*> itemObjs;
  199. for (const auto& d : proj.items_) {
  200. Item* item = creatItem(d.type);
  201. if (!item) continue;
  202. item->setPos(d.x, d.y);
  203. item->setRegisterId(d.registerId);
  204. connect(item, &Item::requestCopy, hmiViews[pageIndex], &MyGraphicsView::onItemRequestCopy);
  205. connect(item, &Item::requestDelete, hmiViews[pageIndex], &MyGraphicsView::onItemRequestDelete);
  206. connect(item, &Item::requestBindRegister, hmiViews[pageIndex], &MyGraphicsView::onItemRequestBindRegister);
  207. hmiScenes[pageIndex]->addItem(item);
  208. registerManager->bindItem(item,d.registerId);
  209. itemObjs.append(item);
  210. }
  211. }
  212. }
  213. void MainWindow::extractSceneToProject(Project &proj, int pageIndex)
  214. {
  215. if (currentIsPLC_)
  216. {
  217. if (pageIndex < 0 || pageIndex >= plcScenes.size()) return;
  218. proj.clear();
  219. QList<Item*> items;
  220. for (QGraphicsItem* gi : plcScenes[pageIndex]->items()){
  221. if (Item* it = dynamic_cast<Item*>(gi)){
  222. items.append(it);
  223. }
  224. }
  225. for (Item* it : items) {
  226. Project::ItemData d;
  227. d.type = it->itemType();
  228. d.x = it->pos().x();
  229. d.y = it->pos().y();
  230. d.registerId = it->registerId();
  231. proj.items_.append(d);
  232. }
  233. for (QGraphicsItem* gi : plcScenes[pageIndex]->items()) {
  234. if (Connection* conn = dynamic_cast<Connection*>(gi)) {
  235. int fromIdx = items.indexOf(conn->from_);
  236. int toIdx = items.indexOf(conn->to_);
  237. if (fromIdx >= 0 && toIdx >= 0) {
  238. Project::ConnectionData c;
  239. c.from = fromIdx;
  240. c.to = toIdx;
  241. c.fromType = static_cast<int>(conn->fromType_);
  242. c.toType = static_cast<int>(conn->toType_);
  243. proj.connections_.append(c);
  244. }
  245. }
  246. }
  247. }
  248. else
  249. {
  250. if (pageIndex < 0 || pageIndex >= hmiScenes.size()) return;
  251. proj.clear();
  252. QList<Item*> items;
  253. for (QGraphicsItem* gi : hmiScenes[pageIndex]->items()){
  254. if (Item* it = dynamic_cast<Item*>(gi)){
  255. items.append(it);
  256. }
  257. }
  258. for (Item* it : items) {
  259. Project::ItemData d;
  260. d.type = it->itemType();
  261. d.x = it->pos().x();
  262. d.y = it->pos().y();
  263. d.registerId = it->registerId();
  264. proj.items_.append(d);
  265. }
  266. }
  267. }
  268. int MainWindow::currentPageIndex() const
  269. {
  270. if (currentIsPLC_) {
  271. return ui->plc_tab_widget->currentIndex();
  272. } else {
  273. return ui->hmi_tab_widget->currentIndex();
  274. }
  275. }
  276. bool MainWindow::eventFilter(QObject *obj, QEvent *event)
  277. {
  278. if (obj == ui->listWidget->viewport()) {
  279. static QListWidgetItem *dragItem = nullptr;
  280. static QPoint startPos;
  281. if (event->type() == QEvent::MouseButtonPress) {
  282. auto *me = static_cast<QMouseEvent*>(event);
  283. if (me->button() == Qt::LeftButton) {
  284. dragItem = ui->listWidget->itemAt(me->pos());
  285. startPos = me->pos();
  286. }
  287. } else if (event->type() == QEvent::MouseMove && dragItem) {
  288. auto *me = static_cast<QMouseEvent*>(event);
  289. if ((me->pos() - startPos).manhattanLength()
  290. >= QApplication::startDragDistance()) {
  291. QString type = dragItem->data(Qt::UserRole).toString();
  292. QMimeData *mime = new QMimeData;
  293. mime->setData("application/x-component", type.toUtf8());
  294. QDrag *drag = new QDrag(this);
  295. drag->setMimeData(mime);
  296. drag->setPixmap(dragItem->icon().pixmap(ui->listWidget->iconSize()));
  297. drag->setHotSpot(drag->pixmap().rect().center());
  298. drag->exec(Qt::CopyAction);
  299. dragItem = nullptr;
  300. }
  301. } else if (event->type() == QEvent::MouseButtonRelease) {
  302. dragItem = nullptr;
  303. }
  304. }
  305. return QWidget::eventFilter(obj, event);
  306. }
  307. void MainWindow::plcChange()
  308. {
  309. currentIsPLC_ = true;
  310. createComponents();
  311. ui->stackedWidget->setCurrentIndex(0); // 显示PLC页面
  312. // setWindowTitle((plcFilePath_.isEmpty() ? "未命名项目" : QFileInfo(plcFilePath_).fileName()) + " - PLC编辑器");
  313. }
  314. void MainWindow::hmiChange()
  315. {
  316. currentIsPLC_ = false;
  317. createComponents();
  318. ui->stackedWidget->setCurrentIndex(1); // 显示HMI页面
  319. // setWindowTitle((hmiFilePath_.isEmpty() ? "未命名项目" : QFileInfo(hmiFilePath_).fileName()) + " - HMI编辑器");
  320. }
  321. void MainWindow::newProject()
  322. {
  323. newPage(currentIsPLC_);
  324. // 更新窗口标题
  325. if (currentIsPLC_) {
  326. setWindowTitle("未命名项目 - PLC编辑器");
  327. } else {
  328. setWindowTitle("未命名项目 - HMI编辑器");
  329. }
  330. }
  331. void MainWindow::openProject()
  332. {
  333. QString filePath;
  334. QString filter;
  335. if (currentIsPLC_) {
  336. filter = "PLC项目文件 (*.plcproj)";
  337. filePath = QFileDialog::getOpenFileName(this, "打开PLC项目", "", filter);
  338. } else {
  339. filter = "HMI项目文件 (*.hmiproj)";
  340. filePath = QFileDialog::getOpenFileName(this, "打开HMI项目", "", filter);
  341. }
  342. if (filePath.isEmpty()) return;
  343. Project project;
  344. if (project.loadFromFile(filePath)) {
  345. // 在当前模块新建一个页面
  346. newPage(currentIsPLC_);
  347. int newPageIndex = currentPageIndex();
  348. // 将项目应用到新页面
  349. applyProjectToScene(project, newPageIndex);
  350. // 更新窗口标题
  351. setWindowTitle(QFileInfo(filePath).fileName() +
  352. (currentIsPLC_ ? " - PLC编辑器" : " - HMI编辑器"));
  353. } else {
  354. QMessageBox::critical(this, "错误", "无法打开项目文件");
  355. }
  356. }
  357. void MainWindow::saveProject()
  358. {
  359. QString filePath;
  360. QString filter;
  361. QString defaultSuffix;
  362. if (currentIsPLC_) {
  363. filter = "PLC项目文件 (*.plcproj)";
  364. defaultSuffix = "plcproj";
  365. filePath = QFileDialog::getSaveFileName(this, "保存PLC项目", "", filter);
  366. } else {
  367. filter = "HMI项目文件 (*.hmiproj)";
  368. defaultSuffix = "hmiproj";
  369. filePath = QFileDialog::getSaveFileName(this, "保存HMI项目", "", filter);
  370. }
  371. if (filePath.isEmpty()) return;
  372. // 确保文件有正确的扩展名
  373. if (!filePath.endsWith("." + defaultSuffix, Qt::CaseInsensitive)) {
  374. filePath += "." + defaultSuffix;
  375. }
  376. Project project;
  377. // 从当前页面提取项目
  378. extractSceneToProject(project, currentPageIndex());
  379. if (project.saveToFile(filePath)) {
  380. // 更新窗口标题
  381. setWindowTitle(QFileInfo(filePath).fileName() +
  382. (currentIsPLC_ ? " - PLC编辑器" : " - HMI编辑器"));
  383. } else {
  384. QMessageBox::critical(this, "错误", "保存项目失败");
  385. }
  386. }
  387. void MainWindow::connection()
  388. {
  389. modbusManager->connectToDevice("COM1",QSerialPort::BaudRate(9600),QSerialPort::DataBits(8),QSerialPort::EvenParity,QSerialPort::StopBits(1));
  390. modbusManager->startSimulation(2000);
  391. }
  392. void MainWindow::disconnection()
  393. {
  394. modbusManager->disconnectDevice();
  395. modbusManager->stopSimulation();
  396. }
  397. void MainWindow::updateConnectionStatus(bool connection)
  398. {
  399. if (connection)
  400. {
  401. ui->textEdit->append("连接");
  402. }
  403. else
  404. {
  405. ui->textEdit->append("断开");
  406. }
  407. }
  408. void MainWindow::onListwidgetCurrenttextchanged(const QString &currentText)
  409. {
  410. selectedComponentType = currentText;
  411. }
  412. void MainWindow::onTabCloseRequested(int index)
  413. {
  414. if (currentIsPLC_) {
  415. if (plcScenes.size() > 1) {
  416. delete plcScenes.takeAt(index);
  417. delete plcViews.takeAt(index);
  418. ui->plc_tab_widget->removeTab(index);
  419. } else {
  420. QMessageBox::information(this, "提示", "至少保留一个PLC页面");
  421. }
  422. } else {
  423. if (hmiScenes.size() > 1) {
  424. delete hmiScenes.takeAt(index);
  425. delete hmiViews.takeAt(index);
  426. ui->hmi_tab_widget->removeTab(index);
  427. } else {
  428. QMessageBox::information(this, "提示", "至少保留一个HMI页面");
  429. }
  430. }
  431. }
  432. void MainWindow::btnInsertClicked()
  433. {
  434. // 1. 找场景中被选中的连线
  435. QList<QGraphicsItem*> selectedItems = plcScenes[currentPageIndex()]->selectedItems();
  436. Connection* selectedConn = nullptr;
  437. for (QGraphicsItem* item : selectedItems) {
  438. selectedConn = dynamic_cast<Connection*>(item);
  439. if (selectedConn) break;
  440. }
  441. if (!selectedConn) {
  442. QMessageBox::warning(this, "提示", "请先选中一条连线");
  443. return;
  444. }
  445. if (selectedComponentType.isEmpty()) {
  446. QMessageBox::warning(this, "提示", "请先在列表中选择要插入的组件");
  447. return;
  448. }
  449. // 2. 计算插入点(中点)
  450. QLineF lf = selectedConn->line();
  451. QPointF insertPos = (lf.p1() + lf.p2()) / 2;
  452. // 3. 删除原连线
  453. Item* from = selectedConn->from_;
  454. Item* to = selectedConn->to_;
  455. Item::AnchorType fromType = selectedConn->fromType_;
  456. Item::AnchorType toType = selectedConn->toType_;
  457. from->removeConnection(selectedConn);
  458. to->removeConnection(selectedConn);
  459. plcScenes[currentPageIndex()]->removeItem(selectedConn);
  460. delete selectedConn;
  461. // 4. 插入新元件
  462. Item* newItem = creatItem(selectedComponentType);
  463. newItem->setPos(insertPos);
  464. connect(newItem, &Item::requestCopy, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestCopy);
  465. connect(newItem, &Item::requestDelete, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestDelete);
  466. connect(newItem, &Item::requestBindRegister, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestBindRegister);
  467. connect(newItem, &Item::requestCompare, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestCompare);
  468. connect(newItem, &Item::requestReset, plcViews[currentPageIndex()], &MyGraphicsView::onItemRequestReset);
  469. plcScenes[currentPageIndex()]->addItem(newItem);
  470. // 5. 新建两条连线
  471. Connection* c1 = new Connection(from, fromType, newItem, Item::Left);
  472. plcScenes[currentPageIndex()]->addItem(c1);
  473. Connection* c2 = new Connection(newItem, Item::Right, to, toType);
  474. plcScenes[currentPageIndex()]->addItem(c2);
  475. }