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.

248 righe
8.8 KiB

  1. #include "hmicontrolitem.h"
  2. #include <QPainter>
  3. #include <QGraphicsScene>
  4. #include <QCursor>
  5. #include <QMenu>
  6. #include <QAction>
  7. #include <QGraphicsView>
  8. #include <QColorDialog>
  9. #include <QDebug>
  10. #include <QInputDialog>
  11. #include "hmibutton.h"
  12. #include "hmiled.h"
  13. #include <QMessageBox>
  14. // 静态剪贴板数据,初始化为空
  15. QVariantMap HmiControlItem::m_clipboardData;
  16. // 构造函数:初始化控件基本属性
  17. HmiControlItem::HmiControlItem(QGraphicsItem* parent)
  18. : QObject(nullptr), QGraphicsItem(parent), m_registerAddress()
  19. {
  20. // 设置控件属性:可移动、可选择、发送几何变化通知
  21. setFlags(QGraphicsItem::ItemIsMovable |
  22. QGraphicsItem::ItemIsSelectable |
  23. QGraphicsItem::ItemSendsGeometryChanges);
  24. // 启用鼠标悬停事件
  25. setAcceptHoverEvents(true);
  26. // 设置变换原点为控件左上角
  27. setTransformOriginPoint(0, 0);
  28. }
  29. // 检查鼠标是否在右下角缩放区域
  30. // 返回:true(在缩放区域内),false(不在)
  31. bool HmiControlItem::isInScaleArea(const QPointF& pos) {
  32. QRectF contentRect = boundingRect().adjusted(1, 1, -1, -1);
  33. return (pos.x() >= contentRect.width() - m_scaleAreaSize &&
  34. pos.x() <= contentRect.width()) &&
  35. (pos.y() >= contentRect.height() - m_scaleAreaSize &&
  36. pos.y() <= contentRect.height());
  37. }
  38. // 鼠标悬停事件处理:根据鼠标位置设置光标样式
  39. void HmiControlItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event) {
  40. if (isSelected()) {
  41. // 如果控件被选中
  42. if (isInScaleArea(event->pos())) {
  43. // 鼠标在右下角缩放区域,显示缩放光标
  44. setCursor(Qt::SizeFDiagCursor);
  45. } else {
  46. // 鼠标在控件其他区域,显示移动光标
  47. setCursor(Qt::SizeAllCursor);
  48. }
  49. } else {
  50. // 控件未选中,显示默认光标
  51. setCursor(Qt::ArrowCursor);
  52. }
  53. QGraphicsItem::hoverMoveEvent(event);
  54. }
  55. // 鼠标按下事件处理:开始拖动或缩放
  56. void HmiControlItem::mousePressEvent(QGraphicsSceneMouseEvent* event) {
  57. if (isSelected() && isInScaleArea(event->pos())) {
  58. // 选中控件且点击缩放区域,进入缩放模式
  59. m_isScaling = true;
  60. m_originalRect = boundingRect().adjusted(1, 1, -1, -1);
  61. m_scaleStartPos = event->pos();
  62. event->accept();
  63. return;
  64. }
  65. // 记录拖动开始位置
  66. m_dragStartPos = event->pos();
  67. QGraphicsItem::mousePressEvent(event);
  68. }
  69. // 鼠标移动事件处理:处理控件拖动或缩放
  70. void HmiControlItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
  71. if (m_isScaling) {
  72. // 缩放模式:根据鼠标移动调整控件大小
  73. qreal deltaX = event->pos().x() - m_scaleStartPos.x();
  74. QRectF currentRect = transform().mapRect(m_originalRect);
  75. qreal newWidth = currentRect.width() + deltaX;
  76. newWidth = qMax(m_minWidth, qMin(newWidth, m_maxWidth));
  77. qreal aspectRatio = getAspectRatio();
  78. qreal newHeight = newWidth / aspectRatio;
  79. newHeight = qMax(m_minHeight, qMin(newHeight, m_maxHeight));
  80. newWidth = newHeight * aspectRatio;
  81. if (qAbs(newWidth - currentRect.width()) > 1 ||
  82. qAbs(newHeight - currentRect.height()) > 1) {
  83. // 更新控件大小,应用缩放变换
  84. prepareGeometryChange();
  85. qreal scaleX = newWidth / m_originalRect.width();
  86. qreal scaleY = newHeight / m_originalRect.height();
  87. resetTransform();
  88. setTransform(QTransform::fromScale(scaleX, scaleY), false);
  89. update();
  90. }
  91. event->accept();
  92. return;
  93. }
  94. if (isSelected()) {
  95. // 使用scenePos计算偏移量,更准确
  96. QPointF delta = event->scenePos() - event->lastScenePos();
  97. QPointF newPos = pos() + delta;
  98. // 获取变换后的边界矩形
  99. QRectF itemRect = mapToScene(boundingRect()).boundingRect();
  100. QRectF sceneRect = scene()->sceneRect();
  101. // 限制控件在场景边界内
  102. newPos.setX(qMax(sceneRect.left(), qMin(newPos.x(), sceneRect.right() - itemRect.width())));
  103. newPos.setY(qMax(sceneRect.top(), qMin(newPos.y(), sceneRect.bottom() - itemRect.height())));
  104. setPos(newPos);
  105. update();
  106. event->accept();
  107. return;
  108. }
  109. QGraphicsItem::mouseMoveEvent(event);
  110. }
  111. // 鼠标释放事件处理:结束缩放或拖动
  112. void HmiControlItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
  113. m_isScaling = false;
  114. QGraphicsItem::mouseReleaseEvent(event);
  115. }
  116. // 右键菜单事件处理:显示控件特定的上下文菜单
  117. void HmiControlItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
  118. QMenu menu;
  119. createContextMenu(menu); // 子类实现具体菜单项
  120. menu.exec(event->screenPos());
  121. event->accept();
  122. }
  123. // 删除控件:从场景中移除并释放内存
  124. void HmiControlItem::deleteItem() {
  125. if (scene()) {
  126. scene()->removeItem(this);
  127. }
  128. delete this;
  129. }
  130. // 更改控件颜色:弹出颜色对话框选择ON或OFF状态颜色
  131. void HmiControlItem::changeAppearanceColor(bool isOnState, QColor& onColor, QColor& offColor) {
  132. QColor newColor = QColorDialog::getColor(
  133. Qt::white,
  134. nullptr,
  135. isOnState ? "选择ON状态颜色" : "选择OFF状态颜色"
  136. );
  137. if (newColor.isValid()) {
  138. if (isOnState) {
  139. onColor = newColor;
  140. } else {
  141. offColor = newColor;
  142. }
  143. update();
  144. }
  145. }
  146. // 设置寄存器绑定
  147. bool HmiControlItem::setRegisterBinding(int address) {
  148. if (address >= 0 && address <= 4000) {
  149. m_registerAddress = address;
  150. return true;
  151. } else {
  152. qDebug() << "Invalid register binding: address=" << address;
  153. return false;
  154. }
  155. }
  156. // 存储数据到剪贴板
  157. void HmiControlItem::copyToClipboard(const QVariantMap& data) {
  158. m_clipboardData = data;
  159. qDebug() << "Copied to clipboard:" << data;
  160. }
  161. // 获取剪贴板数据
  162. QVariantMap HmiControlItem::getClipboardData() {
  163. return m_clipboardData;
  164. }
  165. // 检查剪贴板是否有数据
  166. bool HmiControlItem::hasClipboardData() {
  167. return !m_clipboardData.isEmpty();
  168. }
  169. // 从剪贴板数据创建控件,指定粘贴位置
  170. HmiControlItem* HmiControlItem::createFromClipboard(const QVariantMap& data, const QPointF& pos, QGraphicsItem* parent) {
  171. QString type = data.value("type").toString();
  172. HmiControlItem* item = nullptr;
  173. if (type == "按钮") {
  174. // 创建按钮控件并恢复属性
  175. item = new HmiButton(parent);
  176. HmiButton* button = static_cast<HmiButton*>(item);
  177. button->setButtonState(data.value("isButtonOn", false).toBool());
  178. button->setOnColor(QColor(data.value("onColor").toString()));
  179. button->setOffColor(QColor(data.value("offColor").toString()));
  180. } else if (type == "指示灯") {
  181. // 创建指示灯控件并恢复属性
  182. item = new HmiLed(parent);
  183. HmiLed* led = static_cast<HmiLed*>(item);
  184. led->setOnColor(QColor(data.value("onColor").toString()));
  185. led->setOffColor(QColor(data.value("offColor").toString()));
  186. }
  187. if (item) {
  188. // 设置粘贴位置
  189. item->setPos(pos);
  190. // 恢复缩放
  191. qreal scaleX = data.value("scaleX", 1.0).toDouble();
  192. qreal scaleY = data.value("scaleY", 1.0).toDouble();
  193. item->setTransform(QTransform::fromScale(scaleX, scaleY));
  194. // 恢复寄存器绑定
  195. item->setRegisterBinding(data.value("registerAddress", -1).toInt());
  196. }
  197. return item;
  198. }
  199. // 创建右键菜单(默认实现,子类可扩展)
  200. void HmiControlItem::createContextMenu(QMenu& menu) {
  201. // 默认添加"设置寄存器"选项
  202. QAction* setRegisterAction = new QAction("设置寄存器", &menu);
  203. QObject::connect(setRegisterAction, &QAction::triggered, [this]() {
  204. bool ok;
  205. // 显示当前绑定的寄存器地址作为默认值
  206. int currentAddress = m_registerAddress >= 0 ? m_registerAddress : 0;
  207. int address = QInputDialog::getInt(nullptr,
  208. "输入寄存器地址",
  209. QString("地址 (0-4000):\n当前地址: %1").arg(currentAddress),
  210. currentAddress, 0, 4000, 1, &ok);
  211. if (ok) {
  212. if (!setRegisterBinding(address)) {
  213. QMessageBox::warning(nullptr, "错误", "无效的寄存器地址");
  214. }
  215. }
  216. });
  217. menu.addAction(setRegisterAction);
  218. }
  219. // 存储控件数据到剪贴板(默认实现)
  220. QVariantMap HmiControlItem::saveToClipboard() const
  221. {
  222. QVariantMap data;
  223. data["type"] = getItemType();
  224. data["posX"] = pos().x();
  225. data["posY"] = pos().y();
  226. data["scaleX"] = transform().m11();
  227. data["scaleY"] = transform().m22();
  228. data["registerAddress"] = m_registerAddress;
  229. return data;
  230. }