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

322 lines
9.7 KiB

  1. #include "customgraphics.h"
  2. #include <QPainter>
  3. #include <QGraphicsSceneMouseEvent>
  4. #include <QMenu>
  5. #include <QGraphicsScene>
  6. #include <QColorDialog>
  7. #include <QInputDialog>
  8. #include <QDialog>
  9. #include <QLabel>
  10. #include <QSpinBox>
  11. #include <QHBoxLayout>
  12. #include <QVBoxLayout>
  13. #include <QPushButton>
  14. #include <QGraphicsView>
  15. // HmiComponent 基类实现
  16. HmiComponent::HmiComponent(QGraphicsItem *parent) : QGraphicsObject(parent)
  17. {
  18. setFlag(QGraphicsItem::ItemIsMovable, true);
  19. setFlag(QGraphicsItem::ItemIsSelectable, true);
  20. setAcceptHoverEvents(true);
  21. }
  22. void HmiComponent::setColor(const QColor& color) {
  23. if (m_color != color) {
  24. m_color = color;
  25. update();
  26. }
  27. }
  28. QColor HmiComponent::color() const {
  29. return m_color;
  30. }
  31. void HmiComponent::setComponentName(const QString& name) {
  32. m_componentName = name;
  33. }
  34. QString HmiComponent::componentName() const {
  35. return m_componentName;
  36. }
  37. void HmiComponent::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
  38. {
  39. Q_UNUSED(option);
  40. Q_UNUSED(widget);
  41. painter->setRenderHint(QPainter::Antialiasing);
  42. paintShape(painter);
  43. if (isSelected()) {
  44. painter->setBrush(Qt::NoBrush);
  45. painter->setPen(QPen(Qt::darkRed, 2, Qt::DashLine));
  46. painter->drawRect(boundingRect());
  47. }
  48. }
  49. void HmiComponent::setAddress(int address)
  50. {
  51. if (m_address != address) {
  52. m_address = address;
  53. update();
  54. }
  55. }
  56. int HmiComponent::address() const
  57. {
  58. return m_address;
  59. }
  60. void HmiComponent::mousePressEvent(QGraphicsSceneMouseEvent *event)
  61. {
  62. // 右键不触发选中信号,留给 contextMenuEvent 处理
  63. if (event->button() == Qt::LeftButton) {
  64. emit selected(this);
  65. }
  66. QGraphicsObject::mousePressEvent(event);
  67. }
  68. void HmiComponent::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
  69. {
  70. scene()->clearSelection();
  71. setSelected(true);
  72. emit selected(this);
  73. QMenu menu;
  74. QAction *propertyAction = menu.addAction("属性");
  75. QAction *copyAction = menu.addAction("复制");
  76. QAction *deleteAction = menu.addAction("删除");
  77. connect(propertyAction, &QAction::triggered, this, [this]() {
  78. // 弹出属性对话框
  79. QDialog dialog;
  80. dialog.setWindowTitle("属性");
  81. QLabel* addressLabel = new QLabel("地址: ");
  82. QSpinBox* addressSpin = new QSpinBox();
  83. addressSpin->setRange(0, 4000);
  84. addressSpin->setValue(m_address); // 使用当前组件地址初始化
  85. // ON状态颜色设置
  86. QLabel* onColorLabel = new QLabel("ON状态外观: ");
  87. // 颜色显示,用QLabel设置背景色简单示范
  88. QLabel* onColorDisplay = new QLabel;
  89. onColorDisplay->setFixedSize(40, 20);
  90. onColorDisplay->setAutoFillBackground(true);
  91. QPalette onPal = onColorDisplay->palette();
  92. onPal.setColor(QPalette::Window, m_onColor);
  93. onColorDisplay->setPalette(onPal);
  94. onColorDisplay->setFrameShape(QFrame::Box);
  95. QPushButton* onColorBtn = new QPushButton("选择颜色");
  96. // OFF状态颜色设置
  97. QLabel* offColorLabel = new QLabel("OFF状态外观: ");
  98. QLabel* offColorDisplay = new QLabel;
  99. offColorDisplay->setFixedSize(40, 20);
  100. offColorDisplay->setAutoFillBackground(true);
  101. QPalette offPal = offColorDisplay->palette();
  102. offPal.setColor(QPalette::Window, m_offColor);
  103. offColorDisplay->setPalette(offPal);
  104. offColorDisplay->setFrameShape(QFrame::Box);
  105. QPushButton* offColorBtn = new QPushButton("选择颜色");
  106. QObject::connect(offColorBtn, &QPushButton::clicked, [&]() {
  107. QColor color = QColorDialog::getColor(m_offColor, nullptr, "选择 OFF 状态颜色");
  108. if (color.isValid()) {
  109. m_offColor = color;
  110. QPalette pal = offColorDisplay->palette();
  111. pal.setColor(QPalette::Window, color);
  112. offColorDisplay->setPalette(pal);
  113. offColorDisplay->update();
  114. setColor(color); // 默认显示OFF状态颜色
  115. }
  116. });
  117. // 地址布局
  118. QHBoxLayout* addressLayout = new QHBoxLayout();
  119. addressLayout->addWidget(addressLabel);
  120. addressLayout->addWidget(addressSpin);
  121. // ON颜色布局
  122. QHBoxLayout* onColorLayout = new QHBoxLayout();
  123. onColorLayout->addWidget(onColorLabel);
  124. onColorLayout->addWidget(onColorDisplay);
  125. onColorLayout->addWidget(onColorBtn);
  126. // OFF颜色布局
  127. QHBoxLayout* offColorLayout = new QHBoxLayout();
  128. offColorLayout->addWidget(offColorLabel);
  129. offColorLayout->addWidget(offColorDisplay);
  130. offColorLayout->addWidget(offColorBtn);
  131. // 确定/取消按钮
  132. QPushButton* okBtn = new QPushButton("确定");
  133. QPushButton* cancelBtn = new QPushButton("取消");
  134. QHBoxLayout* btnLayout = new QHBoxLayout();
  135. btnLayout->addStretch();
  136. btnLayout->addWidget(okBtn);
  137. btnLayout->addWidget(cancelBtn);
  138. // 颜色选择按钮点击槽
  139. QObject::connect(onColorBtn, &QPushButton::clicked, [&]() {
  140. QColor color = QColorDialog::getColor(m_onColor, nullptr, "选择 ON 状态颜色");
  141. if (color.isValid()) {
  142. m_onColor = color;
  143. QPalette pal = onColorDisplay->palette();
  144. pal.setColor(QPalette::Window, color);
  145. onColorDisplay->setPalette(pal);
  146. onColorDisplay->update();
  147. }
  148. });
  149. QVBoxLayout* mainLayout = new QVBoxLayout();
  150. mainLayout->addLayout(addressLayout);
  151. mainLayout->addLayout(onColorLayout);
  152. mainLayout->addLayout(offColorLayout);
  153. mainLayout->addLayout(btnLayout);
  154. dialog.setLayout(mainLayout);
  155. // 关闭逻辑
  156. QObject::connect(okBtn, &QPushButton::clicked, [&]() {
  157. // 保存地址
  158. int newAddress = addressSpin->value();
  159. setAddress(newAddress);
  160. // 已经在onColorBtn / offColorBtn中修改了m_onColor / m_offColor,需要触发界面更新
  161. update();
  162. dialog.accept();
  163. });
  164. QObject::connect(cancelBtn, &QPushButton::clicked, &dialog, &QDialog::reject);
  165. dialog.exec();
  166. });
  167. connect(copyAction, &QAction::triggered, this, [this]() {
  168. emit copyRequested(this);
  169. });
  170. connect(deleteAction, &QAction::triggered, this, [this]() {
  171. emit deleteRequested(this);
  172. });
  173. menu.exec(event->screenPos());
  174. }
  175. void HmiComponent::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  176. {
  177. if (!(flags() & QGraphicsItem::ItemIsMovable)) {
  178. // 如果项不可移动,直接调用基类处理
  179. QGraphicsObject::mouseMoveEvent(event);
  180. return;
  181. }
  182. // 计算拖动后的位置
  183. QPointF delta = event->scenePos() - event->lastScenePos();
  184. QPointF newPos = pos() + delta;
  185. // 获取视图(假设只有1个视图)
  186. QList<QGraphicsView*> views = scene()->views();
  187. if (views.isEmpty()) {
  188. // 没有视图则默认处理
  189. QGraphicsObject::mouseMoveEvent(event);
  190. return;
  191. }
  192. QGraphicsView* view = views.first();
  193. // 获取视口矩形(像素坐标)
  194. QRect viewportRect = view->viewport()->rect();
  195. // 将视口矩形映射到场景坐标
  196. QPointF topLeftScene = view->mapToScene(viewportRect.topLeft());
  197. QPointF bottomRightScene = view->mapToScene(viewportRect.bottomRight());
  198. QRectF visibleSceneRect(topLeftScene, bottomRightScene);
  199. // 组件的边界矩形(局部坐标)
  200. QRectF bounds = boundingRect();
  201. // 计算移动后组件边界矩形(在场景坐标)
  202. QRectF newBounds = bounds.translated(newPos);
  203. // 限制组件边界必须完全在视图可见区域内
  204. qreal limitedX = newPos.x();
  205. qreal limitedY = newPos.y();
  206. if (newBounds.left() < visibleSceneRect.left())
  207. limitedX += visibleSceneRect.left() - newBounds.left();
  208. if (newBounds.top() < visibleSceneRect.top())
  209. limitedY += visibleSceneRect.top() - newBounds.top();
  210. if (newBounds.right() > visibleSceneRect.right())
  211. limitedX -= newBounds.right() - visibleSceneRect.right();
  212. if (newBounds.bottom() > visibleSceneRect.bottom())
  213. limitedY -= newBounds.bottom() - visibleSceneRect.bottom();
  214. setPos(QPointF(limitedX, limitedY));
  215. event->accept();
  216. }
  217. void HmiComponent::setOnColor(const QColor& color) {
  218. m_onColor = color;
  219. }
  220. void HmiComponent::setOffColor(const QColor& color) {
  221. m_offColor = color;
  222. setColor(color); // 默认展示 OFF 状态颜色
  223. }
  224. QColor HmiComponent::onColor() const {
  225. return m_onColor;
  226. }
  227. QColor HmiComponent::offColor() const {
  228. return m_offColor;
  229. }
  230. HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent)
  231. {
  232. m_color = Qt::gray;
  233. m_offColor = m_color; // OFF状态颜色设为默认颜色
  234. m_onColor = Qt::green; // ON状态颜色为绿色
  235. m_componentName = "Button";
  236. setColor(m_offColor); // 当前显示OFF颜色
  237. }
  238. QRectF HmiButton::boundingRect() const
  239. {
  240. return QRectF(0, 0, 65, 30);
  241. }
  242. void HmiButton::paintShape(QPainter *painter)
  243. {
  244. painter->setPen(Qt::NoPen);
  245. painter->setBrush(m_color);
  246. painter->drawRect(boundingRect());
  247. }
  248. HmiIndicator::HmiIndicator(QGraphicsItem *parent) : HmiComponent(parent)
  249. {
  250. m_color = Qt::red;
  251. m_offColor = m_color; // OFF状态颜色设为默认颜色
  252. m_onColor = Qt::green; // ON状态颜色为绿色
  253. m_componentName = "Indicator";
  254. setColor(m_offColor); // 当前显示OFF颜色
  255. }
  256. QRectF HmiIndicator::boundingRect() const
  257. {
  258. return QRectF(0, 0, 30, 30);
  259. }
  260. void HmiIndicator::paintShape(QPainter *painter)
  261. {
  262. painter->setPen(Qt::NoPen);
  263. painter->setBrush(m_color);
  264. painter->drawEllipse(boundingRect());
  265. }