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

450 regels
13 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. if (isSelected()) {
  50. QRectF rect = boundingRect();
  51. // 绘制右下角缩放控制点
  52. painter->setBrush(Qt::white);
  53. painter->setPen(QPen(Qt::black, 1));
  54. painter->drawRect(rect.right() - 8, rect.bottom() - 8, 8, 8);
  55. }
  56. }
  57. void HmiComponent::setAddress(int address)
  58. {
  59. if (m_address != address) {
  60. m_address = address;
  61. update();
  62. }
  63. }
  64. int HmiComponent::address() const
  65. {
  66. return m_address;
  67. }
  68. void HmiComponent::setSize(double width, double height)
  69. {
  70. prepareGeometryChange();
  71. m_width = width;
  72. m_height = height;
  73. update();
  74. }
  75. void HmiComponent::mousePressEvent(QGraphicsSceneMouseEvent *event)
  76. {
  77. // 检查是否点击了缩放控制点
  78. if (event->button() == Qt::LeftButton && isSelected()) {
  79. QRectF rect = boundingRect();
  80. QRectF handleRect(rect.right() - 8, rect.bottom() - 8, 8, 8);
  81. if (handleRect.contains(event->pos())) {
  82. m_resizing = true;
  83. m_resizeStartPos = event->scenePos();
  84. m_resizeStartSize = QSizeF(rect.width(), rect.height());
  85. event->accept();
  86. return;
  87. }
  88. }
  89. // 原有的选中处理
  90. if (event->button() == Qt::LeftButton) {
  91. emit selected(this);
  92. }
  93. QGraphicsObject::mousePressEvent(event);
  94. }
  95. void HmiComponent::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
  96. {
  97. scene()->clearSelection();
  98. setSelected(true);
  99. emit selected(this);
  100. QMenu menu;
  101. QAction *propertyAction = menu.addAction("属性");
  102. QAction *setOnAction = nullptr;
  103. QAction *setOffAction = nullptr;
  104. // 只有按钮才添加“置ON”和“置OFF”
  105. if (auto btn = dynamic_cast<HmiButton*>(this)) {
  106. setOnAction = menu.addAction("置ON");
  107. setOffAction = menu.addAction("置OFF");
  108. connect(setOnAction, &QAction::triggered, this, [btn]() {
  109. btn->setOn(true);
  110. });
  111. connect(setOffAction, &QAction::triggered, this, [btn]() {
  112. btn->setOn(false);
  113. });
  114. }
  115. menu.addSeparator();
  116. QAction *copyAction = menu.addAction("复制");
  117. QAction *deleteAction = menu.addAction("删除");
  118. connect(propertyAction, &QAction::triggered, this, [this]() {
  119. // 弹出属性对话框
  120. QDialog dialog;
  121. dialog.setWindowTitle("属性");
  122. QLabel* addressLabel = new QLabel("地址: ");
  123. QSpinBox* addressSpin = new QSpinBox();
  124. addressSpin->setRange(0, 4000);
  125. addressSpin->setValue(m_address); // 使用当前组件地址初始化
  126. // ON状态颜色设置
  127. QLabel* onColorLabel = new QLabel("ON状态外观: ");
  128. // 颜色显示,用QLabel设置背景色简单示范
  129. QLabel* onColorDisplay = new QLabel;
  130. onColorDisplay->setFixedSize(40, 20);
  131. onColorDisplay->setAutoFillBackground(true);
  132. QPalette onPal = onColorDisplay->palette();
  133. onPal.setColor(QPalette::Window, m_onColor);
  134. onColorDisplay->setPalette(onPal);
  135. onColorDisplay->setFrameShape(QFrame::Box);
  136. QPushButton* onColorBtn = new QPushButton("选择颜色");
  137. // OFF状态颜色设置
  138. QLabel* offColorLabel = new QLabel("OFF状态外观: ");
  139. QLabel* offColorDisplay = new QLabel;
  140. offColorDisplay->setFixedSize(40, 20);
  141. offColorDisplay->setAutoFillBackground(true);
  142. QPalette offPal = offColorDisplay->palette();
  143. offPal.setColor(QPalette::Window, m_offColor);
  144. offColorDisplay->setPalette(offPal);
  145. offColorDisplay->setFrameShape(QFrame::Box);
  146. QPushButton* offColorBtn = new QPushButton("选择颜色");
  147. QObject::connect(offColorBtn, &QPushButton::clicked, [&]() {
  148. QColor color = QColorDialog::getColor(m_offColor, nullptr, "选择 OFF 状态颜色");
  149. if (color.isValid()) {
  150. m_offColor = color;
  151. QPalette pal = offColorDisplay->palette();
  152. pal.setColor(QPalette::Window, color);
  153. offColorDisplay->setPalette(pal);
  154. offColorDisplay->update();
  155. setColor(color); // 默认显示OFF状态颜色
  156. }
  157. });
  158. // 地址布局
  159. QHBoxLayout* addressLayout = new QHBoxLayout();
  160. addressLayout->addWidget(addressLabel);
  161. addressLayout->addWidget(addressSpin);
  162. // ON颜色布局
  163. QHBoxLayout* onColorLayout = new QHBoxLayout();
  164. onColorLayout->addWidget(onColorLabel);
  165. onColorLayout->addWidget(onColorDisplay);
  166. onColorLayout->addWidget(onColorBtn);
  167. // OFF颜色布局
  168. QHBoxLayout* offColorLayout = new QHBoxLayout();
  169. offColorLayout->addWidget(offColorLabel);
  170. offColorLayout->addWidget(offColorDisplay);
  171. offColorLayout->addWidget(offColorBtn);
  172. // 确定/取消按钮
  173. QPushButton* okBtn = new QPushButton("确定");
  174. QPushButton* cancelBtn = new QPushButton("取消");
  175. QHBoxLayout* btnLayout = new QHBoxLayout();
  176. btnLayout->addStretch();
  177. btnLayout->addWidget(okBtn);
  178. btnLayout->addWidget(cancelBtn);
  179. // 颜色选择按钮点击槽
  180. QObject::connect(onColorBtn, &QPushButton::clicked, [&]() {
  181. QColor color = QColorDialog::getColor(m_onColor, nullptr, "选择 ON 状态颜色");
  182. if (color.isValid()) {
  183. m_onColor = color;
  184. QPalette pal = onColorDisplay->palette();
  185. pal.setColor(QPalette::Window, color);
  186. onColorDisplay->setPalette(pal);
  187. onColorDisplay->update();
  188. }
  189. });
  190. QVBoxLayout* mainLayout = new QVBoxLayout();
  191. mainLayout->addLayout(addressLayout);
  192. mainLayout->addLayout(onColorLayout);
  193. mainLayout->addLayout(offColorLayout);
  194. mainLayout->addLayout(btnLayout);
  195. dialog.setLayout(mainLayout);
  196. // 关闭逻辑
  197. QObject::connect(okBtn, &QPushButton::clicked, [&]() {
  198. // 保存地址
  199. int newAddress = addressSpin->value();
  200. setAddress(newAddress);
  201. // 已经在onColorBtn / offColorBtn中修改了m_onColor / m_offColor,需要触发界面更新
  202. update();
  203. dialog.accept();
  204. });
  205. QObject::connect(cancelBtn, &QPushButton::clicked, &dialog, &QDialog::reject);
  206. dialog.exec();
  207. });
  208. connect(copyAction, &QAction::triggered, this, [this]() {
  209. emit copyRequested(this);
  210. });
  211. connect(deleteAction, &QAction::triggered, this, [this]() {
  212. emit deleteRequested(this);
  213. });
  214. menu.exec(event->screenPos());
  215. }
  216. void HmiComponent::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  217. {
  218. if (m_resizing) {
  219. // 计算尺寸变化
  220. QPointF delta = event->scenePos() - m_resizeStartPos;
  221. double newWidth = qMax(20.0, m_resizeStartSize.width() + delta.x());
  222. double newHeight = qMax(20.0, m_resizeStartSize.height() + delta.y());
  223. // 更新组件大小
  224. prepareGeometryChange();
  225. // 子类需要实现setSize函数
  226. setSize(newWidth, newHeight);
  227. // 更新缩放控制点位置
  228. update();
  229. event->accept();
  230. return;
  231. }
  232. if (!(flags() & QGraphicsItem::ItemIsMovable)) {
  233. // 如果项不可移动,直接调用基类处理
  234. QGraphicsObject::mouseMoveEvent(event);
  235. return;
  236. }
  237. // 计算拖动后的位置
  238. QPointF delta = event->scenePos() - event->lastScenePos();
  239. QPointF newPos = pos() + delta;
  240. // 获取视图(假设只有1个视图)
  241. QList<QGraphicsView*> views = scene()->views();
  242. if (views.isEmpty()) {
  243. // 没有视图则默认处理
  244. QGraphicsObject::mouseMoveEvent(event);
  245. return;
  246. }
  247. QGraphicsView* view = views.first();
  248. // 获取视口矩形(像素坐标)
  249. QRect viewportRect = view->viewport()->rect();
  250. // 将视口矩形映射到场景坐标
  251. QPointF topLeftScene = view->mapToScene(viewportRect.topLeft());
  252. QPointF bottomRightScene = view->mapToScene(viewportRect.bottomRight());
  253. QRectF visibleSceneRect(topLeftScene, bottomRightScene);
  254. // 组件的边界矩形(局部坐标)
  255. QRectF bounds = boundingRect();
  256. // 计算移动后组件边界矩形(在场景坐标)
  257. QRectF newBounds = bounds.translated(newPos);
  258. // 限制组件边界必须完全在视图可见区域内
  259. double limitedX = newPos.x();
  260. double limitedY = newPos.y();
  261. if (newBounds.left() < visibleSceneRect.left())
  262. limitedX += visibleSceneRect.left() - newBounds.left();
  263. if (newBounds.top() < visibleSceneRect.top())
  264. limitedY += visibleSceneRect.top() - newBounds.top();
  265. if (newBounds.right() > visibleSceneRect.right())
  266. limitedX -= newBounds.right() - visibleSceneRect.right();
  267. if (newBounds.bottom() > visibleSceneRect.bottom())
  268. limitedY -= newBounds.bottom() - visibleSceneRect.bottom();
  269. setPos(QPointF(limitedX, limitedY));
  270. event->accept();
  271. }
  272. void HmiComponent::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
  273. {
  274. if (m_resizing && event->button() == Qt::LeftButton) {
  275. m_resizing = false;
  276. event->accept();
  277. return;
  278. }
  279. QGraphicsObject::mouseReleaseEvent(event);
  280. }
  281. void HmiComponent::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
  282. {
  283. QRectF rect = boundingRect();
  284. QRectF handleRect(rect.right() - 8, rect.bottom() - 8, 8, 8);
  285. if (isSelected() && handleRect.contains(event->pos())) {
  286. setCursor(Qt::SizeFDiagCursor);
  287. m_resizeHandle = BottomRight;
  288. } else {
  289. setCursor(Qt::ArrowCursor);
  290. m_resizeHandle = None;
  291. }
  292. QGraphicsObject::hoverMoveEvent(event);
  293. }
  294. void HmiComponent::setOnColor(const QColor& color) {
  295. m_onColor = color;
  296. }
  297. void HmiComponent::setOffColor(const QColor& color) {
  298. m_offColor = color;
  299. setColor(color); // 默认展示 OFF 状态颜色
  300. }
  301. QColor HmiComponent::onColor() const {
  302. return m_onColor;
  303. }
  304. QColor HmiComponent::offColor() const {
  305. return m_offColor;
  306. }
  307. HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent), m_isOn(false)
  308. {
  309. // 按钮保持矩形形状
  310. m_width = 65.0;
  311. m_height = 30.0;
  312. m_color = Qt::gray;
  313. m_offColor = m_color; // OFF状态颜色设为默认颜色
  314. m_onColor = Qt::green; // ON状态颜色为绿色
  315. m_componentName = "Button";
  316. setColor(m_offColor); // 当前显示OFF颜色
  317. }
  318. void HmiButton::setSize(double width, double height)
  319. {
  320. HmiComponent::setSize(width, height);
  321. }
  322. bool HmiButton::isOn() const
  323. {
  324. return m_isOn;
  325. }
  326. void HmiButton::setOn(bool on)
  327. {
  328. if (m_isOn != on) {
  329. m_isOn = on;
  330. if (m_isOn)
  331. setColor(m_onColor);
  332. else
  333. setColor(m_offColor);
  334. update();
  335. }
  336. }
  337. QRectF HmiButton::boundingRect() const
  338. {
  339. return QRectF(0, 0, m_width, m_height);
  340. }
  341. void HmiButton::paintShape(QPainter *painter)
  342. {
  343. painter->setPen(Qt::NoPen);
  344. painter->setBrush(m_color);
  345. painter->drawRect(boundingRect());
  346. }
  347. HmiIndicator::HmiIndicator(QGraphicsItem *parent) : HmiComponent(parent)
  348. {
  349. // 指示灯初始为圆形
  350. m_width = 30.0;
  351. m_height = 30.0;
  352. m_color = Qt::red;
  353. m_offColor = m_color; // OFF状态颜色设为默认颜色
  354. m_onColor = Qt::green; // ON状态颜色为绿色
  355. m_componentName = "Indicator";
  356. setColor(m_offColor); // 当前显示OFF颜色
  357. }
  358. void HmiIndicator::setSize(double width, double height)
  359. {
  360. HmiComponent::setSize(width, height);
  361. }
  362. QRectF HmiIndicator::boundingRect() const
  363. {
  364. return QRectF(0, 0, m_width, m_height);
  365. }
  366. void HmiIndicator::paintShape(QPainter *painter)
  367. {
  368. painter->setPen(Qt::NoPen);
  369. painter->setBrush(m_color);
  370. painter->drawEllipse(boundingRect());
  371. }