@@ -8,6 +8,7 @@ | |||
#include <QDropEvent> | |||
#include <QGraphicsView> | |||
#include <QMessageBox> | |||
#include <QVBoxLayout> | |||
#include "creatitem.h" | |||
HMI::HMI(QWidget *parent) : | |||
@@ -15,11 +16,9 @@ HMI::HMI(QWidget *parent) : | |||
ui(new Ui::HMI) | |||
{ | |||
ui->setupUi(this); | |||
/* 1. 场景 */ | |||
hmi_scene_ = new QGraphicsScene(this); | |||
ui->graphicsView->setScene(hmi_scene_); | |||
ui->graphicsView->setSceneRect(0, 0, 800, 600); | |||
ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag); | |||
ui->tabWidget->setTabsClosable(true); | |||
ui->tabWidget->setMovable(true); | |||
/* 2. 列表 */ | |||
ui->listWidget->setViewMode(QListView::IconMode); | |||
@@ -27,16 +26,53 @@ HMI::HMI(QWidget *parent) : | |||
ui->listWidget->setDragEnabled(false); | |||
ui->listWidget->viewport()->installEventFilter(this); | |||
// 创建初始页面 | |||
newPage(); | |||
/* 3. 填充列表 */ | |||
createComponents(); | |||
connect(ui->listWidget,&QListWidget::currentTextChanged,this,&HMI::onListwidgetCurrenttextchanged); | |||
// 连接标签页信号 | |||
// connect(ui->tabWidget, &QTabWidget::currentChanged, this, &HMI::onTabChanged); | |||
connect(ui->tabWidget, &QTabWidget::tabCloseRequested, [this](int index) { | |||
if (scenes.size() > 1) { | |||
delete scenes.takeAt(index); | |||
delete views.takeAt(index); | |||
ui->tabWidget->removeTab(index); | |||
} else { | |||
QMessageBox::information(this, "提示", "至少保留一个页面"); | |||
} | |||
}); | |||
} | |||
HMI::~HMI() | |||
{ | |||
qDeleteAll(scenes); | |||
qDeleteAll(views); | |||
delete ui; | |||
} | |||
void HMI::newPage() | |||
{ | |||
// 创建新场景 | |||
QGraphicsScene* newScene = new QGraphicsScene(this); | |||
// 创建新视图 | |||
MyGraphicsView* newView = new MyGraphicsView(ui->tabWidget); | |||
newView->setScene(newScene); | |||
newView->setSceneRect(0, 0, 800, 600); | |||
newView->setDragMode(QGraphicsView::RubberBandDrag); | |||
// 添加到标签页 | |||
int newIndex = ui->tabWidget->addTab(newView, QString("页面 %1").arg(scenes.size() + 1)); | |||
ui->tabWidget->setCurrentIndex(newIndex); | |||
// 保存场景和视图 | |||
scenes.append(newScene); | |||
views.append(newView); | |||
} | |||
bool HMI::eventFilter(QObject *obj, QEvent *event) | |||
{ | |||
if (obj == ui->listWidget->viewport()) { | |||
@@ -102,32 +138,43 @@ void HMI::createComponents() | |||
void HMI::clearScene() | |||
{ | |||
hmi_scene_->clear(); | |||
if (currentPageIndex() >= 0 && currentPageIndex() < scenes.size()) { | |||
scenes[currentPageIndex()]->clear(); | |||
} | |||
} | |||
void HMI::applyProjectToScene(const Project& proj) | |||
int HMI::currentPageIndex() const | |||
{ | |||
clearScene(); | |||
return ui->tabWidget->currentIndex(); | |||
} | |||
void HMI::applyProjectToScene(const Project& proj, int pageIndex) | |||
{ | |||
if (pageIndex < 0 || pageIndex >= scenes.size()) | |||
return; | |||
scenes[pageIndex]->clear(); | |||
QVector<Item*> itemObjs; | |||
for (const auto& d : proj.items_) { | |||
Item* item = creatItem(d.type); | |||
if (!item) continue; | |||
item->setPos(d.x, d.y); | |||
item->setRegisterId(d.registerId); | |||
connect(item, &Item::requestCopy, ui->graphicsView, &MyGraphicsView::onItemRequestCopy); | |||
connect(item, &Item::requestDelete, ui->graphicsView, &MyGraphicsView::onItemRequestDelete); | |||
connect(item, &Item::requestBindRegister, ui->graphicsView, &MyGraphicsView::onItemRequestBindRegister); | |||
hmi_scene_->addItem(item); | |||
connect(item, &Item::requestCopy, views[pageIndex], &MyGraphicsView::onItemRequestCopy); | |||
connect(item, &Item::requestDelete, views[pageIndex], &MyGraphicsView::onItemRequestDelete); | |||
connect(item, &Item::requestBindRegister, views[pageIndex], &MyGraphicsView::onItemRequestBindRegister); | |||
scenes[pageIndex]->addItem(item); | |||
itemObjs.append(item); | |||
} | |||
} | |||
void HMI::extractSceneToProject(Project& proj) | |||
void HMI::extractSceneToProject(Project& proj, int pageIndex) | |||
{ | |||
if (pageIndex < 0 || pageIndex >= scenes.size()) return; | |||
proj.clear(); | |||
QList<Item*> items; | |||
for (QGraphicsItem* gi : hmi_scene_->items()) { | |||
if (Item* it = dynamic_cast<Item*>(gi)) { | |||
for (QGraphicsItem* gi : scenes[pageIndex]->items()){ | |||
if (Item* it = dynamic_cast<Item*>(gi)){ | |||
items.append(it); | |||
} | |||
} | |||
@@ -139,7 +186,7 @@ void HMI::extractSceneToProject(Project& proj) | |||
d.registerId = it->registerId(); | |||
proj.items_.append(d); | |||
} | |||
for (QGraphicsItem* gi : hmi_scene_->items()) { | |||
for (QGraphicsItem* gi : scenes[pageIndex]->items()) { | |||
if (Connection* conn = dynamic_cast<Connection*>(gi)) { | |||
int fromIdx = items.indexOf(conn->from_); | |||
int toIdx = items.indexOf(conn->to_); | |||
@@ -4,6 +4,7 @@ | |||
#include <QGraphicsScene> | |||
#include <QWidget> | |||
#include <project.h> | |||
#include "mygraphicsview.h" | |||
namespace Ui { | |||
class HMI; | |||
@@ -16,11 +17,13 @@ class HMI : public QWidget | |||
public: | |||
explicit HMI(QWidget *parent = nullptr); | |||
~HMI(); | |||
void newPage(); | |||
void createComponents(); | |||
void clearScene(); | |||
void applyProjectToScene(const Project& proj); | |||
void extractSceneToProject(Project& proj); | |||
void applyProjectToScene(const Project& proj, int pageIndex = 0); // 添加页面索引参数 | |||
void extractSceneToProject(Project& proj, int pageIndex = 0); // 添加页面索引参数 | |||
int currentPageIndex() const; // 获取当前页面索引 | |||
protected: | |||
bool eventFilter(QObject *obj, QEvent *event) override; | |||
@@ -30,7 +33,8 @@ private slots: | |||
private: | |||
Ui::HMI *ui; | |||
QGraphicsScene *hmi_scene_; | |||
QList<QGraphicsScene*> scenes; // 存储多个场景 | |||
QList<MyGraphicsView*> views; // 存储多个视图 | |||
QString selectedComponentType; | |||
}; | |||
@@ -19,7 +19,7 @@ | |||
<x>10</x> | |||
<y>30</y> | |||
<width>171</width> | |||
<height>451</height> | |||
<height>431</height> | |||
</rect> | |||
</property> | |||
</widget> | |||
@@ -29,44 +29,14 @@ | |||
<x>190</x> | |||
<y>10</y> | |||
<width>781</width> | |||
<height>481</height> | |||
<height>451</height> | |||
</rect> | |||
</property> | |||
<property name="currentIndex"> | |||
<number>0</number> | |||
<number>-1</number> | |||
</property> | |||
<widget class="QWidget" name="tab"> | |||
<attribute name="title"> | |||
<string>Tab 1</string> | |||
</attribute> | |||
<widget class="MyGraphicsView" name="graphicsView"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>0</x> | |||
<y>0</y> | |||
<width>761</width> | |||
<height>451</height> | |||
</rect> | |||
</property> | |||
<property name="dragMode"> | |||
<enum>QGraphicsView::NoDrag</enum> | |||
</property> | |||
</widget> | |||
</widget> | |||
<widget class="QWidget" name="tab_2"> | |||
<attribute name="title"> | |||
<string>Tab 2</string> | |||
</attribute> | |||
</widget> | |||
</widget> | |||
</widget> | |||
<customwidgets> | |||
<customwidget> | |||
<class>MyGraphicsView</class> | |||
<extends>QGraphicsView</extends> | |||
<header>mygraphicsview.h</header> | |||
</customwidget> | |||
</customwidgets> | |||
<resources/> | |||
<connections/> | |||
</ui> |
@@ -35,7 +35,7 @@ MainWindow::MainWindow(QWidget *parent) | |||
connect(ui->action_disconnect, &QAction::triggered, this, &MainWindow::disconnection); | |||
connect(modbusManager, &ModbusManager::connectionStatusChanged, | |||
plc_, &PLC::updateConnectionStatus); | |||
this, &MainWindow::updateConnectionStatus); | |||
plc_->applyProjectToScene(plcProject_); // 初始空 | |||
@@ -71,9 +71,11 @@ void MainWindow::newProject() | |||
plc_->clearScene(); | |||
setWindowTitle("未命名项目 - PLC编辑器"); | |||
} else { | |||
hmiProject_.clear(); | |||
hmi_->newPage(); | |||
hmiProject_.clear(); // 清除项目数据 | |||
hmiFilePath_.clear(); | |||
hmi_->clearScene(); | |||
// 更新窗口标题 | |||
setWindowTitle("未命名项目 - HMI编辑器"); | |||
} | |||
} | |||
@@ -99,14 +101,20 @@ void MainWindow::openProject() | |||
{ | |||
QString filePath = QFileDialog::getOpenFileName(this, "打开项目", "", "项目文件 (*.hmiproj)"); | |||
if (filePath.isEmpty()) return; | |||
if (hmiProject_.loadFromFile(filePath)) | |||
{ | |||
Project newProject; | |||
if (newProject.loadFromFile(filePath)) { | |||
// 创建新页面 | |||
hmi_->newPage(); | |||
// 应用项目到新页面 | |||
int newPageIndex = hmi_->currentPageIndex(); | |||
hmi_->applyProjectToScene(newProject, newPageIndex); | |||
// 保存文件路径 | |||
hmiFilePath_ = filePath; | |||
hmi_->applyProjectToScene(hmiProject_); | |||
setWindowTitle(QFileInfo(hmiFilePath_).fileName() + " - HMI编辑器"); | |||
} | |||
else | |||
{ | |||
} else { | |||
QMessageBox::critical(this, "错误", "无法打开HMI项目文件"); | |||
} | |||
} | |||
@@ -134,7 +142,10 @@ void MainWindow::saveProject() | |||
filePath += ".hmiproj"; | |||
hmiFilePath_ = filePath; | |||
} | |||
hmi_->extractSceneToProject(hmiProject_); | |||
// 从当前页面提取项目 | |||
hmi_->extractSceneToProject(hmiProject_, hmi_->currentPageIndex()); | |||
if (!hmiProject_.saveToFile(hmiFilePath_)) { | |||
QMessageBox::critical(this, "错误", "保存项目失败"); | |||
} | |||
@@ -152,3 +163,15 @@ void MainWindow::disconnection() | |||
modbusManager->disconnectDevice(); | |||
modbusManager->stopSimulation(); | |||
} | |||
void MainWindow::updateConnectionStatus(bool connection) | |||
{ | |||
if (connection) | |||
{ | |||
ui->textEdit->append("连接"); | |||
} | |||
else | |||
{ | |||
ui->textEdit->append("断开"); | |||
} | |||
} |
@@ -28,6 +28,7 @@ private slots: | |||
void saveProject(); | |||
void connection(); | |||
void disconnection(); | |||
void updateConnectionStatus(bool connection); | |||
private: | |||
Ui::MainWindow *ui; | |||
@@ -20,7 +20,17 @@ | |||
<x>10</x> | |||
<y>0</y> | |||
<width>981</width> | |||
<height>561</height> | |||
<height>511</height> | |||
</rect> | |||
</property> | |||
</widget> | |||
<widget class="QTextEdit" name="textEdit"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>20</x> | |||
<y>470</y> | |||
<width>951</width> | |||
<height>141</height> | |||
</rect> | |||
</property> | |||
</widget> | |||
@@ -237,14 +237,14 @@ void PLC::btnInsertClicked() | |||
ui->graphicsView->scene()->addItem(c2); | |||
} | |||
void PLC::updateConnectionStatus(bool connection) | |||
{ | |||
if (connection) | |||
{ | |||
ui->textEdit->append("连接"); | |||
} | |||
else | |||
{ | |||
ui->textEdit->append("断开"); | |||
} | |||
} | |||
//void PLC::updateConnectionStatus(bool connection) | |||
//{ | |||
// if (connection) | |||
// { | |||
// ui->textEdit->append("连接"); | |||
// } | |||
// else | |||
// { | |||
// ui->textEdit->append("断开"); | |||
// } | |||
//} |
@@ -30,7 +30,7 @@ public slots: | |||
void onListwidgetCurrenttextchanged(const QString ¤tText); | |||
void btnInsertClicked(); | |||
void updateConnectionStatus(bool connection); | |||
// void updateConnectionStatus(bool connection); | |||
private: | |||
Ui::PLC *ui; | |||
@@ -19,7 +19,7 @@ | |||
<x>10</x> | |||
<y>30</y> | |||
<width>171</width> | |||
<height>401</height> | |||
<height>431</height> | |||
</rect> | |||
</property> | |||
</widget> | |||
@@ -29,7 +29,7 @@ | |||
<x>200</x> | |||
<y>30</y> | |||
<width>761</width> | |||
<height>401</height> | |||
<height>431</height> | |||
</rect> | |||
</property> | |||
<property name="dragMode"> | |||
@@ -49,16 +49,6 @@ | |||
<string>插入</string> | |||
</property> | |||
</widget> | |||
<widget class="QTextEdit" name="textEdit"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>10</x> | |||
<y>440</y> | |||
<width>951</width> | |||
<height>87</height> | |||
</rect> | |||
</property> | |||
</widget> | |||
</widget> | |||
<customwidgets> | |||
<customwidget> | |||