Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

409 Zeilen
13 KiB

  1. #include "document.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. HMIDocument::HMIDocument(QWidget *parent)
  20. : BaseDocument(HMI, parent),
  21. m_title("未命名HMI"),
  22. m_canDrawEllipse(false),
  23. m_canDrawRectangle(false),
  24. m_lastPastePos(0, 0)
  25. {
  26. // 创建绘图场景
  27. m_scene = new QGraphicsScene(this);
  28. m_scene->setSceneRect(0, 0, 800, 600);
  29. m_scene->setBackgroundBrush(QBrush(Qt::lightGray));
  30. // 创建视图
  31. m_view = new QGraphicsView(m_scene, this);
  32. m_view->setRenderHint(QPainter::Antialiasing);
  33. m_view->setDragMode(QGraphicsView::RubberBandDrag);
  34. m_view->setAcceptDrops(true);
  35. m_view->viewport()->installEventFilter(this);
  36. // 布局(让视图占满文档区域)
  37. auto layout = new QVBoxLayout(this);
  38. layout->setContentsMargins(0, 0, 0, 0);
  39. layout->addWidget(m_view);
  40. setLayout(layout);
  41. }
  42. HMIDocument::~HMIDocument()
  43. {
  44. }
  45. // 事件过滤器(处理绘图、拖拽等事件)
  46. bool HMIDocument::eventFilter(QObject *obj, QEvent *event)
  47. {
  48. if (obj != m_view->viewport()) {
  49. return QWidget::eventFilter(obj, event);
  50. }
  51. // 鼠标按下事件(绘制图形)
  52. if (event->type() == QEvent::MouseButtonPress)
  53. {
  54. QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
  55. if (mouseEvent->button() == Qt::RightButton)
  56. {
  57. // 右键菜单
  58. showContextMenu(mouseEvent->globalPos());
  59. return true;
  60. }
  61. if (mouseEvent->button() == Qt::LeftButton)
  62. {
  63. QPointF scenePos = m_view->mapToScene(mouseEvent->pos());
  64. QGraphicsItem *item = m_scene->itemAt(scenePos, m_view->transform());
  65. // 如果点击空白区域且有绘制标志,创建图形
  66. if (!item) {
  67. if (m_canDrawEllipse) {
  68. createEllipse(scenePos);
  69. resetDrawFlags();
  70. } else if (m_canDrawRectangle) {
  71. createRectangle(scenePos);
  72. resetDrawFlags();
  73. }
  74. m_scene->clearSelection(); // 清除选择
  75. return true;
  76. }
  77. }
  78. }
  79. // 拖拽事件(从工具栏拖拽绘制)
  80. if (event->type() == QEvent::DragEnter) {
  81. QDragEnterEvent *dragEvent = static_cast<QDragEnterEvent*>(event);
  82. dragEvent->acceptProposedAction();
  83. return true;
  84. }
  85. if (event->type() == QEvent::DragMove) {
  86. static_cast<QDragMoveEvent*>(event)->acceptProposedAction();
  87. return true;
  88. }
  89. if (event->type() == QEvent::Drop) {
  90. QDropEvent *dropEvent = static_cast<QDropEvent*>(event);
  91. const QMimeData *mimeData = dropEvent->mimeData(); // 显式获取mimeData
  92. QPointF scenePos = m_view->mapToScene(dropEvent->pos());
  93. // 使用QMimeData
  94. if (mimeData && mimeData->hasText()) {
  95. QString type = mimeData->text();
  96. if (type == "指示灯") {
  97. createEllipse(scenePos);
  98. } else if (type == "按钮") {
  99. createRectangle(scenePos);
  100. }
  101. }
  102. dropEvent->acceptProposedAction();
  103. return true;
  104. }
  105. return QWidget::eventFilter(obj, event);
  106. }
  107. // 创建椭圆(指示灯)
  108. void HMIDocument::createEllipse(const QPointF &pos)
  109. {
  110. ResizableEllipse *ellipse = new ResizableEllipse(pos.x(), pos.y(), 50, 50);
  111. ellipse->setBrush(QBrush(Qt::red));
  112. ellipse->setPen(QPen(Qt::black, 1));
  113. m_scene->addItem(ellipse);
  114. ellipse->setSelected(true);
  115. }
  116. // 创建矩形(按钮)
  117. void HMIDocument::createRectangle(const QPointF &pos)
  118. {
  119. ResizableRectangle *rect = new ResizableRectangle(pos.x(), pos.y(), 100, 50);
  120. rect->setBrush(QBrush(Qt::yellow));
  121. rect->setPen(QPen(Qt::black, 1));
  122. m_scene->addItem(rect);
  123. rect->setSelected(true);
  124. }
  125. // 重置绘制标志
  126. void HMIDocument::resetDrawFlags()
  127. {
  128. m_canDrawEllipse = false;
  129. m_canDrawRectangle = false;
  130. }
  131. // 右键菜单
  132. void HMIDocument::showContextMenu(QPoint globalPos)
  133. {
  134. QPoint viewportPos = m_view->mapFromGlobal(globalPos);
  135. QPointF scenePos = m_view->mapToScene(viewportPos);
  136. QGraphicsItem *clickedItem = m_scene->itemAt(scenePos, m_view->transform());
  137. QMenu menu(this);
  138. if (!clickedItem) {
  139. // 空白区域:仅显示粘贴
  140. QAction *pasteAction = menu.addAction("粘贴");
  141. pasteAction->setEnabled(!m_copiedItemsData.isEmpty());
  142. connect(pasteAction, &QAction::triggered, this, &HMIDocument::pasteItems);
  143. } else {
  144. // 选中图形:显示完整菜单
  145. QAction *propAction = menu.addAction("属性");
  146. QAction *copyAction = menu.addAction("复制");
  147. QAction *pasteAction = menu.addAction("粘贴");
  148. QAction *deleteAction = menu.addAction("删除");
  149. QAction *onAction = menu.addAction("置为ON");
  150. QAction *offAction = menu.addAction("置为OFF");
  151. pasteAction->setEnabled(!m_copiedItemsData.isEmpty());
  152. connect(propAction, &QAction::triggered, this, &HMIDocument::showItemProperties);
  153. connect(copyAction, &QAction::triggered, this, &HMIDocument::copySelectedItems);
  154. connect(pasteAction, &QAction::triggered, this, &HMIDocument::pasteItems);
  155. connect(deleteAction, &QAction::triggered, this, &HMIDocument::deleteSelectedItems);
  156. // ON/OFF动作(针对指示灯和按钮)
  157. connect(onAction, &QAction::triggered, [=]() {
  158. if (auto ellipse = dynamic_cast<ResizableEllipse*>(clickedItem)) {
  159. ellipse->setBrush(ellipse->onColor());
  160. } else if (auto rect = dynamic_cast<ResizableRectangle*>(clickedItem)) {
  161. rect->setBrush(rect->pressedColor());
  162. }
  163. });
  164. connect(offAction, &QAction::triggered, [=]() {
  165. if (auto ellipse = dynamic_cast<ResizableEllipse*>(clickedItem)) {
  166. ellipse->setBrush(ellipse->offColor());
  167. } else if (auto rect = dynamic_cast<ResizableRectangle*>(clickedItem)) {
  168. rect->setBrush(rect->releasedColor());
  169. }
  170. });
  171. }
  172. menu.exec(globalPos + QPoint(10, 10));
  173. }
  174. // 复制选中项
  175. void HMIDocument::copySelectedItems()
  176. {
  177. m_copiedItemsData.clear();
  178. QList<QGraphicsItem*> selectedItems = m_scene->selectedItems();
  179. if (selectedItems.isEmpty()) return;
  180. foreach (QGraphicsItem *item, selectedItems) {
  181. m_copiedItemsData.append(serializeItem(item));
  182. }
  183. m_lastPastePos = selectedItems.first()->pos();
  184. }
  185. // 粘贴项
  186. void HMIDocument::pasteItems()
  187. {
  188. if (m_copiedItemsData.isEmpty()) return;
  189. QPointF offset(20, 20);
  190. m_lastPastePos += offset;
  191. m_scene->clearSelection();
  192. foreach (const QByteArray &itemData, m_copiedItemsData) {
  193. QGraphicsItem *newItem = deserializeItem(itemData);
  194. if (newItem) {
  195. m_scene->addItem(newItem);
  196. newItem->setPos(m_lastPastePos);
  197. newItem->setSelected(true);
  198. }
  199. }
  200. }
  201. // 删除选中项
  202. void HMIDocument::deleteSelectedItems()
  203. {
  204. QList<QGraphicsItem*> selectedItems = m_scene->selectedItems();
  205. foreach (QGraphicsItem *item, selectedItems) {
  206. m_scene->removeItem(item);
  207. delete item;
  208. }
  209. }
  210. // 显示属性对话框
  211. void HMIDocument::showItemProperties()
  212. {
  213. QList<QGraphicsItem*> selectedItems = m_scene->selectedItems();
  214. if (selectedItems.isEmpty()) return;
  215. QGraphicsItem *item = selectedItems.first();
  216. // 将QGraphicsItem转换为NamedItem
  217. NamedItem *namedItem = dynamic_cast<NamedItem*>(item);
  218. if (!namedItem) return;
  219. QDialog dialog(this);
  220. dialog.setWindowTitle("属性设置");
  221. QFormLayout *form = new QFormLayout(&dialog);
  222. // 名称输入 - 使用NamedItem接口
  223. QLineEdit *nameEdit = new QLineEdit(namedItem->name());
  224. // 颜色选择(根据图形类型)
  225. QColor tempColor1, tempColor2;
  226. QPushButton *colorBtn1 = new QPushButton;
  227. QPushButton *colorBtn2 = new QPushButton;
  228. if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) {
  229. tempColor1 = ellipse->onColor();
  230. tempColor2 = ellipse->offColor();
  231. form->addRow("ON颜色:", colorBtn1);
  232. form->addRow("OFF颜色:", colorBtn2);
  233. } else if (auto rect = dynamic_cast<ResizableRectangle*>(item)) {
  234. tempColor1 = rect->pressedColor();
  235. tempColor2 = rect->releasedColor();
  236. form->addRow("按下颜色:", colorBtn1);
  237. form->addRow("释放颜色:", colorBtn2);
  238. }
  239. // 初始化颜色按钮
  240. colorBtn1->setStyleSheet("background-color:" + tempColor1.name());
  241. colorBtn2->setStyleSheet("background-color:" + tempColor2.name());
  242. // 颜色选择对话框
  243. connect(colorBtn1, &QPushButton::clicked, [&]() {
  244. QColor c = QColorDialog::getColor(tempColor1, &dialog);
  245. if (c.isValid()) {
  246. tempColor1 = c;
  247. colorBtn1->setStyleSheet("background-color:" + c.name());
  248. }
  249. });
  250. connect(colorBtn2, &QPushButton::clicked, [&]() {
  251. QColor c = QColorDialog::getColor(tempColor2, &dialog);
  252. if (c.isValid()) {
  253. tempColor2 = c;
  254. colorBtn2->setStyleSheet("background-color:" + c.name());
  255. }
  256. });
  257. // 布局组装
  258. form->addRow("名称:", nameEdit);
  259. QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
  260. form->addRow(btnBox);
  261. connect(btnBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
  262. connect(btnBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
  263. // 应用属性
  264. if (dialog.exec() == QDialog::Accepted) {
  265. // 使用NamedItem接口设置名称
  266. namedItem->setName(nameEdit->text());
  267. if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) {
  268. ellipse->setOnColor(tempColor1);
  269. ellipse->setOffColor(tempColor2);
  270. } else if (auto rect = dynamic_cast<ResizableRectangle*>(item)) {
  271. rect->setPressedColor(tempColor1);
  272. rect->setReleasedColor(tempColor2);
  273. }
  274. item->update();
  275. }
  276. }
  277. // 序列化图形项(用于复制粘贴)
  278. QByteArray HMIDocument::serializeItem(QGraphicsItem *item)
  279. {
  280. QByteArray data;
  281. QDataStream stream(&data, QIODevice::WriteOnly);
  282. // 基础信息
  283. stream << item->type();
  284. stream << item->pos();
  285. stream << item->flags();
  286. stream << item->transform();
  287. stream << item->isVisible();
  288. // 形状信息
  289. if (auto shape = dynamic_cast<QAbstractGraphicsShapeItem*>(item)) {
  290. stream << shape->pen();
  291. stream << shape->brush();
  292. stream << shape->boundingRect();
  293. }
  294. // 具体图形信息
  295. if (auto rect = dynamic_cast<ResizableRectangle*>(item)) {
  296. stream << rect->rect();
  297. stream << rect->pressedColor();
  298. stream << rect->releasedColor();
  299. stream << rect->name();
  300. } else if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) {
  301. stream << ellipse->rect();
  302. stream << ellipse->onColor();
  303. stream << ellipse->offColor();
  304. stream << ellipse->name();
  305. }
  306. return data;
  307. }
  308. // 反序列化图形项(用于粘贴)
  309. QGraphicsItem *HMIDocument::deserializeItem(const QByteArray &data)
  310. {
  311. QDataStream stream(data);
  312. int type;
  313. QPointF pos;
  314. QGraphicsItem::GraphicsItemFlags flags;
  315. QTransform transform;
  316. bool visible;
  317. stream >> type >> pos >> flags >> transform >> visible;
  318. QGraphicsItem *item = nullptr;
  319. if (type == QGraphicsRectItem::Type) {
  320. QPen pen;
  321. QBrush brush;
  322. QRectF boundRect;
  323. QRectF rect;
  324. QColor pressedColor, releasedColor;
  325. QString name;
  326. stream >> pen >> brush >> boundRect >> rect >> pressedColor >> releasedColor >> name;
  327. ResizableRectangle *rectItem = new ResizableRectangle(0, 0, rect.width(), rect.height());
  328. rectItem->setPen(pen);
  329. rectItem->setBrush(brush);
  330. rectItem->setPressedColor(pressedColor);
  331. rectItem->setReleasedColor(releasedColor);
  332. rectItem->setName(name);
  333. item = rectItem;
  334. } else if (type == QGraphicsEllipseItem::Type) {
  335. QPen pen;
  336. QBrush brush;
  337. QRectF boundRect;
  338. QRectF rect;
  339. QColor onColor, offColor;
  340. QString name;
  341. stream >> pen >> brush >> boundRect >> rect >> onColor >> offColor >> name;
  342. ResizableEllipse *ellipseItem = new ResizableEllipse(0, 0, rect.width(), rect.height());
  343. ellipseItem->setPen(pen);
  344. ellipseItem->setBrush(brush);
  345. ellipseItem->setOnColor(onColor);
  346. ellipseItem->setOffColor(offColor);
  347. ellipseItem->setName(name);
  348. item = ellipseItem;
  349. }
  350. if (item) {
  351. item->setFlags(flags);
  352. item->setTransform(transform);
  353. item->setVisible(visible);
  354. }
  355. return item;
  356. }