Ver código fonte

修改HMI框架

master
kai__wei 1 dia atrás
pai
commit
2854ab282c
9 arquivos alterados com 561 adições e 275 exclusões
  1. +89
    -0
      ComprehensivePlatformProgrammer/HMI/hmibutton.cpp
  2. +44
    -0
      ComprehensivePlatformProgrammer/HMI/hmibutton.h
  3. +18
    -33
      ComprehensivePlatformProgrammer/HMI/hmicontrolitem.cpp
  4. +236
    -198
      ComprehensivePlatformProgrammer/HMI/hmieditorwidget.cpp
  5. +46
    -21
      ComprehensivePlatformProgrammer/HMI/hmieditorwidget.h
  6. +84
    -0
      ComprehensivePlatformProgrammer/HMI/hmiled.cpp
  7. +44
    -0
      ComprehensivePlatformProgrammer/HMI/hmiled.h
  8. +0
    -11
      ComprehensivePlatformProgrammer/HMI/hmiwidgetfactory.cpp
  9. +0
    -12
      ComprehensivePlatformProgrammer/HMI/hmiwidgetfactory.h

+ 89
- 0
ComprehensivePlatformProgrammer/HMI/hmibutton.cpp Ver arquivo

@@ -0,0 +1,89 @@
#include "hmibutton.h"
#include <QPainter>
#include <QMenu>
#include <QAction>
#include <QGraphicsScene>

// 构造函数:初始化按钮控件
HmiButton::HmiButton(QGraphicsItem* parent)
: HmiControlItem(parent) {
}

// 返回按钮的边界矩形
QRectF HmiButton::boundingRect() const {
return QRectF(0, 0, 80, 40).adjusted(-1, -1, 1, 1);
}

// 绘制按钮(根据状态设置不同颜色与文本)
void HmiButton::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::black);
painter->setBrush(m_isButtonOn ? m_buttonOnColor : m_buttonOffColor);
painter->drawRect(0, 0, 80, 40);
painter->drawText(QRectF(0, 0, 80, 40), Qt::AlignCenter, m_isButtonOn ? "ON" : "OFF");
if (isSelected()) {
painter->setPen(QPen(Qt::black, 1, Qt::DashLine));
painter->setBrush(Qt::NoBrush);
painter->drawRect(boundingRect().adjusted(1, 1, -1, -1));
}
}

// 右键菜单
void HmiButton::createContextMenu(QMenu& menu) {
HmiControlItem::createContextMenu(menu);

QAction* deleteAction = new QAction("删除", &menu);
QObject::connect(deleteAction, &QAction::triggered, [this]() { deleteItem(); });

QAction* copyAction = new QAction("复制", &menu);
QObject::connect(copyAction, &QAction::triggered, [this]() {
copyToClipboard(saveToClipboard());
});

QAction* setOnAction = new QAction("置ON", &menu);
QObject::connect(setOnAction, &QAction::triggered, [this]() { setButtonState(true); });

QAction* setOffAction = new QAction("置OFF", &menu);
QObject::connect(setOffAction, &QAction::triggered, [this]() { setButtonState(false); });

QMenu* appearanceMenu = menu.addMenu("更改外观");
QAction* changeOnAppearance = new QAction("设置ON状态颜色", appearanceMenu);
QObject::connect(changeOnAppearance, &QAction::triggered,
[this]() { changeAppearanceColor(true, m_buttonOnColor, m_buttonOffColor); });

QAction* changeOffAppearance = new QAction("设置OFF状态颜色", appearanceMenu);
QObject::connect(changeOffAppearance, &QAction::triggered,
[this]() { changeAppearanceColor(false, m_buttonOnColor, m_buttonOffColor); });

menu.addAction(copyAction);
menu.addSeparator();
menu.addAction(setOnAction);
menu.addAction(setOffAction);
menu.addMenu(appearanceMenu);
appearanceMenu->addAction(changeOnAppearance);
appearanceMenu->addAction(changeOffAppearance);
menu.addSeparator();
menu.addAction(deleteAction);
}

// 保存按钮数据
QVariantMap HmiButton::saveToClipboard() const {
QVariantMap data = HmiControlItem::saveToClipboard();
data["isButtonOn"] = m_isButtonOn;
data["onColor"] = m_buttonOnColor.name();
data["offColor"] = m_buttonOffColor.name();
return data;
}

// 设置按钮状态并触发重绘(仅在变化时发出信号,避免读回写环路)
void HmiButton::setButtonState(bool on) {
if (m_isButtonOn == on) { update(); return; }
m_isButtonOn = on;
update();
emit stateChanged(m_isButtonOn);
}

\

+ 44
- 0
ComprehensivePlatformProgrammer/HMI/hmibutton.h Ver arquivo

@@ -0,0 +1,44 @@
#pragma once
#include <QVariantMap>
#include "hmicontrolitem.h"
#include <QGraphicsSceneMouseEvent>

// HMI按钮控件类,继承自HmiControlItem,实现按钮特有的功能
class HmiButton : public HmiControlItem {
Q_OBJECT
public:
// 构造函数,初始化按钮控件
explicit HmiButton(QGraphicsItem* parent = nullptr);

// 返回按钮的边界矩形(固定大小80x40,带调整边距)
QRectF boundingRect() const override;

// 绘制按钮(根据ON/OFF状态绘制不同颜色和文本)
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;

// 返回控件类型名称(用于创建/保存等)
QString getItemType() const override { return "按钮"; }

// 保存按钮数据到剪贴板(包含状态与颜色)
QVariantMap saveToClipboard() const override;

// 设置ON/OFF颜色(给现有创建/粘贴流程用)
void setOnColor(const QColor& color) { m_buttonOnColor = color; update(); }
void setOffColor(const QColor& color) { m_buttonOffColor = color; update(); }

// 设置按钮ON/OFF;当状态发生变化时,会发出 stateChanged() 供 Modbus 写线圈
void setButtonState(bool on);

signals:
void stateChanged(bool on); // 新增:按钮状态变化信号

protected:

// 右键菜单(保留已有:复制/删除/置ON/置OFF/改颜色/设置寄存器)
void createContextMenu(QMenu& menu) override;

private:
bool m_isButtonOn = false; // 按钮状态(true为ON,false为OFF)
QColor m_buttonOnColor = Qt::blue; // ON状态颜色
QColor m_buttonOffColor = Qt::lightGray; // OFF状态颜色
};

+ 18
- 33
ComprehensivePlatformProgrammer/HMI/hmicontrolitem.cpp Ver arquivo

@@ -16,7 +16,7 @@ QVariantMap HmiControlItem::m_clipboardData;

// 构造函数:初始化控件基本属性
HmiControlItem::HmiControlItem(QGraphicsItem* parent)
: QObject(nullptr), QGraphicsItem(parent), m_registerAddress(-1)
: QObject(nullptr), QGraphicsItem(parent), m_registerAddress()
{
// 设置控件属性:可移动、可选择、发送几何变化通知
setFlags(QGraphicsItem::ItemIsMovable |
@@ -97,38 +97,24 @@ void HmiControlItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
return;
}
if (isSelected()) {
// 拖动模式:移动控件位置
QPointF delta = event->pos() - m_dragStartPos;
QPointF newPos = pos() + delta;
QRectF itemRect = transform().mapRect(boundingRect());
QRectF sceneRect = scene()->sceneRect();
// 限制控件在场景边界内
newPos.setX(qMax(sceneRect.left(), qMin(newPos.x(), sceneRect.right() - itemRect.width())));
newPos.setY(qMax(sceneRect.top(), qMin(newPos.y(), sceneRect.bottom() - itemRect.height())));
if (newPos.x() + itemRect.width() > sceneRect.right()) {
newPos.setX(sceneRect.right() - itemRect.width());
}
if (newPos.y() + itemRect.height() > sceneRect.bottom()) {
newPos.setY(sceneRect.bottom() - itemRect.height());
}
if (newPos.x() < sceneRect.left()) {
newPos.setX(sceneRect.left());
}
if (newPos.y() < sceneRect.top()) {
newPos.setY(sceneRect.top());
}
setPos(newPos);
if (scene()) {
// 更新场景和视图
scene()->update();
if (scene()->views().size() > 0) {
scene()->views().first()->viewport()->update();
}
// 使用scenePos计算偏移量,更准确
QPointF delta = event->scenePos() - event->lastScenePos();
QPointF newPos = pos() + delta;

// 获取变换后的边界矩形
QRectF itemRect = mapToScene(boundingRect()).boundingRect();
QRectF sceneRect = scene()->sceneRect();

// 限制控件在场景边界内
newPos.setX(qMax(sceneRect.left(), qMin(newPos.x(), sceneRect.right() - itemRect.width())));
newPos.setY(qMax(sceneRect.top(), qMin(newPos.y(), sceneRect.bottom() - itemRect.height())));

setPos(newPos);
update();
event->accept();
return;
}
event->accept();
return;
}
QGraphicsItem::mouseMoveEvent(event);
QGraphicsItem::mouseMoveEvent(event);
}

// 鼠标释放事件处理:结束缩放或拖动
@@ -170,7 +156,6 @@ void HmiControlItem::changeAppearanceColor(bool isOnState, QColor& onColor, QCol
}
}

// 设置寄存器绑定
// 设置寄存器绑定
bool HmiControlItem::setRegisterBinding(int address) {
if (address >= 0 && address <= 4000) {


+ 236
- 198
ComprehensivePlatformProgrammer/HMI/hmieditorwidget.cpp Ver arquivo

@@ -1,54 +1,28 @@
#include "hmieditorwidget.h"
#include "hmiwidgetfactory.h"
#include <QHBoxLayout>
#include "hmibutton.h"
#include "hmiled.h"
#include "hmicontrolitem.h"

#include <QVBoxLayout>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QDebug>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QContextMenuEvent>
#include <QMenu>
#include <QTimer>
#include <QCoreApplication>
#include <QMetaObject>
#include "hmicontrolitem.h"
#include <QGraphicsView>
#include <QApplication>
#include <QClipboard>

HmiEditorWidget::HmiEditorWidget(QWidget* parent)
: QWidget(parent)
: EditorWidget(parent)
{
QHBoxLayout* mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(10, 10, 10, 10);
mainLayout->setSpacing(10);

QVBoxLayout* leftLayout = new QVBoxLayout();
leftLayout->setSpacing(5);

hmiToolbar = new QListWidget(this);
hmiToolbar->setViewMode(QListWidget::IconMode);
hmiToolbar->setIconSize(QSize(48, 48));
hmiToolbar->setFixedWidth(100);
hmiToolbar->setSpacing(10);
hmiToolbar->setDragEnabled(false);

QListWidgetItem* buttonItem = new QListWidgetItem();
buttonItem->setIcon(QIcon(":resource/image/button.png"));
buttonItem->setData(Qt::UserRole, QString("按钮"));
buttonItem->setToolTip("按钮");
hmiToolbar->addItem(buttonItem);

QListWidgetItem* ledItem = new QListWidgetItem();
ledItem->setIcon(QIcon(":resource/image/light.png"));
ledItem->setData(Qt::UserRole, QString("指示灯"));
ledItem->setToolTip("指示灯");
hmiToolbar->addItem(ledItem);

leftLayout->addWidget(hmiToolbar);
// 左侧:在父类 EditorWidget 的左侧工具栏区域追加页面按钮与标签
QLayoutItem* item = layout()->itemAt(0)->widget()->layout()->itemAt(0);
auto* leftLayout = dynamic_cast<QVBoxLayout*>(item->layout());

m_newPageButton = new QPushButton("新建页面", this);
m_newPageButton = new QPushButton("新建页面", this);
m_deletePageButton = new QPushButton("删除页面", this);
m_prevPageButton = new QPushButton("上一页", this);
m_nextPageButton = new QPushButton("下一页", this);
m_pageLabel = new QLabel("无页面", this);
m_prevPageButton = new QPushButton("上一页", this);
m_nextPageButton = new QPushButton("下一页", this);
m_pageLabel = new QLabel("无页面", this);

leftLayout->addWidget(m_newPageButton);
leftLayout->addWidget(m_deletePageButton);
@@ -57,100 +31,128 @@ HmiEditorWidget::HmiEditorWidget(QWidget* parent)
leftLayout->addWidget(m_pageLabel);
leftLayout->addStretch();

mainLayout->addLayout(leftLayout);

hmiEditArea = new QGraphicsView(this);
hmiEditArea->setRenderHint(QPainter::Antialiasing);
hmiEditArea->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
hmiEditArea->setOptimizationFlags(QGraphicsView::DontSavePainterState | QGraphicsView::DontAdjustForAntialiasing);
hmiEditArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
hmiEditArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
mainLayout->addWidget(hmiEditArea, 1);

hmiEditArea->installEventFilter(this);

connect(hmiToolbar, &QListWidget::itemClicked, this, &HmiEditorWidget::onToolbarItemClicked);
connect(m_newPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onNewPageClicked);
connect(m_newPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onNewPageClicked);
connect(m_deletePageButton, &QPushButton::clicked, this, &HmiEditorWidget::onDeletePageClicked);
connect(m_prevPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onPreviousPageClicked);
connect(m_nextPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onNextPageClicked);
connect(m_prevPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onPreviousPageClicked);
connect(m_nextPageButton, &QPushButton::clicked, this, &HmiEditorWidget::onNextPageClicked);

// 工具栏项
initToolbar();

// 确保视图已创建(父类提供)
ensureViewReady();

// 捕获编辑区鼠标:既监听 QGraphicsView 自身,也监听其 viewport
if (editArea) {
editArea->installEventFilter(this);
if (editArea->viewport())
editArea->viewport()->installEventFilter(this);
}
}

HmiEditorWidget::~HmiEditorWidget()
{
HmiEditorWidget::~HmiEditorWidget() {
clearPages();
}

void HmiEditorWidget::ensureViewReady()
void HmiEditorWidget::refreshView()
{
QCoreApplication::processEvents();
hmiEditArea->resetTransform();
hmiEditArea->viewport()->update();

if (hmiEditArea->viewport()->rect().isEmpty()) {
hmiEditArea->setFixedSize(600, 400);
QCoreApplication::processEvents();
if (m_currentPageIndex >= 0 && !m_pages.isEmpty()) {
editArea->setScene(nullptr);
editArea->setScene(m_pages[m_currentPageIndex]);
initViewScale();
editArea->viewport()->update();
}
}

void HmiEditorWidget::showEvent(QShowEvent* event)
{
QWidget::showEvent(event);
if (m_firstShow && m_pages.isEmpty()) {
m_firstShow = false;
createNewPage();
}
void HmiEditorWidget::initToolbar() {
// “按钮”
auto* buttonItem = new QListWidgetItem();
buttonItem->setIcon(QIcon(":resource/image/button.png"));
buttonItem->setData(Qt::UserRole, QString("按钮"));
buttonItem->setToolTip("按钮");
toolbar->addItem(buttonItem);

// “指示灯”
auto* ledItem = new QListWidgetItem();
ledItem->setIcon(QIcon(":resource/image/light.png"));
ledItem->setData(Qt::UserRole, QString("指示灯"));
ledItem->setToolTip("指示灯");
toolbar->addItem(ledItem);
}

void HmiEditorWidget::onToolbarItemClicked(QListWidgetItem* item) {
if (!item) return;

// —— 关键改动 ——:
// 不再“立即创建到场景中心”,而是进入“放置模式”
m_pendingType = item->data(Qt::UserRole).toString();
setPlacingCursor(true);

appendOutput(QString("已选择控件:%1。请在编辑区点击以放置。").arg(m_pendingType));
}

QGraphicsScene* HmiEditorWidget::getCurrentScene() const {
if (m_currentPageIndex >= 0 && m_currentPageIndex < m_pages.size())
return m_pages[m_currentPageIndex];
return nullptr;
}

void HmiEditorWidget::clearPages() {
for (QGraphicsScene* scene : m_pages) delete scene;
m_pages.clear();
m_currentPageIndex = -1;
if (editArea) editArea->setScene(nullptr);
updatePageLabel();
}

void HmiEditorWidget::createNewPage()
{
QGraphicsScene* newScene = new QGraphicsScene(this);
QRect viewRect = hmiEditArea->viewport()->rect();

auto* newScene = new QGraphicsScene(this);
QRect viewRect = editArea->viewport()->rect();
if (viewRect.isEmpty()) {
viewRect = QRect(0, 0, 800, 600);
hmiEditArea->setSceneRect(viewRect);
viewRect = QRect(0,0,800,600);
editArea->setSceneRect(viewRect);
}

newScene->setSceneRect(viewRect);
m_pages.append(newScene);
m_currentPageIndex = m_pages.size() - 1;

QMetaObject::invokeMethod(this, [this, newScene]() {
hmiEditArea->setScene(newScene);
QMetaObject::invokeMethod(this, [this, newScene](){
editArea->setScene(newScene);
initViewScale();
updatePageLabel();
hmiEditArea->viewport()->update();
editArea->viewport()->update();
}, Qt::QueuedConnection);
}

void HmiEditorWidget::refreshView()
{
if (m_currentPageIndex >= 0 && !m_pages.isEmpty()) {
hmiEditArea->setScene(nullptr);
hmiEditArea->setScene(m_pages[m_currentPageIndex]);
initViewScale();
hmiEditArea->viewport()->update();
void HmiEditorWidget::switchToPage(int index) {
if (index >= 0 && index < m_pages.size()) {
m_currentPageIndex = index;
editArea->resetTransform();
editArea->setScene(m_pages[index]);
updatePageLabel();
QMetaObject::invokeMethod(this, [this](){
initViewScale();
editArea->viewport()->update();
}, Qt::QueuedConnection);
}
}

void HmiEditorWidget::initViewScale()
{
if (!hmiEditArea || m_currentPageIndex < 0 || m_pages.isEmpty()) {
return;
}
if (!editArea || m_currentPageIndex < 0 || m_pages.isEmpty()) return;

QRect viewRect = hmiEditArea->viewport()->rect();
if (viewRect.isEmpty()) {
viewRect = QRect(0, 0, 800, 600);
}
QRect viewRect = editArea->viewport()->rect();
if (viewRect.isEmpty()) viewRect = QRect(0,0,800,600);

QGraphicsScene* currentScene = m_pages[m_currentPageIndex];
currentScene->setSceneRect(viewRect);
hmiEditArea->fitInView(viewRect, Qt::KeepAspectRatio);
hmiEditArea->viewport()->update();
auto* currentScene = m_pages[m_currentPageIndex];
QRectF currentRect = currentScene->sceneRect();
const qreal newW = qMax(currentRect.width(), qreal(viewRect.width()));
const qreal newH = qMax(currentRect.height(), qreal(viewRect.height()));
currentScene->setSceneRect(0,0,newW,newH);

editArea->viewport()->update();
}

void HmiEditorWidget::updatePageLabel()
@@ -161,51 +163,36 @@ void HmiEditorWidget::updatePageLabel()
m_prevPageButton->setEnabled(false);
m_nextPageButton->setEnabled(false);
} else {
m_pageLabel->setText(QString("第 %1/%2 页").arg(m_currentPageIndex + 1).arg(m_pages.size()));
m_deletePageButton->setEnabled(m_pages.size() > 1);
m_prevPageButton->setEnabled(m_currentPageIndex > 0);
m_nextPageButton->setEnabled(m_currentPageIndex < m_pages.size() - 1);
}
}

void HmiEditorWidget::switchToPage(int index)
{
if (index >= 0 && index < m_pages.size()) {
m_currentPageIndex = index;
hmiEditArea->resetTransform();
hmiEditArea->setScene(m_pages[index]);

QRectF sceneRect = m_pages[index]->sceneRect();
if (!sceneRect.isEmpty()) {
hmiEditArea->fitInView(sceneRect, Qt::KeepAspectRatio);
}

updatePageLabel();
QMetaObject::invokeMethod(this, [this]() {
hmiEditArea->viewport()->update();
}, Qt::QueuedConnection);
m_pageLabel->setText(QString("第 %1/%2 页").arg(m_currentPageIndex+1).arg(m_pages.size()));
m_deletePageButton->setEnabled(m_pages.size()>1);
m_prevPageButton->setEnabled(m_currentPageIndex>0);
m_nextPageButton->setEnabled(m_currentPageIndex<m_pages.size()-1);
}
}

bool HmiEditorWidget::eventFilter(QObject* obj, QEvent* event)
{
if (obj == hmiEditArea && event->type() == QEvent::ContextMenu) {
QContextMenuEvent* contextEvent = static_cast<QContextMenuEvent*>(event);
// —— 右键空白处:弹出“粘贴”菜单(保留原逻辑) —— //
if (obj == editArea && event->type() == QEvent::ContextMenu) {
auto* contextEvent = static_cast<QContextMenuEvent*>(event);
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) {
QGraphicsItem* item = hmiEditArea->itemAt(contextEvent->pos());
if (!item) {
QGraphicsItem* hit = editArea->itemAt(contextEvent->pos());
if (!hit) {
QMenu menu;
QAction* pasteAction = new QAction("粘贴", &menu);
pasteAction->setEnabled(HmiControlItem::hasClipboardData());
QObject::connect(pasteAction, &QAction::triggered, [this, contextEvent]() {
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex] && HmiControlItem::hasClipboardData()) {
QPointF scenePos = hmiEditArea->mapToScene(contextEvent->pos());
HmiControlItem* newItem = HmiControlItem::createFromClipboard(HmiControlItem::getClipboardData(), scenePos);
connect(pasteAction, &QAction::triggered, [this, contextEvent](){
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex] &&
HmiControlItem::hasClipboardData()) {

const QPointF scenePos = editArea->mapToScene(contextEvent->pos());
HmiControlItem* newItem =
HmiControlItem::createFromClipboard(HmiControlItem::getClipboardData(), scenePos);
if (newItem) {
m_pages[m_currentPageIndex]->addItem(newItem);
m_pages[m_currentPageIndex]->clearSelection();
newItem->setSelected(true);
hmiEditArea->viewport()->update();
editArea->viewport()->update();
}
}
});
@@ -215,116 +202,167 @@ bool HmiEditorWidget::eventFilter(QObject* obj, QEvent* event)
}
return true;
}
return QWidget::eventFilter(obj, event);
}

void HmiEditorWidget::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
if (m_currentPageIndex >= 0 && !m_pages.isEmpty()) {
initViewScale();
// —— 关键改动:放置模式下,点击编辑区创建 —— //
// 捕获 QGraphicsView 的 viewport 左键按下
if (obj == (editArea ? editArea->viewport() : nullptr)
&& event->type() == QEvent::MouseButtonPress && !m_pendingType.isEmpty()) {

auto* me = static_cast<QMouseEvent*>(event);
if (me->button() == Qt::LeftButton
&& m_currentPageIndex >= 0
&& m_pages[m_currentPageIndex]) {

const QPointF scenePos = editArea->mapToScene(me->pos());
HmiControlItem* item = createItemByType(m_pendingType, scenePos);
if (item) {
m_pages[m_currentPageIndex]->addItem(item);
m_pages[m_currentPageIndex]->clearSelection();
item->setSelected(true);
editArea->viewport()->update();
}
// 一次放一个:退出放置模式(如需连续放置,注释掉两行)
m_pendingType.clear();
setPlacingCursor(false);

return true; // 已处理
}
}

return EditorWidget::eventFilter(obj, event);
}

void HmiEditorWidget::resizeEvent(QResizeEvent* e) {
EditorWidget::resizeEvent(e);
if (m_currentPageIndex >= 0 && !m_pages.isEmpty()) initViewScale();
}

void HmiEditorWidget::showEvent(QShowEvent* e) {
EditorWidget::showEvent(e);
if (m_pages.isEmpty()) createNewPage();
}

void HmiEditorWidget::keyPressEvent(QKeyEvent* event)
{
// Ctrl+C:复制(保留原逻辑——复制首个选中项至静态剪贴板)
if (event->matches(QKeySequence::Copy)) {
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) {
auto selectedItems = m_pages[m_currentPageIndex]->selectedItems();
if (!selectedItems.isEmpty()) {
HmiControlItem* item = dynamic_cast<HmiControlItem*>(selectedItems.first());
if (item) {
if (auto* item = dynamic_cast<HmiControlItem*>(selectedItems.first())) {
HmiControlItem::copyToClipboard(item->saveToClipboard());
}
}
}
return;
}
else if (event->matches(QKeySequence::Paste)) {
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex] && HmiControlItem::hasClipboardData()) {
QRectF sceneRect = m_pages[m_currentPageIndex]->sceneRect();
QPointF centerPos(sceneRect.width() / 2, sceneRect.height() / 2);
HmiControlItem* newItem = HmiControlItem::createFromClipboard(HmiControlItem::getClipboardData(), centerPos);

// Ctrl+V:粘贴(关键改动:若有选中源控件 → 旁边粘贴;否则 → 场景中心)
if (event->matches(QKeySequence::Paste)) {
if (m_currentPageIndex >= 0 &&
m_pages[m_currentPageIndex] &&
HmiControlItem::hasClipboardData()) {

QPointF pastePos;
auto selectedItems = m_pages[m_currentPageIndex]->selectedItems();
static int s_pasteIndex = 0; // 连续多次粘贴时,用来轻微错行

if (!selectedItems.isEmpty()) {
if (auto* src = dynamic_cast<HmiControlItem*>(selectedItems.first())) {
pastePos = calcPasteBesidePos(src, s_pasteIndex++);
}
} else {
// 无选中项:仍按原逻辑 → 场景中心
QRectF rect = m_pages[m_currentPageIndex]->sceneRect();
pastePos = QPointF(rect.width()/2, rect.height()/2);
s_pasteIndex = 0;
}

HmiControlItem* newItem =
HmiControlItem::createFromClipboard(HmiControlItem::getClipboardData(), pastePos);
if (newItem) {
m_pages[m_currentPageIndex]->addItem(newItem);
m_pages[m_currentPageIndex]->clearSelection();
newItem->setSelected(true);
hmiEditArea->viewport()->update();
editArea->viewport()->update();
}
}
return;
}
else if (event->matches(QKeySequence::Delete)) {

// Delete:删除(保留原逻辑)
if (event->matches(QKeySequence::Delete)) {
if (m_currentPageIndex >= 0 && m_pages[m_currentPageIndex]) {
auto selectedItems = m_pages[m_currentPageIndex]->selectedItems();
for (QGraphicsItem* item : selectedItems) {
HmiControlItem* controlItem = dynamic_cast<HmiControlItem*>(item);
if (controlItem) {
controlItem->deleteItem();
}
for (QGraphicsItem* gi : selectedItems) {
if (auto* ci = dynamic_cast<HmiControlItem*>(gi)) ci->deleteItem();
}
hmiEditArea->viewport()->update();
editArea->viewport()->update();
}
}
QWidget::keyPressEvent(event);
}

void HmiEditorWidget::onToolbarItemClicked(QListWidgetItem* item)
{
if (!item || m_currentPageIndex < 0 || !m_pages[m_currentPageIndex]) return;

QString type = item->data(Qt::UserRole).toString();
HmiControlItem* newItem = nullptr;
if (type == "按钮") {
newItem = new HmiButton();
} else if (type == "指示灯") {
newItem = new HmiLed();
return;
}

if (newItem) {
QRectF sceneRect = m_pages[m_currentPageIndex]->sceneRect();
newItem->setPos(sceneRect.width() / 2, sceneRect.height() / 2);
m_pages[m_currentPageIndex]->addItem(newItem);
m_pages[m_currentPageIndex]->clearSelection();
newItem->setSelected(true);
hmiEditArea->viewport()->update();
}
EditorWidget::keyPressEvent(event);
}

void HmiEditorWidget::onNewPageClicked()
{
createNewPage();
}
void HmiEditorWidget::onNewPageClicked() { createNewPage(); }

void HmiEditorWidget::onDeletePageClicked()
{
void HmiEditorWidget::onDeletePageClicked() {
if (m_currentPageIndex >= 0 && m_pages.size() > 1) {
QGraphicsScene* scene = m_pages[m_currentPageIndex];
m_pages.removeAt(m_currentPageIndex);
delete scene;
m_currentPageIndex = qMin(m_currentPageIndex, m_pages.size() - 1);
hmiEditArea->setScene(m_pages[m_currentPageIndex]);

m_currentPageIndex = qMin(m_currentPageIndex, m_pages.size()-1);
editArea->setScene(m_pages[m_currentPageIndex]);
initViewScale();
updatePageLabel();
hmiEditArea->viewport()->update();
editArea->viewport()->update();
appendOutput(QString("删除页面,剩余页面数: %1").arg(m_pages.size()));
}
}

void HmiEditorWidget::onPreviousPageClicked()
void HmiEditorWidget::onPreviousPageClicked() { switchToPage(m_currentPageIndex - 1); }
void HmiEditorWidget::onNextPageClicked() { switchToPage(m_currentPageIndex + 1); }

// —— 私有辅助 —— //

HmiControlItem* HmiEditorWidget::createItemByType(const QString& type, const QPointF& scenePos)
{
switchToPage(m_currentPageIndex - 1);
HmiControlItem* item = nullptr;
if (type == "按钮") {
item = new HmiButton();
appendOutput("添加按钮控件");
} else if (type == "指示灯") {
item = new HmiLed();
appendOutput("添加指示灯控件");
}
if (item) item->setPos(scenePos);
return item;
}

void HmiEditorWidget::onNextPageClicked()
void HmiEditorWidget::setPlacingCursor(bool on)
{
switchToPage(m_currentPageIndex + 1);
if (!editArea) return;
if (on) {
editArea->setCursor(Qt::CrossCursor);
if (editArea->viewport()) editArea->viewport()->setCursor(Qt::CrossCursor);
} else {
editArea->unsetCursor();
if (editArea->viewport()) editArea->viewport()->unsetCursor();
}
}

void HmiEditorWidget::clearPages()
QPointF HmiEditorWidget::calcPasteBesidePos(HmiControlItem* src, int pasteIndex) const
{
for (QGraphicsScene* scene : m_pages) {
delete scene;
}
m_pages.clear();
m_currentPageIndex = -1;
hmiEditArea->setScene(nullptr);
updatePageLabel();
if (!src) return QPointF();

// 使用“映射到场景后的边界框”来获取可视宽度,避免缩放带来的误差
QRectF sceneRect = src->mapToScene(src->boundingRect()).boundingRect();
const qreal w = qMax<qreal>(sceneRect.width(), 24.0); // 保底宽度
const qreal gap = 12.0; // 与原件的水平间隔
const qreal stepY = 8.0; // 连续粘贴时的竖向微调

QPointF base = src->pos();
return QPointF(base.x() + w + gap, base.y() + pasteIndex * stepY);
}

+ 46
- 21
ComprehensivePlatformProgrammer/HMI/hmieditorwidget.h Ver arquivo

@@ -1,51 +1,76 @@
#pragma once
#include <QWidget>
#include <QListWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include "editorwidget.h"
#include <QLabel>
#include <QPushButton>
#include <QList>
#include <QListWidgetItem>
#include <QKeyEvent>
#include <QEvent>
#include <QGraphicsScene>

class HmiControlItem;

class HmiEditorWidget : public QWidget {
/**
* @brief HMI (Human Machine Interface) 图形编辑器控件
*
* 继承自 EditorWidget,扩展了多页面管理、点击放置与“旁边粘贴”
*/
class HmiEditorWidget : public EditorWidget {
Q_OBJECT

public:
explicit HmiEditorWidget(QWidget* parent = nullptr);
~HmiEditorWidget();

Q_INVOKABLE QList<QGraphicsScene*> getPages() const { return m_pages; }
Q_INVOKABLE int getCurrentPageIndex() const { return m_currentPageIndex; }
Q_INVOKABLE void clearPages();
Q_INVOKABLE void createNewPage();
Q_INVOKABLE void refreshView();
// 页面管理接口
QGraphicsScene* getCurrentScene() const override;
QList<QGraphicsScene*> getPages() const override { return m_pages; }
int getCurrentPageIndex() const override { return m_currentPageIndex; }
void clearPages() override;
void createNewPage();
void switchToPage(int index);
void refreshView();

protected:
// 覆盖 EditorWidget 接口
void initToolbar() override;
void onToolbarItemClicked(QListWidgetItem* item) override;
void keyPressEvent(QKeyEvent* event) override;
bool eventFilter(QObject* obj, QEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void showEvent(QShowEvent* event) override;

private slots:
void onToolbarItemClicked(QListWidgetItem* item);
void onNewPageClicked();
void onDeletePageClicked();
void onPreviousPageClicked();
void onNextPageClicked();

private:
// —— 辅助 —— //
void initViewScale();
void updatePageLabel();
void ensureViewReady();

QListWidget* hmiToolbar;
QGraphicsView* hmiEditArea;
// 根据类型名创建控件并放到 scenePos 上;成功返回新控件指针
HmiControlItem* createItemByType(const QString& type, const QPointF& scenePos);

// 设置/清除“放置模式”的鼠标提示
void setPlacingCursor(bool on);

// 计算“在原控件旁边”的粘贴位置(第 n 次粘贴做轻微错行)
QPointF calcPasteBesidePos(HmiControlItem* src, int pasteIndex = 0) const;

private:
// 页面
QList<QGraphicsScene*> m_pages;
int m_currentPageIndex = -1;
QLabel* m_pageLabel;
QPushButton* m_newPageButton;
QPushButton* m_deletePageButton;
QPushButton* m_prevPageButton;
QPushButton* m_nextPageButton;
bool m_firstShow = true;

// UI
QLabel* m_pageLabel = nullptr;
QPushButton* m_newPageButton = nullptr;
QPushButton* m_deletePageButton = nullptr;
QPushButton* m_prevPageButton = nullptr;
QPushButton* m_nextPageButton = nullptr;

// 交互状态:待放置的控件类型(与工具栏 item 的 UserRole 文本一致:如 “按钮”“指示灯”)
QString m_pendingType;
};

+ 84
- 0
ComprehensivePlatformProgrammer/HMI/hmiled.cpp Ver arquivo

@@ -0,0 +1,84 @@
#include "hmiled.h"
#include <QPainter>
#include <QMenu>
#include <QAction>
#include <QGraphicsScene>

// 构造函数:初始化指示灯控件
HmiLed::HmiLed(QGraphicsItem* parent)
: HmiControlItem(parent) {
}

// 返回指示灯的边界矩形
QRectF HmiLed::boundingRect() const {
return QRectF(0, 0, 60, 60).adjusted(-1, -1, 1, 1);
}

// 绘制指示灯:根据状态绘制圆形,使用ON或OFF状态颜色
void HmiLed::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
Q_UNUSED(option); // 消除未使用参数警告
Q_UNUSED(widget); // 消除未使用参数警告
painter->setRenderHint(QPainter::Antialiasing); // 启用抗锯齿
painter->setPen(Qt::black); // 设置边框颜色
painter->setBrush(m_isLedOn ? m_ledOnColor : m_ledOffColor); // 根据状态设置填充颜色
qreal size = qMin(boundingRect().width(), boundingRect().height());
painter->drawEllipse(0, 0, size - 2, size - 2); // 绘制圆形
painter->drawText(QRectF(0, 0, size - 2, size - 2), Qt::AlignCenter, ""); // 空文本(占位)
if (isSelected()) {
// 选中时绘制虚线边框
painter->setPen(QPen(Qt::black, 1, Qt::DashLine));
painter->setBrush(Qt::NoBrush);
painter->drawRect(boundingRect().adjusted(1, 1, -1, -1));
}
}

// 创建指示灯的右键菜单
void HmiLed::createContextMenu(QMenu& menu) {
// 调用基类的右键菜单,添加寄存器绑定选项
HmiControlItem::createContextMenu(menu);

// 删除选项
QAction* deleteAction = new QAction("删除", &menu);
QObject::connect(deleteAction, &QAction::triggered, [this]() { deleteItem(); });

// 复制选项
QAction* copyAction = new QAction("复制", &menu);
QObject::connect(copyAction, &QAction::triggered, [this]() {
copyToClipboard(saveToClipboard());
});

// 更改外观子菜单
QMenu* appearanceMenu = menu.addMenu("更改外观");
QAction* changeOnAppearance = new QAction("设置ON状态颜色", appearanceMenu);
QObject::connect(changeOnAppearance, &QAction::triggered,
[this]() { changeAppearanceColor(true, m_ledOnColor, m_ledOffColor); });

QAction* changeOffAppearance = new QAction("设置OFF状态颜色", appearanceMenu);
QObject::connect(changeOffAppearance, &QAction::triggered,
[this]() { changeAppearanceColor(false, m_ledOnColor, m_ledOffColor); });

// 添加菜单项
menu.addAction(copyAction);
menu.addSeparator();
menu.addMenu(appearanceMenu);
appearanceMenu->addAction(changeOnAppearance);
appearanceMenu->addAction(changeOffAppearance);
menu.addSeparator();
menu.addAction(deleteAction);
}

// 保存指示灯数据到剪贴板
QVariantMap HmiLed::saveToClipboard() const {
QVariantMap data = HmiControlItem::saveToClipboard();
data["isLedOn"] = m_isLedOn;
data["onColor"] = m_ledOnColor.name();
data["offColor"] = m_ledOffColor.name();
return data;
}

// 设置指示灯状态并触发重绘
void HmiLed::setLedState(bool on) {
m_isLedOn = on;
update();
}

+ 44
- 0
ComprehensivePlatformProgrammer/HMI/hmiled.h Ver arquivo

@@ -0,0 +1,44 @@
#pragma once
#include <QVariantMap>
#include "hmicontrolitem.h"

// HMI指示灯控件类,继承自HmiControlItem,实现指示灯特有的功能
class HmiLed : public HmiControlItem {
Q_OBJECT
public:
// 构造函数,初始化指示灯控件
explicit HmiLed(QGraphicsItem* parent = nullptr);

// 返回指示灯的边界矩形(固定大小60x60,带调整边距)
QRectF boundingRect() const override;

// 绘制指示灯(根据ON/OFF状态绘制不同颜色)
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;

// 返回控件类型(固定为“指示灯”)
QString getItemType() const override { return "指示灯"; }

// 保存指示灯数据到剪贴板(位置、缩放、状态、颜色等)
QVariantMap saveToClipboard() const override;

// 设置ON状态颜色,触发重绘
void setOnColor(const QColor& color) { m_ledOnColor = color; update(); }

// 设置OFF状态颜色,触发重绘
void setOffColor(const QColor& color) { m_ledOffColor = color; update(); }

// 设置指示灯状态(ON/OFF),供外部调用(如寄存器状态变化),触发重绘
void setLedState(bool on);

protected:
// 返回宽高比(指示灯为正方形,固定为1.0)
qreal getAspectRatio() const override { return 1.0; }

// 创建指示灯的右键菜单(包含复制、删除、颜色设置等,不包含置ON/OFF)
void createContextMenu(QMenu& menu) override;

private:
bool m_isLedOn = false; // 指示灯状态(true为ON,false为OFF)
QColor m_ledOnColor = Qt::green; // ON状态颜色
QColor m_ledOffColor = Qt::red; // OFF状态颜色
};

+ 0
- 11
ComprehensivePlatformProgrammer/HMI/hmiwidgetfactory.cpp Ver arquivo

@@ -1,11 +0,0 @@
#include "hmiwidgetfactory.h"

// 根据名称创建HMI控件
HmiControlItem* HmiWidgetFactory::createItem(const QString& name) {
if (name == "按钮") {
return new HmiButton();
} else if (name == "指示灯") {
return new HmiLed();
}
return nullptr; // 未知类型返回空
}

+ 0
- 12
ComprehensivePlatformProgrammer/HMI/hmiwidgetfactory.h Ver arquivo

@@ -1,12 +0,0 @@
#pragma once
#include "hmibutton.h"
#include "hmiled.h"
#include <QString>

// HMI控件工厂类(静态类,用于创建HMI控件)
class HmiWidgetFactory {
public:
// 静态方法:根据名称创建对应的HMI控件
static HmiControlItem* createItem(const QString& name);
};


Carregando…
Cancelar
Salvar