@@ -4,6 +4,14 @@ | |||||
#include <QMenu> | #include <QMenu> | ||||
#include <QGraphicsScene> | #include <QGraphicsScene> | ||||
#include <QColorDialog> | #include <QColorDialog> | ||||
#include <QInputDialog> | |||||
#include <QDialog> | |||||
#include <QLabel> | |||||
#include <QSpinBox> | |||||
#include <QHBoxLayout> | |||||
#include <QVBoxLayout> | |||||
#include <QPushButton> | |||||
#include <QGraphicsView> | |||||
// HmiComponent 基类实现 | // HmiComponent 基类实现 | ||||
HmiComponent::HmiComponent(QGraphicsItem *parent) : QGraphicsObject(parent) | HmiComponent::HmiComponent(QGraphicsItem *parent) : QGraphicsObject(parent) | ||||
@@ -47,6 +55,19 @@ void HmiComponent::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti | |||||
} | } | ||||
} | } | ||||
void HmiComponent::setAddress(int address) | |||||
{ | |||||
if (m_address != address) { | |||||
m_address = address; | |||||
update(); | |||||
} | |||||
} | |||||
int HmiComponent::address() const | |||||
{ | |||||
return m_address; | |||||
} | |||||
void HmiComponent::mousePressEvent(QGraphicsSceneMouseEvent *event) | void HmiComponent::mousePressEvent(QGraphicsSceneMouseEvent *event) | ||||
{ | { | ||||
// 右键不触发选中信号,留给 contextMenuEvent 处理 | // 右键不触发选中信号,留给 contextMenuEvent 处理 | ||||
@@ -58,38 +79,212 @@ void HmiComponent::mousePressEvent(QGraphicsSceneMouseEvent *event) | |||||
void HmiComponent::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) | void HmiComponent::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) | ||||
{ | { | ||||
// 右键点击时,先清空场景中的其他选中项,然后选中当前项 | |||||
scene()->clearSelection(); | scene()->clearSelection(); | ||||
setSelected(true); | setSelected(true); | ||||
emit selected(this); // 同样可以发出选中信号,以便更新属性面板等 | |||||
emit selected(this); | |||||
QMenu menu; | QMenu menu; | ||||
QAction *propertyAction = menu.addAction("属性"); | |||||
QAction *copyAction = menu.addAction("复制"); | QAction *copyAction = menu.addAction("复制"); | ||||
QAction *deleteAction = menu.addAction("删除"); | QAction *deleteAction = menu.addAction("删除"); | ||||
QAction *appearanceAction = menu.addAction("改变外观"); | |||||
// 使用 connect 将菜单动作的触发连接到信号的发射,复制和删除 | |||||
connect(propertyAction, &QAction::triggered, this, [this]() { | |||||
// 弹出属性对话框 | |||||
QDialog dialog; | |||||
dialog.setWindowTitle("属性"); | |||||
QLabel* addressLabel = new QLabel("地址: "); | |||||
QSpinBox* addressSpin = new QSpinBox(); | |||||
addressSpin->setRange(0, 4000); | |||||
addressSpin->setValue(m_address); // 使用当前组件地址初始化 | |||||
// ON状态颜色设置 | |||||
QLabel* onColorLabel = new QLabel("ON状态外观: "); | |||||
// 颜色显示,用QLabel设置背景色简单示范 | |||||
QLabel* onColorDisplay = new QLabel; | |||||
onColorDisplay->setFixedSize(40, 20); | |||||
onColorDisplay->setAutoFillBackground(true); | |||||
QPalette onPal = onColorDisplay->palette(); | |||||
onPal.setColor(QPalette::Window, m_onColor); | |||||
onColorDisplay->setPalette(onPal); | |||||
onColorDisplay->setFrameShape(QFrame::Box); | |||||
QPushButton* onColorBtn = new QPushButton("选择颜色"); | |||||
// OFF状态颜色设置 | |||||
QLabel* offColorLabel = new QLabel("OFF状态外观: "); | |||||
QLabel* offColorDisplay = new QLabel; | |||||
offColorDisplay->setFixedSize(40, 20); | |||||
offColorDisplay->setAutoFillBackground(true); | |||||
QPalette offPal = offColorDisplay->palette(); | |||||
offPal.setColor(QPalette::Window, m_offColor); | |||||
offColorDisplay->setPalette(offPal); | |||||
offColorDisplay->setFrameShape(QFrame::Box); | |||||
QPushButton* offColorBtn = new QPushButton("选择颜色"); | |||||
QObject::connect(offColorBtn, &QPushButton::clicked, [&]() { | |||||
QColor color = QColorDialog::getColor(m_offColor, nullptr, "选择 OFF 状态颜色"); | |||||
if (color.isValid()) { | |||||
m_offColor = color; | |||||
QPalette pal = offColorDisplay->palette(); | |||||
pal.setColor(QPalette::Window, color); | |||||
offColorDisplay->setPalette(pal); | |||||
offColorDisplay->update(); | |||||
setColor(color); // 默认显示OFF状态颜色 | |||||
} | |||||
}); | |||||
// 地址布局 | |||||
QHBoxLayout* addressLayout = new QHBoxLayout(); | |||||
addressLayout->addWidget(addressLabel); | |||||
addressLayout->addWidget(addressSpin); | |||||
// ON颜色布局 | |||||
QHBoxLayout* onColorLayout = new QHBoxLayout(); | |||||
onColorLayout->addWidget(onColorLabel); | |||||
onColorLayout->addWidget(onColorDisplay); | |||||
onColorLayout->addWidget(onColorBtn); | |||||
// OFF颜色布局 | |||||
QHBoxLayout* offColorLayout = new QHBoxLayout(); | |||||
offColorLayout->addWidget(offColorLabel); | |||||
offColorLayout->addWidget(offColorDisplay); | |||||
offColorLayout->addWidget(offColorBtn); | |||||
// 确定/取消按钮 | |||||
QPushButton* okBtn = new QPushButton("确定"); | |||||
QPushButton* cancelBtn = new QPushButton("取消"); | |||||
QHBoxLayout* btnLayout = new QHBoxLayout(); | |||||
btnLayout->addStretch(); | |||||
btnLayout->addWidget(okBtn); | |||||
btnLayout->addWidget(cancelBtn); | |||||
// 颜色选择按钮点击槽 | |||||
QObject::connect(onColorBtn, &QPushButton::clicked, [&]() { | |||||
QColor color = QColorDialog::getColor(m_onColor, nullptr, "选择 ON 状态颜色"); | |||||
if (color.isValid()) { | |||||
m_onColor = color; | |||||
QPalette pal = onColorDisplay->palette(); | |||||
pal.setColor(QPalette::Window, color); | |||||
onColorDisplay->setPalette(pal); | |||||
onColorDisplay->update(); | |||||
} | |||||
}); | |||||
QVBoxLayout* mainLayout = new QVBoxLayout(); | |||||
mainLayout->addLayout(addressLayout); | |||||
mainLayout->addLayout(onColorLayout); | |||||
mainLayout->addLayout(offColorLayout); | |||||
mainLayout->addLayout(btnLayout); | |||||
dialog.setLayout(mainLayout); | |||||
// 关闭逻辑 | |||||
QObject::connect(okBtn, &QPushButton::clicked, [&]() { | |||||
// 保存地址 | |||||
int newAddress = addressSpin->value(); | |||||
setAddress(newAddress); | |||||
// 已经在onColorBtn / offColorBtn中修改了m_onColor / m_offColor,需要触发界面更新 | |||||
update(); | |||||
dialog.accept(); | |||||
}); | |||||
QObject::connect(cancelBtn, &QPushButton::clicked, &dialog, &QDialog::reject); | |||||
dialog.exec(); | |||||
}); | |||||
connect(copyAction, &QAction::triggered, this, [this]() { | connect(copyAction, &QAction::triggered, this, [this]() { | ||||
emit copyRequested(this); | emit copyRequested(this); | ||||
}); | }); | ||||
connect(deleteAction, &QAction::triggered, this, [this]() { | connect(deleteAction, &QAction::triggered, this, [this]() { | ||||
emit deleteRequested(this); | emit deleteRequested(this); | ||||
}); | }); | ||||
connect(appearanceAction, &QAction::triggered, this, &HmiComponent::changeAppearance); | |||||
// 在鼠标光标位置显示菜单 | |||||
menu.exec(event->screenPos()); | menu.exec(event->screenPos()); | ||||
} | } | ||||
void HmiComponent::changeAppearance() | |||||
void HmiComponent::mouseMoveEvent(QGraphicsSceneMouseEvent *event) | |||||
{ | { | ||||
emit appearanceChangedRequested(this); | |||||
if (!(flags() & QGraphicsItem::ItemIsMovable)) { | |||||
// 如果项不可移动,直接调用基类处理 | |||||
QGraphicsObject::mouseMoveEvent(event); | |||||
return; | |||||
} | |||||
// 计算拖动后的位置 | |||||
QPointF delta = event->scenePos() - event->lastScenePos(); | |||||
QPointF newPos = pos() + delta; | |||||
// 获取视图(假设只有1个视图) | |||||
QList<QGraphicsView*> views = scene()->views(); | |||||
if (views.isEmpty()) { | |||||
// 没有视图则默认处理 | |||||
QGraphicsObject::mouseMoveEvent(event); | |||||
return; | |||||
} | |||||
QGraphicsView* view = views.first(); | |||||
// 获取视口矩形(像素坐标) | |||||
QRect viewportRect = view->viewport()->rect(); | |||||
// 将视口矩形映射到场景坐标 | |||||
QPointF topLeftScene = view->mapToScene(viewportRect.topLeft()); | |||||
QPointF bottomRightScene = view->mapToScene(viewportRect.bottomRight()); | |||||
QRectF visibleSceneRect(topLeftScene, bottomRightScene); | |||||
// 组件的边界矩形(局部坐标) | |||||
QRectF bounds = boundingRect(); | |||||
// 计算移动后组件边界矩形(在场景坐标) | |||||
QRectF newBounds = bounds.translated(newPos); | |||||
// 限制组件边界必须完全在视图可见区域内 | |||||
qreal limitedX = newPos.x(); | |||||
qreal limitedY = newPos.y(); | |||||
if (newBounds.left() < visibleSceneRect.left()) | |||||
limitedX += visibleSceneRect.left() - newBounds.left(); | |||||
if (newBounds.top() < visibleSceneRect.top()) | |||||
limitedY += visibleSceneRect.top() - newBounds.top(); | |||||
if (newBounds.right() > visibleSceneRect.right()) | |||||
limitedX -= newBounds.right() - visibleSceneRect.right(); | |||||
if (newBounds.bottom() > visibleSceneRect.bottom()) | |||||
limitedY -= newBounds.bottom() - visibleSceneRect.bottom(); | |||||
setPos(QPointF(limitedX, limitedY)); | |||||
event->accept(); | |||||
} | |||||
void HmiComponent::setOnColor(const QColor& color) { | |||||
m_onColor = color; | |||||
} | |||||
void HmiComponent::setOffColor(const QColor& color) { | |||||
m_offColor = color; | |||||
setColor(color); // 默认展示 OFF 状态颜色 | |||||
} | |||||
QColor HmiComponent::onColor() const { | |||||
return m_onColor; | |||||
} | } | ||||
QColor HmiComponent::offColor() const { | |||||
return m_offColor; | |||||
} | |||||
HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent) | HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent) | ||||
{ | { | ||||
m_color = Qt::gray; | m_color = Qt::gray; | ||||
m_offColor = m_color; // OFF状态颜色设为默认颜色 | |||||
m_onColor = Qt::green; // ON状态颜色为绿色 | |||||
m_componentName = "Button"; | m_componentName = "Button"; | ||||
setColor(m_offColor); // 当前显示OFF颜色 | |||||
} | } | ||||
QRectF HmiButton::boundingRect() const | QRectF HmiButton::boundingRect() const | ||||
@@ -106,8 +301,11 @@ void HmiButton::paintShape(QPainter *painter) | |||||
HmiIndicator::HmiIndicator(QGraphicsItem *parent) : HmiComponent(parent) | HmiIndicator::HmiIndicator(QGraphicsItem *parent) : HmiComponent(parent) | ||||
{ | { | ||||
m_color = Qt::green; | |||||
m_color = Qt::red; | |||||
m_offColor = m_color; // OFF状态颜色设为默认颜色 | |||||
m_onColor = Qt::green; // ON状态颜色为绿色 | |||||
m_componentName = "Indicator"; | m_componentName = "Indicator"; | ||||
setColor(m_offColor); // 当前显示OFF颜色 | |||||
} | } | ||||
QRectF HmiIndicator::boundingRect() const | QRectF HmiIndicator::boundingRect() const | ||||
@@ -2,6 +2,7 @@ | |||||
#define CUSTOMGRAPHICS_H | #define CUSTOMGRAPHICS_H | ||||
#include <QGraphicsObject> | #include <QGraphicsObject> | ||||
#include <QColor> | |||||
// 只需要前向声明,因为头文件中只用到了指针 | // 只需要前向声明,因为头文件中只用到了指针 | ||||
class QPainter; | class QPainter; | ||||
class QGraphicsSceneMouseEvent; | class QGraphicsSceneMouseEvent; | ||||
@@ -16,41 +17,56 @@ enum class ComponentType { | |||||
class HmiComponent : public QGraphicsObject | class HmiComponent : public QGraphicsObject | ||||
{ | { | ||||
Q_OBJECT | Q_OBJECT | ||||
Q_PROPERTY(QColor color READ color WRITE setColor) | |||||
Q_PROPERTY(QColor color READ color WRITE setColor) // 当前颜色(默认使用 OFF 状态) | |||||
Q_PROPERTY(QString componentName READ componentName WRITE setComponentName) | Q_PROPERTY(QString componentName READ componentName WRITE setComponentName) | ||||
// 新增address属性 | |||||
Q_PROPERTY(int address READ address WRITE setAddress) | |||||
signals: | signals: | ||||
void selected(HmiComponent* item); | void selected(HmiComponent* item); | ||||
// 为复制和删除请求添加信号 | |||||
void copyRequested(HmiComponent* item); | void copyRequested(HmiComponent* item); | ||||
void deleteRequested(HmiComponent* item); | void deleteRequested(HmiComponent* item); | ||||
void appearanceChangedRequested(HmiComponent* item); | |||||
// 添加信号用于改变状态颜色 | |||||
void changeOnColorRequested(HmiComponent* item); | |||||
void changeOffColorRequested(HmiComponent* item); | |||||
public: | public: | ||||
explicit HmiComponent(QGraphicsItem *parent = nullptr); | explicit HmiComponent(QGraphicsItem *parent = nullptr); | ||||
void setColor(const QColor& color); | |||||
void setColor(const QColor& color); // 当前显示颜色 | |||||
QColor color() const; | QColor color() const; | ||||
void setComponentName(const QString& name); | void setComponentName(const QString& name); | ||||
QString componentName() const; | QString componentName() const; | ||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; | |||||
// 设置 ON / OFF 状态颜色 | |||||
void setOnColor(const QColor& color); | |||||
void setOffColor(const QColor& color); | |||||
QColor onColor() const; | |||||
QColor offColor() const; | |||||
public slots: | |||||
void changeAppearance(); | |||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; | |||||
// 地址相关 | |||||
void setAddress(int address); | |||||
int address() const; | |||||
protected: | protected: | ||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override; | void mousePressEvent(QGraphicsSceneMouseEvent *event) override; | ||||
// 重写上下文菜单事件 | |||||
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; | void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; | ||||
virtual void paintShape(QPainter* painter) = 0; | virtual void paintShape(QPainter* painter) = 0; | ||||
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; | |||||
protected: | protected: | ||||
QColor m_color; | |||||
QColor m_color; // 当前显示颜色(编辑器中为 OFF) | |||||
QColor m_onColor = Qt::green; | |||||
QColor m_offColor; | |||||
QString m_componentName; | QString m_componentName; | ||||
int m_address = 0; | |||||
}; | }; | ||||
// 按钮类 | // 按钮类 | ||||
class HmiButton : public HmiComponent | class HmiButton : public HmiComponent | ||||
{ | { | ||||
@@ -7,6 +7,11 @@ | |||||
#include <QVBoxLayout> | #include <QVBoxLayout> | ||||
#include <algorithm> | #include <algorithm> | ||||
#include <QColorDialog> | #include <QColorDialog> | ||||
#include <QFile> | |||||
#include <QJsonDocument> | |||||
#include <QJsonArray> | |||||
#include <QJsonObject> | |||||
#include <QDebug> | |||||
HMIModule::HMIModule(Ui::MainWindow* ui, QObject *parent) | HMIModule::HMIModule(Ui::MainWindow* ui, QObject *parent) | ||||
: QObject{parent}, ui_(ui), m_scene(nullptr) | : QObject{parent}, ui_(ui), m_scene(nullptr) | ||||
@@ -56,6 +61,236 @@ void HMIModule::init() { | |||||
connect(this, &HMIModule::pageRemoved, this, &HMIModule::onPageRemoved); | connect(this, &HMIModule::pageRemoved, this, &HMIModule::onPageRemoved); | ||||
} | } | ||||
bool HMIModule::saveToFile(const QString& filePath) | |||||
{ | |||||
QTabWidget* tabWidget = ui_->tabWidget_2; | |||||
QJsonArray pagesArray; | |||||
for (int i = 0; i < tabWidget->count(); ++i) { | |||||
QWidget* page = tabWidget->widget(i); | |||||
QString pageName = tabWidget->tabText(i); | |||||
QGraphicsView* graphicsView = page->findChild<QGraphicsView*>(); | |||||
if (!graphicsView) continue; | |||||
QGraphicsScene* scene = graphicsView->scene(); | |||||
if (!scene) continue; | |||||
QJsonObject pageObject; | |||||
pageObject["pageName"] = pageName; | |||||
QJsonArray componentsArray; | |||||
for (QGraphicsItem* item : scene->items()) { | |||||
HmiComponent* component = dynamic_cast<HmiComponent*>(item); | |||||
if (!component) | |||||
continue; | |||||
QJsonObject compObj; | |||||
// 组件类型 | |||||
if (dynamic_cast<HmiButton*>(component)) { | |||||
compObj["type"] = "Button"; | |||||
} else if (dynamic_cast<HmiIndicator*>(component)) { | |||||
compObj["type"] = "Indicator"; | |||||
} else { | |||||
continue; // 忽略未知类型 | |||||
} | |||||
// 位置 | |||||
QPointF pos = component->pos(); | |||||
compObj["x"] = pos.x(); | |||||
compObj["y"] = pos.y(); | |||||
// 颜色 (保存在HEX) | |||||
compObj["color"] = component->color().name(); | |||||
// On / Off颜色 | |||||
compObj["onColor"] = component->onColor().name(); | |||||
compObj["offColor"] = component->offColor().name(); | |||||
// 组件名称 | |||||
compObj["componentName"] = component->componentName(); | |||||
// 新增地址属性 | |||||
compObj["address"] = component->address(); | |||||
componentsArray.append(compObj); | |||||
} | |||||
pageObject["components"] = componentsArray; | |||||
pagesArray.append(pageObject); | |||||
} | |||||
QJsonObject rootObject; | |||||
rootObject["pages"] = pagesArray; | |||||
QJsonDocument doc(rootObject); | |||||
QFile file(filePath); | |||||
if (!file.open(QIODevice::WriteOnly)) { | |||||
emit logMessageGenerated(QString("无法打开文件进行保存: %1").arg(filePath)); | |||||
return false; | |||||
} | |||||
file.write(doc.toJson(QJsonDocument::Indented)); | |||||
file.close(); | |||||
emit logMessageGenerated(QString("[%1] 保存成功: %2") | |||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) | |||||
.arg(filePath)); | |||||
return true; | |||||
} | |||||
// 清空所有页面,重建页面并加载组件 | |||||
bool HMIModule::openFromFile(const QString& filePath) | |||||
{ | |||||
QFile file(filePath); | |||||
if (!file.open(QIODevice::ReadOnly)) { | |||||
emit logMessageGenerated(QString("无法打开文件进行加载: %1").arg(filePath)); | |||||
return false; | |||||
} | |||||
QByteArray data = file.readAll(); | |||||
file.close(); | |||||
QJsonParseError parseError; | |||||
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError); | |||||
if (parseError.error != QJsonParseError::NoError) { | |||||
emit logMessageGenerated(QString("JSON解析错误: %1").arg(parseError.errorString())); | |||||
return false; | |||||
} | |||||
if (!doc.isObject()) { | |||||
emit logMessageGenerated("JSON格式错误,不是对象"); | |||||
return false; | |||||
} | |||||
QJsonObject rootObj = doc.object(); | |||||
if (!rootObj.contains("pages") || !rootObj["pages"].isArray()) { | |||||
emit logMessageGenerated("JSON缺少pages数组"); | |||||
return false; | |||||
} | |||||
QJsonArray pagesArray = rootObj["pages"].toArray(); | |||||
QTabWidget* tabWidget = ui_->tabWidget_2; | |||||
// 清空所有现有页面 | |||||
while (tabWidget->count() > 0) { | |||||
QWidget* page = tabWidget->widget(0); | |||||
tabWidget->removeTab(0); | |||||
delete page; | |||||
} | |||||
m_availablePageNumbers.clear(); | |||||
m_pageOrder.clear(); | |||||
for (const QJsonValue& pageVal : pagesArray) { | |||||
if (!pageVal.isObject()) | |||||
continue; | |||||
QJsonObject pageObj = pageVal.toObject(); | |||||
QString pageName = pageObj["pageName"].toString("Unnamed"); | |||||
int pageNumber = 0; | |||||
if (pageName.startsWith("Page ")) { | |||||
pageNumber = pageName.mid(5).toInt(); | |||||
} | |||||
// 新建页面 | |||||
QWidget* newPage = new QWidget(tabWidget); | |||||
newPage->setObjectName(pageName); | |||||
QGraphicsView* newGraphicsView = new QGraphicsView(newPage); | |||||
CustomGraphicsScene* newScene = new CustomGraphicsScene(newGraphicsView, this); | |||||
// 设置大小,与默认页面相同 | |||||
QSize defaultSize(691, 381); | |||||
newGraphicsView->resize(defaultSize); | |||||
newScene->setSceneRect(0, 0, 800, 600); | |||||
newGraphicsView->setScene(newScene); | |||||
QVBoxLayout* layout = new QVBoxLayout(newPage); | |||||
layout->addWidget(newGraphicsView); | |||||
newPage->setLayout(layout); | |||||
int tabIndex = tabWidget->addTab(newPage, pageName); | |||||
// 连接信号 | |||||
connect(newScene, &CustomGraphicsScene::componentCreated, this, &HMIModule::onComponentCreated); | |||||
connect(newScene, &CustomGraphicsScene::copyRequestFromScene, this, &HMIModule::onCopyRequested); | |||||
connect(newScene, &CustomGraphicsScene::pasteRequestFromScene, this, &HMIModule::onPasteRequested); | |||||
connect(newScene, &CustomGraphicsScene::deleteRequestFromScene, this, &HMIModule::onDeleteRequested); | |||||
m_pageOrder.append(pageNumber); | |||||
QJsonArray compArray = pageObj["components"].toArray(); | |||||
for (const QJsonValue& compVal : compArray) { | |||||
if (!compVal.isObject()) | |||||
continue; | |||||
QJsonObject compObj = compVal.toObject(); | |||||
QString typeStr = compObj["type"].toString(); | |||||
double x = compObj["x"].toDouble(); | |||||
double y = compObj["y"].toDouble(); | |||||
QString colorName = compObj["color"].toString(); | |||||
QString onColorName = compObj["onColor"].toString(); | |||||
QString offColorName = compObj["offColor"].toString(); | |||||
QString componentName = compObj["componentName"].toString(); | |||||
int address = compObj["address"].toInt(0); | |||||
HmiComponent* newItem = nullptr; | |||||
if (typeStr == "Button") { | |||||
newItem = new HmiButton(); | |||||
} else if (typeStr == "Indicator") { | |||||
newItem = new HmiIndicator(); | |||||
} else { | |||||
continue; | |||||
} | |||||
if (newItem) { | |||||
newItem->setPos(QPointF(x, y)); | |||||
QColor color(colorName); | |||||
if (color.isValid()) { | |||||
newItem->setColor(color); | |||||
} | |||||
QColor onColor(onColorName); | |||||
if (onColor.isValid()) | |||||
newItem->setOnColor(onColor); | |||||
QColor offColor(offColorName); | |||||
if (offColor.isValid()) | |||||
newItem->setOffColor(offColor); | |||||
QColor currentColor(colorName); | |||||
if (currentColor.isValid()) { | |||||
// 可以根据实际业务逻辑判断,是否恢复为onColor或offColor | |||||
// 这里直接恢复当前颜色 | |||||
newItem->setColor(currentColor); | |||||
} else { | |||||
// 如果当前颜色无效,则使用 offColor 作为默认颜色 | |||||
newItem->setColor(newItem->offColor()); | |||||
} | |||||
newItem->setComponentName(componentName); | |||||
// 设置地址 | |||||
newItem->setAddress(address); | |||||
newScene->addItem(newItem); | |||||
setupNewComponent(newItem); | |||||
} | |||||
} | |||||
} | |||||
// 自动切换到第一页 | |||||
if (tabWidget->count() > 0) { | |||||
tabWidget->setCurrentIndex(0); | |||||
} | |||||
emit logMessageGenerated(QString("[%1] 打开文件成功: %2") | |||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) | |||||
.arg(filePath)); | |||||
return true; | |||||
} | |||||
void HMIModule::setupNewComponent(HmiComponent* item) | void HMIModule::setupNewComponent(HmiComponent* item) | ||||
{ | { | ||||
if (!item) return; | if (!item) return; | ||||
@@ -63,7 +298,9 @@ void HMIModule::setupNewComponent(HmiComponent* item) | |||||
connect(item, &HmiComponent::selected, this, &HMIModule::onComponentSelected); | connect(item, &HmiComponent::selected, this, &HMIModule::onComponentSelected); | ||||
connect(item, &HmiComponent::copyRequested, this, &HMIModule::onCopyRequested); | connect(item, &HmiComponent::copyRequested, this, &HMIModule::onCopyRequested); | ||||
connect(item, &HmiComponent::deleteRequested, this, &HMIModule::onDeleteRequested); | connect(item, &HmiComponent::deleteRequested, this, &HMIModule::onDeleteRequested); | ||||
connect(item, &HmiComponent::appearanceChangedRequested, this, &HMIModule::onChangeAppearanceRequested); // 连接信号 | |||||
connect(item, &HmiComponent::changeOnColorRequested, this, &HMIModule::onChangeOnColorRequested); | |||||
connect(item, &HmiComponent::changeOffColorRequested, this, &HMIModule::onChangeOffColorRequested); | |||||
QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); | QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); | ||||
QString log = QString("[%1] 创建 %2 组件").arg(currentTime).arg(item->componentName()); | QString log = QString("[%1] 创建 %2 组件").arg(currentTime).arg(item->componentName()); | ||||
@@ -177,13 +414,6 @@ void HMIModule::onDeleteRequested() | |||||
} | } | ||||
} | } | ||||
void HMIModule::onChangeAppearanceRequested(HmiComponent* item) | |||||
{ | |||||
QColor color = QColorDialog::getColor(item->color(), nullptr, "选择颜色"); | |||||
if (color.isValid()) { | |||||
item->setColor(color); | |||||
} | |||||
} | |||||
// 添加页面后的槽函数 | // 添加页面后的槽函数 | ||||
void HMIModule::onPageAdded(int pageNumber, int index) | void HMIModule::onPageAdded(int pageNumber, int index) | ||||
@@ -203,6 +433,30 @@ void HMIModule::onPageRemoved(int pageNumber, int index) | |||||
} | } | ||||
} | } | ||||
void HMIModule::onChangeOnColorRequested(HmiComponent* item) | |||||
{ | |||||
QColor color = QColorDialog::getColor(item->onColor(), nullptr, "选择 ON 状态颜色"); | |||||
if (color.isValid()) { | |||||
item->setOnColor(color); | |||||
emit logMessageGenerated(QString("[%1] 设置 %2 的 ON 状态颜色为 %3") | |||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) | |||||
.arg(item->componentName()) | |||||
.arg(color.name())); | |||||
} | |||||
} | |||||
void HMIModule::onChangeOffColorRequested(HmiComponent* item) | |||||
{ | |||||
QColor color = QColorDialog::getColor(item->offColor(), nullptr, "选择 OFF 状态颜色"); | |||||
if (color.isValid()) { | |||||
item->setOffColor(color); // 同时更新当前显示颜色 | |||||
emit logMessageGenerated(QString("[%1] 设置 %2 的 OFF 状态颜色为 %3") | |||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) | |||||
.arg(item->componentName()) | |||||
.arg(color.name())); | |||||
} | |||||
} | |||||
void HMIModule::onComponentSelected(HmiComponent* item) | void HMIModule::onComponentSelected(HmiComponent* item) | ||||
{ | { | ||||
// 不需要任何操作,可以留空 | // 不需要任何操作,可以留空 | ||||
@@ -266,7 +520,7 @@ void HMIModule::addPage() | |||||
tabIndex = tabWidget->addTab(newPage, pageName); | tabIndex = tabWidget->addTab(newPage, pageName); | ||||
} | } | ||||
// 连接信号和槽 ******************************************************************* | |||||
// 连接信号和槽 | |||||
connect(newScene, &CustomGraphicsScene::componentCreated, this, &HMIModule::onComponentCreated); | connect(newScene, &CustomGraphicsScene::componentCreated, this, &HMIModule::onComponentCreated); | ||||
connect(newScene, &CustomGraphicsScene::copyRequestFromScene, this, &HMIModule::onCopyRequested); | connect(newScene, &CustomGraphicsScene::copyRequestFromScene, this, &HMIModule::onCopyRequested); | ||||
connect(newScene, &CustomGraphicsScene::pasteRequestFromScene, this, &HMIModule::onPasteRequested); | connect(newScene, &CustomGraphicsScene::pasteRequestFromScene, this, &HMIModule::onPasteRequested); | ||||
@@ -15,6 +15,8 @@ public: | |||||
explicit HMIModule(Ui::MainWindow* ui, QObject *parent = nullptr); | explicit HMIModule(Ui::MainWindow* ui, QObject *parent = nullptr); | ||||
void setButtonIcon(QAbstractButton* button, const QString& iconPath); | void setButtonIcon(QAbstractButton* button, const QString& iconPath); | ||||
void init(); | void init(); | ||||
bool saveToFile(const QString& filePath); | |||||
bool openFromFile(const QString& filePath); | |||||
signals: | signals: | ||||
void pageAdded(int pageNumber, int index); | void pageAdded(int pageNumber, int index); | ||||
@@ -29,13 +31,14 @@ private slots: | |||||
void onCopyRequested(); | void onCopyRequested(); | ||||
void onPasteRequested(const QPointF& scenePos); | void onPasteRequested(const QPointF& scenePos); | ||||
void onDeleteRequested(); | void onDeleteRequested(); | ||||
void onChangeAppearanceRequested(HmiComponent* item); | |||||
void addPage(); | void addPage(); | ||||
void deletePage(); | void deletePage(); | ||||
void prePage(); | void prePage(); | ||||
void nextPage(); | void nextPage(); | ||||
void onPageAdded(int pageNumber, int index); | void onPageAdded(int pageNumber, int index); | ||||
void onPageRemoved(int pageNumber, int index); | void onPageRemoved(int pageNumber, int index); | ||||
void onChangeOnColorRequested(HmiComponent* item); | |||||
void onChangeOffColorRequested(HmiComponent* item); | |||||
private: | private: | ||||
void setupNewComponent(HmiComponent* item); | void setupNewComponent(HmiComponent* item); | ||||
@@ -3,7 +3,8 @@ | |||||
#include <QToolButton> | #include <QToolButton> | ||||
#include <QVBoxLayout> | #include <QVBoxLayout> | ||||
#include <QButtonGroup> | #include <QButtonGroup> | ||||
#include <QFileDialog> | |||||
#include <QMessageBox> | |||||
MainWindow::MainWindow(QWidget *parent) | MainWindow::MainWindow(QWidget *parent) | ||||
: QMainWindow(parent) | : QMainWindow(parent) | ||||
@@ -16,6 +17,24 @@ MainWindow::MainWindow(QWidget *parent) | |||||
initMainWindow(); | initMainWindow(); | ||||
// 连接 HMIModule 的信号到 MainWindow 的槽 | // 连接 HMIModule 的信号到 MainWindow 的槽 | ||||
connect(hmi_, &HMIModule::logMessageGenerated, this, &MainWindow::appendLog); | connect(hmi_, &HMIModule::logMessageGenerated, this, &MainWindow::appendLog); | ||||
connect(ui_->action_3, &QAction::triggered, this, [this]() { | |||||
QString fileName = QFileDialog::getSaveFileName(this, "保存HMI设计", "", "JSON文件 (*.json)"); | |||||
if (!fileName.isEmpty()) { | |||||
if (!hmi_->saveToFile(fileName)) { | |||||
QMessageBox::warning(this, "保存失败", "文件保存失败!"); | |||||
} | |||||
} | |||||
}); | |||||
connect(ui_->action_2, &QAction::triggered, this, [this]() { | |||||
QString fileName = QFileDialog::getOpenFileName(this, "打开HMI设计", "", "JSON文件 (*.json)"); | |||||
if (!fileName.isEmpty()) { | |||||
if (!hmi_->openFromFile(fileName)) { | |||||
QMessageBox::warning(this, "打开失败", "文件打开失败或格式不正确!"); | |||||
} | |||||
} | |||||
}); | |||||
} | } | ||||
MainWindow::~MainWindow() | MainWindow::~MainWindow() | ||||