@@ -86,6 +86,26 @@ void HmiComponent::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) | |||||
QMenu menu; | QMenu menu; | ||||
QAction *propertyAction = menu.addAction("属性"); | QAction *propertyAction = menu.addAction("属性"); | ||||
QAction *setOnAction = nullptr; | |||||
QAction *setOffAction = nullptr; | |||||
// 只有按钮才添加“置ON”和“置OFF” | |||||
if (auto btn = dynamic_cast<HmiButton*>(this)) { | |||||
setOnAction = menu.addAction("置ON"); | |||||
setOffAction = menu.addAction("置OFF"); | |||||
connect(setOnAction, &QAction::triggered, this, [btn]() { | |||||
btn->setOn(true); | |||||
}); | |||||
connect(setOffAction, &QAction::triggered, this, [btn]() { | |||||
btn->setOn(false); | |||||
}); | |||||
} | |||||
menu.addSeparator(); | |||||
QAction *copyAction = menu.addAction("复制"); | QAction *copyAction = menu.addAction("复制"); | ||||
QAction *deleteAction = menu.addAction("删除"); | QAction *deleteAction = menu.addAction("删除"); | ||||
@@ -278,7 +298,7 @@ QColor HmiComponent::offColor() const { | |||||
} | } | ||||
HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent) | |||||
HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent), m_isOn(false) | |||||
{ | { | ||||
m_color = Qt::gray; | m_color = Qt::gray; | ||||
m_offColor = m_color; // OFF状态颜色设为默认颜色 | m_offColor = m_color; // OFF状态颜色设为默认颜色 | ||||
@@ -287,6 +307,23 @@ HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent) | |||||
setColor(m_offColor); // 当前显示OFF颜色 | setColor(m_offColor); // 当前显示OFF颜色 | ||||
} | } | ||||
bool HmiButton::isOn() const | |||||
{ | |||||
return m_isOn; | |||||
} | |||||
void HmiButton::setOn(bool on) | |||||
{ | |||||
if (m_isOn != on) { | |||||
m_isOn = on; | |||||
if (m_isOn) | |||||
setColor(m_onColor); | |||||
else | |||||
setColor(m_offColor); | |||||
update(); | |||||
} | |||||
} | |||||
QRectF HmiButton::boundingRect() const | QRectF HmiButton::boundingRect() const | ||||
{ | { | ||||
return QRectF(0, 0, 65, 30); | return QRectF(0, 0, 65, 30); | ||||
@@ -74,8 +74,15 @@ public: | |||||
HmiButton(QGraphicsItem *parent = nullptr); | HmiButton(QGraphicsItem *parent = nullptr); | ||||
QRectF boundingRect() const override; | QRectF boundingRect() const override; | ||||
// 新增状态标志,true = ON,false = OFF | |||||
bool isOn() const; | |||||
void setOn(bool on); | |||||
protected: | protected: | ||||
void paintShape(QPainter *painter) override; | void paintShape(QPainter *painter) override; | ||||
private: | |||||
bool m_isOn = false; // 默认OFF | |||||
}; | }; | ||||
// 指示灯类 | // 指示灯类 | ||||
@@ -111,6 +111,11 @@ bool HMIModule::saveToFile(const QString& filePath) | |||||
compObj["componentName"] = component->componentName(); | compObj["componentName"] = component->componentName(); | ||||
// 新增地址属性 | // 新增地址属性 | ||||
compObj["address"] = component->address(); | compObj["address"] = component->address(); | ||||
// 添加按钮状态 | |||||
compObj["isOn"] = false; // 默认false | |||||
if (auto btn = dynamic_cast<HmiButton*>(component)) { | |||||
compObj["isOn"] = btn->isOn(); | |||||
} | |||||
componentsArray.append(compObj); | componentsArray.append(compObj); | ||||
} | } | ||||
@@ -136,6 +141,8 @@ bool HMIModule::saveToFile(const QString& filePath) | |||||
emit logMessageGenerated(QString("[%1] 保存成功: %2") | emit logMessageGenerated(QString("[%1] 保存成功: %2") | ||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) | .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) | ||||
.arg(filePath)); | .arg(filePath)); | ||||
m_isModified = false; | |||||
setModified(false); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -261,10 +268,18 @@ bool HMIModule::openFromFile(const QString& filePath) | |||||
if (offColor.isValid()) | if (offColor.isValid()) | ||||
newItem->setOffColor(offColor); | newItem->setOffColor(offColor); | ||||
// 恢复 isOn 状态 | |||||
bool isOn = false; | |||||
if (compObj.contains("isOn")) { | |||||
isOn = compObj["isOn"].toBool(); | |||||
} | |||||
if (auto btn = dynamic_cast<HmiButton*>(newItem)) { | |||||
btn->setOn(isOn); | |||||
} | |||||
QColor currentColor(colorName); | QColor currentColor(colorName); | ||||
if (currentColor.isValid()) { | if (currentColor.isValid()) { | ||||
// 可以根据实际业务逻辑判断,是否恢复为onColor或offColor | |||||
// 这里直接恢复当前颜色 | |||||
// 根据实际业务逻辑判断,是否恢复为onColor或offColor,这里直接恢复当前颜色 | |||||
newItem->setColor(currentColor); | newItem->setColor(currentColor); | ||||
} else { | } else { | ||||
// 如果当前颜色无效,则使用 offColor 作为默认颜色 | // 如果当前颜色无效,则使用 offColor 作为默认颜色 | ||||
@@ -288,9 +303,84 @@ bool HMIModule::openFromFile(const QString& filePath) | |||||
emit logMessageGenerated(QString("[%1] 打开文件成功: %2") | emit logMessageGenerated(QString("[%1] 打开文件成功: %2") | ||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) | .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) | ||||
.arg(filePath)); | .arg(filePath)); | ||||
setModified(false); | |||||
return true; | return true; | ||||
} | } | ||||
void HMIModule::resetPages() | |||||
{ | |||||
QTabWidget* tabWidget = ui_->tabWidget_2; | |||||
if (tabWidget->count() == 0) { | |||||
// 没有页面,创建第一页 | |||||
m_pageCount = 1; | |||||
m_availablePageNumbers.clear(); | |||||
m_pageOrder.clear(); | |||||
QWidget* newPage = new QWidget(tabWidget); | |||||
QString pageName = QString("Page 1"); | |||||
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); | |||||
tabWidget->addTab(newPage, pageName); | |||||
m_pageOrder.append(1); | |||||
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); | |||||
return; | |||||
} | |||||
// 有页面时: | |||||
// 删除除第一页以外的所有页面 | |||||
while (tabWidget->count() > 1) { | |||||
QWidget* page = tabWidget->widget(1); | |||||
tabWidget->removeTab(1); | |||||
delete page; | |||||
} | |||||
// 清空第一页场景 | |||||
QWidget* firstPage = tabWidget->widget(0); | |||||
QGraphicsView* view = firstPage->findChild<QGraphicsView*>(); | |||||
if (view && view->scene()) { | |||||
view->scene()->clear(); | |||||
} | |||||
// 重置页面编号管理 | |||||
m_availablePageNumbers.clear(); | |||||
m_pageOrder.clear(); | |||||
m_pageCount = 1; | |||||
m_pageOrder.append(1); | |||||
// 可以选择更新第一页Tab名称(如果可能被改名) | |||||
tabWidget->setTabText(0, "Page 1"); | |||||
firstPage->setObjectName("Page 1"); | |||||
} | |||||
bool HMIModule::isModified() const | |||||
{ | |||||
return m_isModified; | |||||
} | |||||
void HMIModule::setModified(bool modified) | |||||
{ | |||||
m_isModified = modified; | |||||
} | |||||
void HMIModule::setupNewComponent(HmiComponent* item) | void HMIModule::setupNewComponent(HmiComponent* item) | ||||
{ | { | ||||
if (!item) return; | if (!item) return; | ||||
@@ -338,6 +428,7 @@ void HMIModule::prepareToCreateIndicator() | |||||
void HMIModule::onComponentCreated(HmiComponent* item) | void HMIModule::onComponentCreated(HmiComponent* item) | ||||
{ | { | ||||
setupNewComponent(item); | setupNewComponent(item); | ||||
setModified(true); // 标记已修改 | |||||
} | } | ||||
void HMIModule::onCopyRequested() | void HMIModule::onCopyRequested() | ||||
@@ -360,6 +451,13 @@ void HMIModule::onCopyRequested() | |||||
m_copiedType = ComponentType::Indicator; | m_copiedType = ComponentType::Indicator; | ||||
} | } | ||||
m_copiedColor = itemToCopy->color(); | m_copiedColor = itemToCopy->color(); | ||||
// 新增复制以下属性 | |||||
m_copiedOnColor = itemToCopy->onColor(); | |||||
m_copiedOffColor = itemToCopy->offColor(); | |||||
m_copiedComponentName = itemToCopy->componentName(); | |||||
m_copiedAddress = itemToCopy->address(); | |||||
m_hasCopiedItem = true; | m_hasCopiedItem = true; | ||||
emit logMessageGenerated(QString("[%1] 复制组件: %2") | emit logMessageGenerated(QString("[%1] 复制组件: %2") | ||||
@@ -386,10 +484,15 @@ void HMIModule::onPasteRequested(const QPointF& scenePos) | |||||
if (newItem) { | if (newItem) { | ||||
newItem->setColor(m_copiedColor); | newItem->setColor(m_copiedColor); | ||||
newItem->setOnColor(m_copiedOnColor); | |||||
newItem->setOffColor(m_copiedOffColor); | |||||
newItem->setComponentName(m_copiedComponentName); | |||||
newItem->setAddress(m_copiedAddress); | |||||
newItem->setPos(scenePos); | newItem->setPos(scenePos); | ||||
currentScene->addItem(newItem); // 使用当前页面的 scene | currentScene->addItem(newItem); // 使用当前页面的 scene | ||||
setupNewComponent(newItem); | setupNewComponent(newItem); | ||||
} | } | ||||
setModified(true); // 内容改变 | |||||
} | } | ||||
void HMIModule::onDeleteRequested() | void HMIModule::onDeleteRequested() | ||||
@@ -412,6 +515,7 @@ void HMIModule::onDeleteRequested() | |||||
currentScene->removeItem(item); // 使用当前页面的 scene | currentScene->removeItem(item); // 使用当前页面的 scene | ||||
delete item; | delete item; | ||||
} | } | ||||
setModified(true); // 标记已修改 | |||||
} | } | ||||
@@ -533,6 +637,7 @@ void HMIModule::addPage() | |||||
// 记录日志 | // 记录日志 | ||||
emit logMessageGenerated(QString("[%1] 添加页面: %2").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(pageName)); | emit logMessageGenerated(QString("[%1] 添加页面: %2").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(pageName)); | ||||
setModified(true); // 页面变更,标记已修改 | |||||
} | } | ||||
// 删除页面的槽函数 | // 删除页面的槽函数 | ||||
@@ -570,6 +675,7 @@ void HMIModule::deletePage() | |||||
// 记录日志 | // 记录日志 | ||||
emit logMessageGenerated(QString("[%1] 删除页面: %2").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(pageName)); | emit logMessageGenerated(QString("[%1] 删除页面: %2").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(pageName)); | ||||
} | } | ||||
setModified(true); // 标记已修改 | |||||
} | } | ||||
// 前一页的槽函数 | // 前一页的槽函数 | ||||
@@ -17,6 +17,10 @@ public: | |||||
void init(); | void init(); | ||||
bool saveToFile(const QString& filePath); | bool saveToFile(const QString& filePath); | ||||
bool openFromFile(const QString& filePath); | bool openFromFile(const QString& filePath); | ||||
// 重置页面,清空所有页,只保留一个空的第一页 | |||||
void resetPages(); | |||||
bool isModified() const; // 判断是否有未保存的修改 | |||||
void setModified(bool modified); // 设置修改状态 | |||||
signals: | signals: | ||||
void pageAdded(int pageNumber, int index); | void pageAdded(int pageNumber, int index); | ||||
@@ -48,10 +52,17 @@ private: | |||||
CustomGraphicsScene* m_scene; | CustomGraphicsScene* m_scene; | ||||
ComponentType m_copiedType; | ComponentType m_copiedType; | ||||
QColor m_copiedColor; | QColor m_copiedColor; | ||||
// 新增这些成员变量 | |||||
QColor m_copiedOnColor; | |||||
QColor m_copiedOffColor; | |||||
QString m_copiedComponentName; | |||||
int m_copiedAddress = 0; | |||||
bool m_hasCopiedItem = false; // 指示当前是否已经有组件被复制 | bool m_hasCopiedItem = false; // 指示当前是否已经有组件被复制 | ||||
int m_pageCount = 1; // 初始化页面计数器 | int m_pageCount = 1; // 初始化页面计数器 | ||||
QList<int> m_availablePageNumbers; // 存储可用的页面编号 (有序列表) | QList<int> m_availablePageNumbers; // 存储可用的页面编号 (有序列表) | ||||
QList<int> m_pageOrder; // 维护页面顺序 | QList<int> m_pageOrder; // 维护页面顺序 | ||||
bool m_isModified = false; // 添加一个修改标志变量 | |||||
}; | }; | ||||
#endif // HMIMODULE_H | #endif // HMIMODULE_H |
@@ -5,6 +5,7 @@ | |||||
#include <QButtonGroup> | #include <QButtonGroup> | ||||
#include <QFileDialog> | #include <QFileDialog> | ||||
#include <QMessageBox> | #include <QMessageBox> | ||||
#include <QCloseEvent> | |||||
MainWindow::MainWindow(QWidget *parent) | MainWindow::MainWindow(QWidget *parent) | ||||
: QMainWindow(parent) | : QMainWindow(parent) | ||||
@@ -18,8 +19,11 @@ MainWindow::MainWindow(QWidget *parent) | |||||
// 连接 HMIModule 的信号到 MainWindow 的槽 | // 连接 HMIModule 的信号到 MainWindow 的槽 | ||||
connect(hmi_, &HMIModule::logMessageGenerated, this, &MainWindow::appendLog); | connect(hmi_, &HMIModule::logMessageGenerated, this, &MainWindow::appendLog); | ||||
// 新建菜单 | |||||
connect(ui_->action, &QAction::triggered, this, &MainWindow::onNewFile); | |||||
connect(ui_->action_3, &QAction::triggered, this, [this]() { | connect(ui_->action_3, &QAction::triggered, this, [this]() { | ||||
QString fileName = QFileDialog::getSaveFileName(this, "保存HMI设计", "", "JSON文件 (*.json)"); | |||||
QString fileName = QFileDialog::getSaveFileName(this, "保存HMI文件", "", "JSON文件 (*.hmi)"); | |||||
if (!fileName.isEmpty()) { | if (!fileName.isEmpty()) { | ||||
if (!hmi_->saveToFile(fileName)) { | if (!hmi_->saveToFile(fileName)) { | ||||
QMessageBox::warning(this, "保存失败", "文件保存失败!"); | QMessageBox::warning(this, "保存失败", "文件保存失败!"); | ||||
@@ -28,7 +32,7 @@ MainWindow::MainWindow(QWidget *parent) | |||||
}); | }); | ||||
connect(ui_->action_2, &QAction::triggered, this, [this]() { | connect(ui_->action_2, &QAction::triggered, this, [this]() { | ||||
QString fileName = QFileDialog::getOpenFileName(this, "打开HMI设计", "", "JSON文件 (*.json)"); | |||||
QString fileName = QFileDialog::getOpenFileName(this, "打开HMI文件", "", "JSON文件 (*.hmi)"); | |||||
if (!fileName.isEmpty()) { | if (!fileName.isEmpty()) { | ||||
if (!hmi_->openFromFile(fileName)) { | if (!hmi_->openFromFile(fileName)) { | ||||
QMessageBox::warning(this, "打开失败", "文件打开失败或格式不正确!"); | QMessageBox::warning(this, "打开失败", "文件打开失败或格式不正确!"); | ||||
@@ -46,6 +50,8 @@ void MainWindow::initMainWindow() | |||||
{ | { | ||||
setWindowTitle("综合平台编辑器"); | setWindowTitle("综合平台编辑器"); | ||||
setWindowIcon(QIcon(":/resource/image/editor.png")); | setWindowIcon(QIcon(":/resource/image/editor.png")); | ||||
// 运行默认打开HMI页面 | |||||
ui_->tabWidget->setCurrentIndex(1); | |||||
} | } | ||||
// 实现槽函数 | // 实现槽函数 | ||||
@@ -53,3 +59,36 @@ void MainWindow::appendLog(const QString& message) | |||||
{ | { | ||||
ui_->textEdit->append(message); | ui_->textEdit->append(message); | ||||
} | } | ||||
// 新建菜单动作槽函数 | |||||
void MainWindow::onNewFile() | |||||
{ | |||||
if (!hmi_) | |||||
return; | |||||
if (hmi_->isModified()) { | |||||
auto ret = QMessageBox::warning(this, "提示", | |||||
"当前文件有未保存的更改,是否保存?", | |||||
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, | |||||
QMessageBox::Save); | |||||
if (ret == QMessageBox::Save) { | |||||
QString fileName = QFileDialog::getSaveFileName(this, "保存HMI设计", "", "JSON文件 (*.json)"); | |||||
if (!fileName.isEmpty()) { | |||||
if (!hmi_->saveToFile(fileName)) { | |||||
QMessageBox::warning(this, "保存失败", "文件保存失败!"); | |||||
return; // 不新建,保存失败 | |||||
} | |||||
} else { | |||||
return; // 取消保存,取消新建 | |||||
} | |||||
} else if (ret == QMessageBox::Cancel) { | |||||
return; // 取消新建 | |||||
} | |||||
// 如果是Discard,直接继续新建,丢弃当前修改 | |||||
} | |||||
// 清空编辑区,新建页面 | |||||
hmi_->resetPages(); | |||||
} | |||||
@@ -21,12 +21,13 @@ public: | |||||
private: | private: | ||||
void initMainWindow(); | void initMainWindow(); | ||||
// 一个用于接收日志并更新UI的槽函数 | |||||
private slots: | private slots: | ||||
void appendLog(const QString& message); | void appendLog(const QString& message); | ||||
void onNewFile(); // 新增槽函数,处理“新建”菜单 | |||||
private: | private: | ||||
Ui::MainWindow *ui_; | Ui::MainWindow *ui_; | ||||
HMIModule* hmi_; | HMIModule* hmi_; | ||||
}; | }; | ||||
#endif // MAINWINDOW_H | #endif // MAINWINDOW_H |