2226 lines
72 KiB
C++
Executable File
2226 lines
72 KiB
C++
Executable File
/**
|
||
* @file mainwindow.cpp
|
||
* @author jinchao
|
||
* @brief 主窗口类实现
|
||
* @version 1.0
|
||
* @date 2025-02-14
|
||
*
|
||
* @copyright Copyright (c) 2025 COMAC
|
||
*
|
||
*/
|
||
#include "mainwindow.h"
|
||
#include "./ui_mainwindow.h"
|
||
#include <QFileDialog>
|
||
#include <QFile>
|
||
#include <QTextStream>
|
||
#include <QMessageBox>
|
||
#include <QXmlStreamReader>
|
||
#include <QHBoxLayout>
|
||
#include <QTreeWidget>
|
||
#include <QSplitter>
|
||
#include <QGroupBox>
|
||
#include <QLabel>
|
||
#include <QGridLayout>
|
||
#include <QTableWidget>
|
||
#include <QCheckBox>
|
||
#include <QHeaderView>
|
||
#include <QPushButton>
|
||
#include <QInputDialog>
|
||
#include <QFileInfo>
|
||
#include <QXmlStreamWriter>
|
||
#include <QDir>
|
||
#include <QTimer>
|
||
#include <QDateTimeEdit>
|
||
#include <QSet>
|
||
#include <algorithm>
|
||
#include <QSharedPointer>
|
||
#include <QStack>
|
||
#include <QRegularExpression>
|
||
#include <QDebug>
|
||
#include <QDomDocument>
|
||
#include <QDomElement>
|
||
#include <QDomNode>
|
||
#include <QDomNamedNodeMap>
|
||
|
||
//构造函数
|
||
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
|
||
{
|
||
ui->setupUi(this);
|
||
isModified = false; // 初始化
|
||
|
||
// 设置窗口标题
|
||
setWindowTitle("XNEditor");
|
||
// 设置窗口图标
|
||
setWindowIcon(QIcon(":/icon/XNEditor.png"));
|
||
|
||
// 设置窗口大小
|
||
resize(700, 1000);
|
||
|
||
// 初始化菜单栏
|
||
initMenu();
|
||
|
||
//初始化中央控件
|
||
initCentralWidget(0);
|
||
}
|
||
|
||
// 构造函数
|
||
MainWindow::MainWindow(QString fileName, QWidget *parent)
|
||
: QMainWindow(parent), ui(new Ui::MainWindow)
|
||
{
|
||
ui->setupUi(this);
|
||
|
||
// 设置窗口标题
|
||
setWindowTitle("XNEditor");
|
||
// 设置窗口图标
|
||
setWindowIcon(QIcon(":/icon/XNEditor.png"));
|
||
|
||
// 设置窗口大小
|
||
resize(500, 1000);
|
||
|
||
// 初始化菜单栏
|
||
initMenu();
|
||
|
||
// 设置当前文件路径
|
||
currentFilePath = fileName;
|
||
isModified = false;
|
||
|
||
// 根据文件类型初始化中央控件
|
||
if (currentFilePath.endsWith(".mcfg")) { // 模型配置文件
|
||
initCentralWidget(1);
|
||
currentFileType = 1;
|
||
// 打开模型配置文件
|
||
openModelFile();
|
||
} else if (currentFilePath.endsWith(".scfg")) { // 服务配置文件
|
||
initCentralWidget(1);
|
||
currentFileType = 2;
|
||
// 打开服务配置文件
|
||
openServiceFile();
|
||
} else if (currentFilePath.endsWith(".idl")) { // IDL文件
|
||
initCentralWidget(2);
|
||
currentFileType = 3;
|
||
// 打开IDL文件
|
||
openIDLFile();
|
||
}
|
||
}
|
||
|
||
// 析构函数
|
||
MainWindow::~MainWindow()
|
||
{
|
||
delete ui;
|
||
}
|
||
|
||
// 初始化菜单栏
|
||
void MainWindow::initMenu()
|
||
{
|
||
// 初始化文件菜单
|
||
initFileMenu();
|
||
//初始化编辑菜单
|
||
initEditMenu();
|
||
}
|
||
|
||
// 初始化文件菜单
|
||
void MainWindow::initFileMenu()
|
||
{
|
||
// 获取菜单栏
|
||
QMenuBar *menuBar = ui->menubar;
|
||
|
||
// 设置菜单
|
||
QMenu *fileMenu = new QMenu("&File", this);
|
||
menuBar->addMenu(fileMenu);
|
||
|
||
// New菜单项
|
||
QAction *newAction = new QAction("&New Scenario", this);
|
||
fileMenu->addAction(newAction);
|
||
newAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_N));
|
||
connect(newAction, &QAction::triggered, this, &MainWindow::onNewFileTriggered);
|
||
|
||
// 添加分隔
|
||
fileMenu->addSeparator();
|
||
|
||
// Open菜单项
|
||
QAction *openAction = new QAction("&Open", this);
|
||
openAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_O));
|
||
fileMenu->addAction(openAction);
|
||
connect(openAction, &QAction::triggered, this, &MainWindow::onOpenFileTriggered);
|
||
|
||
// 添加分隔
|
||
fileMenu->addSeparator();
|
||
|
||
// Save菜单项
|
||
QAction *saveAction = new QAction("&Save", this);
|
||
fileMenu->addAction(saveAction);
|
||
saveAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
|
||
connect(saveAction, &QAction::triggered, [this]() {
|
||
if (currentFilePath.isEmpty()) {
|
||
return;
|
||
}
|
||
//保存文件
|
||
saveFile(currentFilePath);
|
||
});
|
||
|
||
//Save As 菜单项
|
||
QAction *saveAsAction = new QAction("Save As...", this);
|
||
fileMenu->addAction(saveAsAction);
|
||
saveAsAction->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_S));
|
||
connect(saveAsAction, &QAction::triggered, [this]() {
|
||
// 打开选择保存路径对话框
|
||
QString filePath = QFileDialog::getSaveFileName(
|
||
this, tr("Save As"), "", tr("Scenario Files (*.sce);;All Files (*)"));
|
||
if (!filePath.isEmpty()) {
|
||
currentFilePath = filePath;
|
||
//保存文件
|
||
saveFile(filePath);
|
||
}
|
||
});
|
||
|
||
// 添加分隔
|
||
fileMenu->addSeparator();
|
||
|
||
// Close 菜单项
|
||
QAction *closeAction = new QAction("&Close File", this);
|
||
fileMenu->addAction(closeAction);
|
||
closeAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_W));
|
||
connect(closeAction, &QAction::triggered, this, &MainWindow::onCloseFileTriggered);
|
||
|
||
// 添加分隔
|
||
fileMenu->addSeparator();
|
||
|
||
// Exit 菜单项
|
||
QAction *exitAction = new QAction("&Exit", this);
|
||
fileMenu->addAction(exitAction);
|
||
exitAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q));
|
||
connect(exitAction, &QAction::triggered, [this]() { close(); });
|
||
}
|
||
|
||
// 初始化编辑菜单
|
||
void MainWindow::initEditMenu()
|
||
{
|
||
// 获取菜单栏
|
||
QMenuBar *menuBar = ui->menubar;
|
||
//TODO 添加编辑相关菜单项
|
||
}
|
||
|
||
// 初始化中心组件
|
||
void MainWindow::initCentralWidget(int style)
|
||
{
|
||
// 清理旧中心组件
|
||
QWidget *oldCentralWidget = centralWidget();
|
||
if (oldCentralWidget) {
|
||
delete oldCentralWidget;
|
||
}
|
||
|
||
// 新建新中心组件
|
||
QWidget *centralWidget = new QWidget(this);
|
||
setCentralWidget(centralWidget);
|
||
|
||
// 中心组件水平布局
|
||
QHBoxLayout *layout = new QHBoxLayout(centralWidget);
|
||
|
||
if (style == 0) { //如果是运行环境配置文件类型
|
||
// 创建一个水平分割器
|
||
QSplitter *splitter = new QSplitter(Qt::Horizontal, centralWidget);
|
||
splitter->setObjectName("splitter");
|
||
layout->addWidget(splitter);
|
||
|
||
// 创建左边窗口
|
||
QWidget *leftWidget = new QWidget(splitter);
|
||
leftWidget->setObjectName("leftWidget");
|
||
//左侧窗口垂直布局
|
||
QVBoxLayout *leftLayout = new QVBoxLayout(leftWidget);
|
||
// 创建模型配置文件树型控件
|
||
QTreeWidget *ModelWidget = new QTreeWidget(leftWidget);
|
||
ModelWidget->setObjectName("ModelWidget");
|
||
ModelWidget->setHeaderLabels(QStringList() << "Models");
|
||
leftLayout->addWidget(ModelWidget);
|
||
// 创建服务配置文件树型控件
|
||
QTreeWidget *ServiceWidget = new QTreeWidget(leftWidget);
|
||
ServiceWidget->setObjectName("ServiceWidget");
|
||
ServiceWidget->setHeaderLabels(QStringList() << "Services");
|
||
leftLayout->addWidget(ServiceWidget);
|
||
// 为模型配置文件树型控件添加右键菜单
|
||
ModelWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||
connect(ModelWidget, &QTreeWidget::customContextMenuRequested, this,
|
||
[=](const QPoint &pos) {
|
||
//获取点击右键时选中的元素
|
||
QTreeWidgetItem *item = ModelWidget->itemAt(pos);
|
||
if (item) {
|
||
//新建右键菜单
|
||
QMenu contextMenu;
|
||
//添加在新窗口打开的选项
|
||
QAction *openAction = new QAction("Open in New Window", this);
|
||
contextMenu.addAction(openAction);
|
||
// 链接打开选项的槽
|
||
connect(openAction, &QAction::triggered, this, [this, item]() {
|
||
//获取选中元素的文件名
|
||
QString fileName = modelPath + "/" + item->text(0);
|
||
//在新窗口打开文件
|
||
openFileInNewWindow(fileName);
|
||
});
|
||
//显示右键菜单
|
||
contextMenu.exec(ModelWidget->mapToGlobal(pos));
|
||
}
|
||
});
|
||
|
||
// 为服务配置文件树型控件添加右键菜单
|
||
ServiceWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||
connect(ServiceWidget, &QTreeWidget::customContextMenuRequested, this,
|
||
[=](const QPoint &pos) {
|
||
//获取点击右键时选中的元素
|
||
QTreeWidgetItem *item = ServiceWidget->itemAt(pos);
|
||
if (item) {
|
||
//新建右键菜单
|
||
QMenu contextMenu;
|
||
//添加在新窗口打开的选项
|
||
QAction *openAction = new QAction("Open in New Window", this);
|
||
contextMenu.addAction(openAction);
|
||
// 链接打开选项的槽
|
||
connect(openAction, &QAction::triggered, this, [this, item]() {
|
||
//获取选中元素的文件名
|
||
QString fileName = ServicePath + "/" + item->text(0);
|
||
//在新窗口打开文件
|
||
openFileInNewWindow(fileName);
|
||
});
|
||
//显示右键菜单
|
||
contextMenu.exec(ServiceWidget->mapToGlobal(pos));
|
||
}
|
||
});
|
||
|
||
// 将左边窗口添加到分割器
|
||
splitter->addWidget(leftWidget);
|
||
|
||
// 创建右边窗口
|
||
QWidget *rightWidget = new QWidget(splitter);
|
||
rightWidget->setObjectName("rightWidget");
|
||
//右侧窗口垂直布局
|
||
QVBoxLayout *rightLayout = new QVBoxLayout(rightWidget);
|
||
rightLayout->setObjectName("rightLayout");
|
||
|
||
// 将右边窗口添加到分割器
|
||
splitter->addWidget(rightWidget);
|
||
|
||
// 设置分割器两侧大小
|
||
splitter->setSizes({200, 400});
|
||
} else if (style == 1) { //如果是模型/服务配置文件类型
|
||
// 创建右边窗口
|
||
QWidget *rightWidget = new QWidget(centralWidget);
|
||
rightWidget->setObjectName("rightWidget");
|
||
//右侧窗口垂直布局
|
||
QVBoxLayout *rightLayout = new QVBoxLayout(rightWidget);
|
||
rightLayout->setObjectName("rightLayout");
|
||
|
||
layout->addWidget(rightWidget);
|
||
} else if (style == 2) { //如果是IDL文件类型
|
||
// 创建一个水平分割器
|
||
QSplitter *splitter = new QSplitter(Qt::Horizontal, centralWidget);
|
||
splitter->setObjectName("splitter");
|
||
layout->addWidget(splitter);
|
||
|
||
// 创建左边窗口,为树型控件,显示IDL结构
|
||
QTreeWidget *leftWidget = new QTreeWidget(splitter);
|
||
leftWidget->setObjectName("leftWidget");
|
||
// 初始化该树型控件
|
||
initIDLTreeWidget();
|
||
// 将左边窗口添加到分割器
|
||
splitter->addWidget(leftWidget);
|
||
|
||
// 创建右边窗口,为表格控件,显示结构体内容
|
||
QTableWidget *rightWidget = new QTableWidget(splitter);
|
||
rightWidget->setObjectName("rightWidget");
|
||
// 设置表格为4列
|
||
rightWidget->setColumnCount(4);
|
||
// 设置列标题
|
||
QStringList headers;
|
||
headers << "Name" << "Type" << "Size" << "Description";
|
||
rightWidget->setHorizontalHeaderLabels(headers);
|
||
// 设置表格只能单选
|
||
rightWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
// 设置表头自动伸缩
|
||
rightWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||
|
||
// 将右边窗口添加到分割器
|
||
splitter->addWidget(rightWidget);
|
||
|
||
// 设置分割器两侧大小
|
||
splitter->setSizes({200, 400}); // Adjust these values as needed
|
||
}
|
||
}
|
||
|
||
// 初始化IDL文件结构窗口
|
||
void MainWindow::initIDLTreeWidget()
|
||
{
|
||
// 获取左边窗口
|
||
QTreeWidget *leftWidget = findChild<QTreeWidget *>("leftWidget");
|
||
if (leftWidget) {
|
||
// 设置右键菜单策略
|
||
leftWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||
|
||
// 连接右键菜单信号
|
||
connect(
|
||
leftWidget, &QTreeWidget::customContextMenuRequested, this,
|
||
[this, leftWidget](const QPoint &pos) {
|
||
// 获取点击右键时选择的元素
|
||
QTreeWidgetItem *item = leftWidget->itemAt(pos);
|
||
QMenu contextMenu;
|
||
if (!item) { // 在空白处右键,只能添加顶层namespace
|
||
// 新建添加namespace的选项
|
||
QAction *addNamespaceAction = new QAction("Add Module", this);
|
||
// 链接添加namespace的槽
|
||
connect(addNamespaceAction, &QAction::triggered, [this, leftWidget]() {
|
||
bool ok;
|
||
QString name;
|
||
do {
|
||
// 打开输入namespace名称的对话框
|
||
name = QInputDialog::getText(
|
||
this, tr("Add Module"),
|
||
tr("Module name (must be a valid C++ identifier):"),
|
||
QLineEdit::Normal, "", &ok);
|
||
|
||
if (!ok)
|
||
return; // 用户点击取消
|
||
|
||
// 只能输入c++标准的类名
|
||
QRegularExpression regex("^[A-Za-z_][A-Za-z0-9_]*$");
|
||
if (!regex.match(name).hasMatch()) {
|
||
// 如果输入的名称不符合c++标准的类名,则提示错误
|
||
QMessageBox::warning(
|
||
this, tr("Invalid Name"),
|
||
tr("Module name must start with a letter or underscore and "
|
||
"contain only letters, numbers and underscores."));
|
||
ok = false;
|
||
}
|
||
} while (!ok || name.isEmpty());
|
||
// 新建一个namespace
|
||
QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList() << name);
|
||
// 将namespace添加到树型控件
|
||
leftWidget->addTopLevelItem(newItem);
|
||
// 将namespace添加到存储好的结构中
|
||
NamespaceDefinition newNamespace;
|
||
newNamespace.namespaceName = name;
|
||
namespaces.push_back(QSharedPointer<NamespaceDefinition>(&newNamespace));
|
||
// 设置修改状态
|
||
isModified = true;
|
||
// 更新窗口标题
|
||
updateWindowTitle();
|
||
});
|
||
contextMenu.addAction(addNamespaceAction);
|
||
} else { //如果不是空白处
|
||
//计算在第几层
|
||
int level = 0;
|
||
QTreeWidgetItem *parent = item;
|
||
while (parent->parent()) {
|
||
parent = parent->parent();
|
||
level++;
|
||
}
|
||
|
||
if (level < 2) { // 第一层或第二层可以添加module和struct
|
||
// 新建添加module的选项
|
||
QAction *addNamespaceAction = new QAction("Add Module", this);
|
||
// 链接添加module的槽
|
||
connect(addNamespaceAction, &QAction::triggered, [this, item]() {
|
||
bool ok;
|
||
QString name;
|
||
do {
|
||
// 打开输入namespace名称的对话框
|
||
name = QInputDialog::getText(
|
||
this, tr("Add Module"),
|
||
tr("Module name (must be a valid C++ identifier):"),
|
||
QLineEdit::Normal, "", &ok);
|
||
|
||
if (!ok)
|
||
return; // 用户点击取消
|
||
// 只能输入c++标准的类名
|
||
QRegularExpression regex("^[A-Za-z_][A-Za-z0-9_]*$");
|
||
if (!regex.match(name).hasMatch()) {
|
||
QMessageBox::warning(
|
||
this, tr("Invalid Name"),
|
||
tr("Module name must start with a letter or underscore and "
|
||
"contain only letters, numbers and underscores."));
|
||
ok = false;
|
||
}
|
||
} while (!ok || name.isEmpty());
|
||
// 将namespace添加为该元素的子元素
|
||
QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList() << name);
|
||
item->addChild(newItem);
|
||
item->setExpanded(true);
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
});
|
||
contextMenu.addAction(addNamespaceAction);
|
||
// 新建添加Struct的选项
|
||
QAction *addStructAction = new QAction("Add Struct", this);
|
||
// 链接添加Struct的槽
|
||
connect(addStructAction, &QAction::triggered, [this, item]() {
|
||
bool ok;
|
||
QString name;
|
||
do {
|
||
// 打开输入Struct名称的对话框
|
||
name = QInputDialog::getText(
|
||
this, tr("Add Struct"),
|
||
tr("Struct name (must be a valid C++ identifier):"),
|
||
QLineEdit::Normal, "", &ok);
|
||
|
||
if (!ok)
|
||
return; // 用户点击取消
|
||
// 只能输入c++标准的类名
|
||
QRegularExpression regex("^[A-Za-z_][A-Za-z0-9_]*$");
|
||
if (!regex.match(name).hasMatch()) {
|
||
QMessageBox::warning(
|
||
this, tr("Invalid Name"),
|
||
tr("Struct name must start with a letter or underscore and "
|
||
"contain only letters, numbers and underscores."));
|
||
ok = false;
|
||
}
|
||
} while (!ok || name.isEmpty());
|
||
// 新建一个Struct
|
||
QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList() << name);
|
||
// 将Struct添加为该元素的子元素
|
||
item->addChild(newItem);
|
||
item->setExpanded(true);
|
||
// 设置修改状态
|
||
isModified = true;
|
||
// 更新窗口标题
|
||
updateWindowTitle();
|
||
});
|
||
contextMenu.addAction(addStructAction);
|
||
} else if (level == 2) { // 第三层只能添加struct
|
||
// 新建添加Struct的选项
|
||
QAction *addStructAction = new QAction("Add Struct", this);
|
||
// 链接添加Struct的槽
|
||
connect(addStructAction, &QAction::triggered, [this, item]() {
|
||
bool ok;
|
||
QString name;
|
||
do {
|
||
// 打开输入Struct名称的对话框
|
||
name = QInputDialog::getText(
|
||
this, tr("Add Struct"),
|
||
tr("Struct name (must be a valid C++ identifier):"),
|
||
QLineEdit::Normal, "", &ok);
|
||
|
||
if (!ok)
|
||
return; // 用户点击取消
|
||
// 只能输入c++标准的类名
|
||
QRegularExpression regex("^[A-Za-z_][A-Za-z0-9_]*$");
|
||
if (!regex.match(name).hasMatch()) {
|
||
QMessageBox::warning(
|
||
this, tr("Invalid Name"),
|
||
tr("Struct name must start with a letter or underscore and "
|
||
"contain only letters, numbers and underscores."));
|
||
ok = false;
|
||
}
|
||
} while (!ok || name.isEmpty());
|
||
// 新建一个Struct
|
||
QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList() << name);
|
||
// 将Struct添加为该元素的子元素
|
||
item->addChild(newItem);
|
||
item->setExpanded(true);
|
||
// 设置修改状态
|
||
isModified = true;
|
||
// 更新窗口标题
|
||
updateWindowTitle();
|
||
});
|
||
contextMenu.addAction(addStructAction);
|
||
}
|
||
|
||
// 所有层级都可以删除
|
||
contextMenu.addSeparator();
|
||
QAction *deleteAction = new QAction("Delete", this);
|
||
connect(deleteAction, &QAction::triggered, [this, item]() {
|
||
delete item;
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
});
|
||
contextMenu.addAction(deleteAction);
|
||
}
|
||
|
||
if (!contextMenu.actions().isEmpty()) {
|
||
contextMenu.exec(leftWidget->mapToGlobal(pos));
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 新建文件
|
||
void MainWindow::onNewFileTriggered()
|
||
{
|
||
// 如果文件被修改,则先关闭文件
|
||
if (isModified) {
|
||
// 关闭文件
|
||
onCloseFileTriggered();
|
||
}
|
||
// 打开新建环境配置文件对话框
|
||
QString filePath = QFileDialog::getSaveFileName(this, tr("New Scenario"), "",
|
||
tr("Scenario Files (*.sce);;All Files (*)"));
|
||
// 如果文件路径不为空
|
||
if (!filePath.isEmpty()) {
|
||
// 打开模板文件
|
||
QFile templateFile("template/Template.sce.tmp");
|
||
// 如果模板文件存在
|
||
if (templateFile.exists()) {
|
||
// 打开模板文件
|
||
if (templateFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||
QFile newFile(filePath);
|
||
if (newFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||
// 读取模板文件
|
||
QTextStream in(&templateFile);
|
||
// 写入新文件
|
||
QTextStream out(&newFile);
|
||
out << in.readAll();
|
||
// 关闭模板文件
|
||
templateFile.close();
|
||
// 关闭新文件
|
||
newFile.close();
|
||
// 设置当前文件路径
|
||
currentFilePath = filePath;
|
||
// 设置当前文件类型
|
||
currentFileType = 0;
|
||
// 打开环境配置文件
|
||
openScenarioFile();
|
||
} else {
|
||
// 如果新建文件失败,则提示错误
|
||
QMessageBox::warning(this, tr("Error"),
|
||
tr("Failed to create new scenario file."));
|
||
}
|
||
} else {
|
||
// 如果打开模板文件失败,则提示错误
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to open template file."));
|
||
}
|
||
} else {
|
||
// 如果模板文件不存在,则提示错误
|
||
QMessageBox::warning(this, tr("Error"), tr("Template file does not exist."));
|
||
}
|
||
}
|
||
}
|
||
|
||
// 打开文件
|
||
void MainWindow::onOpenFileTriggered()
|
||
{
|
||
// 如果文件被修改,则先关闭文件
|
||
if (isModified) {
|
||
// 关闭文件
|
||
onCloseFileTriggered();
|
||
}
|
||
// 打开打开文件对话框
|
||
QString filePath = QFileDialog::getOpenFileName(
|
||
this, tr("Open File"), "",
|
||
tr("All Supported Files (*.sce *.xml *.mcfg *.scfg *.idl);;All Files (*)"));
|
||
// 如果文件路径不为空
|
||
if (!filePath.isEmpty()) {
|
||
// 设置当前文件路径
|
||
currentFilePath = filePath;
|
||
if (filePath.endsWith(".idl")) { //如果是IDL文件
|
||
currentFileType = 3;
|
||
// 重新初始化布局
|
||
initCentralWidget(2);
|
||
// 打开IDL文件
|
||
openIDLFile();
|
||
return;
|
||
}
|
||
// 打开文件
|
||
QFile file(filePath);
|
||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||
// 读取文件
|
||
QXmlStreamReader xmlReader(&file);
|
||
while (!xmlReader.atEnd() && !xmlReader.hasError()) {
|
||
// 读取下一个元素
|
||
QXmlStreamReader::TokenType token = xmlReader.readNext();
|
||
if (token == QXmlStreamReader::StartElement) {
|
||
// 获取元素名称
|
||
QString rootElement = xmlReader.name().toString();
|
||
if (rootElement == "Scenario") { //如果是环境配置文件
|
||
currentFileType = 0;
|
||
resize(700, 1000);
|
||
initCentralWidget(0); // 重新初始化布局
|
||
file.close();
|
||
// 打开环境配置文件
|
||
openScenarioFile();
|
||
return;
|
||
} else if (rootElement == "Model") { //如果是模型文件
|
||
currentFileType = 1;
|
||
resize(500, 1000);
|
||
initCentralWidget(1); // 重新初始化布局
|
||
file.close();
|
||
// 打开模型文件
|
||
openModelFile();
|
||
return;
|
||
} else if (rootElement == "Service") { //如果是服务文件
|
||
currentFileType = 2;
|
||
resize(500, 1000);
|
||
initCentralWidget(1); // 重新初始化布局
|
||
file.close();
|
||
// 打开服务文件
|
||
openServiceFile();
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
file.close();
|
||
} else {
|
||
// 如果打开文件失败,则提示错误
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to open file."));
|
||
}
|
||
}
|
||
}
|
||
|
||
// 关闭文件
|
||
void MainWindow::onCloseFileTriggered()
|
||
{
|
||
if (isModified) { // 只在文件被修改时提示保存
|
||
// 提示保存
|
||
QMessageBox::StandardButton reply;
|
||
reply = QMessageBox::question(this, tr("Save Changes"),
|
||
tr("Do you want to save changes to the current file?"),
|
||
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
||
if (reply == QMessageBox::Yes) { // 如果选择保存
|
||
if (currentFilePath.isEmpty()) { // 如果当前文件路径为空
|
||
// 打开保存文件对话框
|
||
QString filePath =
|
||
QFileDialog::getSaveFileName(this, tr("Save As"), "", tr("All Files (*)"));
|
||
if (!filePath.isEmpty()) { // 如果文件路径不为空
|
||
// 保存文件
|
||
saveFile(filePath);
|
||
}
|
||
} else {
|
||
// 保存文件
|
||
saveFile(currentFilePath);
|
||
}
|
||
} else if (reply == QMessageBox::Cancel) { // 如果选择取消
|
||
return;
|
||
}
|
||
}
|
||
// 清除内容
|
||
QTreeWidget *ModelWidget = findChild<QTreeWidget *>("ModelWidget");
|
||
if (ModelWidget) {
|
||
ModelWidget->clear();
|
||
}
|
||
QTreeWidget *ServiceWidget = findChild<QTreeWidget *>("ServiceWidget");
|
||
if (ServiceWidget) {
|
||
ServiceWidget->clear();
|
||
}
|
||
QVBoxLayout *layout = findChild<QVBoxLayout *>("rightLayout");
|
||
if (layout) {
|
||
QLayoutItem *child;
|
||
while ((child = layout->takeAt(0)) != nullptr) {
|
||
if (child->widget()) {
|
||
child->widget()->setParent(nullptr);
|
||
}
|
||
delete child;
|
||
}
|
||
}
|
||
isModified = false; // 重置修改状态
|
||
currentFilePath = "";
|
||
currentFileType = 0;
|
||
// 更新窗口标题
|
||
updateWindowTitle();
|
||
}
|
||
|
||
// 打开场景配置文件
|
||
void MainWindow::openScenarioFile()
|
||
{
|
||
// 获取右布局
|
||
QVBoxLayout *layout = findChild<QVBoxLayout *>("rightLayout");
|
||
if (!layout) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Software error."));
|
||
return;
|
||
}
|
||
|
||
// 打开文件
|
||
QFile file(currentFilePath);
|
||
if (!file.open(QIODevice::ReadOnly)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to open file."));
|
||
return;
|
||
}
|
||
|
||
// 创建DOM文档
|
||
QDomDocument doc;
|
||
if (!doc.setContent(&file)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to parse scenario file."));
|
||
file.close();
|
||
return;
|
||
}
|
||
|
||
// 重置模型路径和服务路径
|
||
modelPath = "";
|
||
ServicePath = "";
|
||
|
||
// 获取根元素
|
||
QDomElement root = doc.documentElement();
|
||
if (root.tagName() == "Scenario") {
|
||
// 遍历子元素
|
||
QDomNode node = root.firstChild();
|
||
while (!node.isNull()) {
|
||
QDomElement element = node.toElement();
|
||
if (!element.isNull()) {
|
||
QString elementName = element.tagName();
|
||
if (elementName == "Environment") {
|
||
QDomNamedNodeMap elementAttributes = element.attributes();
|
||
addGroupBox(layout, elementName, elementAttributes);
|
||
} else if (elementName == "ConsoleOutput" || elementName == "Log") {
|
||
// 添加控制台输出/日志组框
|
||
QDomNamedNodeMap elementAttributes = element.attributes();
|
||
addGroupBox2(layout, elementName, elementAttributes);
|
||
} else if (elementName == "ModelGroup") {
|
||
QDomNamedNodeMap elementAttributes = element.attributes();
|
||
// 处理模型列表
|
||
QVector<QDomNamedNodeMap> modelList;
|
||
QDomNodeList models = element.elementsByTagName("Model");
|
||
|
||
for (int i = 0; i < models.count(); i++) {
|
||
QDomElement modelElement = models.at(i).toElement();
|
||
QDomNamedNodeMap attributes = modelElement.attributes();
|
||
|
||
modelList.append(attributes);
|
||
}
|
||
|
||
// 添加模型列表组框
|
||
addGroupBox3(layout, elementName, elementAttributes, modelList, 0);
|
||
|
||
} else if (elementName == "ServicesList") {
|
||
QDomNamedNodeMap elementAttributes = element.attributes();
|
||
// 处理服务列表
|
||
QVector<QDomNamedNodeMap> serviceList;
|
||
QDomNodeList services = element.elementsByTagName("Service");
|
||
|
||
for (int i = 0; i < services.count(); i++) {
|
||
QDomElement serviceElement = services.at(i).toElement();
|
||
QDomNamedNodeMap attributes = serviceElement.attributes();
|
||
|
||
serviceList.append(attributes);
|
||
}
|
||
|
||
// 添加服务列表组框
|
||
addGroupBox3(layout, elementName, elementAttributes, serviceList, 1);
|
||
}
|
||
}
|
||
node = node.nextSibling();
|
||
}
|
||
|
||
// // 更新模型和服务列表
|
||
// QTableWidget *modelsTable = findChild<QTableWidget *>();
|
||
// if (modelsTable) {
|
||
// UpdateModelList(modelsTable);
|
||
// }
|
||
// QTableWidget *servicesTable = findChild<QTableWidget *>();
|
||
// if (servicesTable) {
|
||
// UpdateServiceList(servicesTable);
|
||
// }
|
||
|
||
updateWindowTitle();
|
||
}
|
||
file.close();
|
||
}
|
||
|
||
// 打开模型文件
|
||
void MainWindow::openModelFile()
|
||
{
|
||
// 获取右布局
|
||
QVBoxLayout *layout = findChild<QVBoxLayout *>("rightLayout");
|
||
if (!layout) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Software error."));
|
||
return;
|
||
}
|
||
|
||
// 打开文件
|
||
QFile file(currentFilePath);
|
||
if (!file.open(QIODevice::ReadOnly)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to open file."));
|
||
return;
|
||
}
|
||
|
||
// 创建DOM文档
|
||
QDomDocument doc;
|
||
if (!doc.setContent(&file)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to parse model config file."));
|
||
file.close();
|
||
return;
|
||
}
|
||
|
||
// 获取根元素
|
||
QDomElement root = doc.documentElement();
|
||
if (root.tagName() == "Model") {
|
||
// 添加组框
|
||
QGroupBox *groupBox = new QGroupBox("Model Info", this);
|
||
groupBox->setObjectName("Model Info");
|
||
layout->addWidget(groupBox);
|
||
QGridLayout *groupBoxLayout = new QGridLayout(groupBox);
|
||
groupBox->setLayout(groupBoxLayout);
|
||
|
||
// 遍历子元素
|
||
QDomNode node = root.firstChild();
|
||
while (!node.isNull()) {
|
||
QDomElement element = node.toElement();
|
||
if (!element.isNull()) {
|
||
QString elementName = element.tagName();
|
||
if (elementName == "Name" || elementName == "Description" || elementName == "Author"
|
||
|| elementName == "Version" || elementName == "Node"
|
||
|| elementName == "Priority" || elementName == "MathLib") {
|
||
// 添加基本信息
|
||
addItemIntoGroupBox(groupBoxLayout, elementName, element.text(), 0);
|
||
} else if (elementName == "CreateTime" || elementName == "ChangeTime") {
|
||
// 添加时间信息
|
||
addItemIntoGroupBox(groupBoxLayout, elementName, element.text(), 3);
|
||
} else if (elementName == "CommandList") {
|
||
// 处理命令列表
|
||
QVector<QDomNamedNodeMap> commandList;
|
||
QDomNodeList commands = element.elementsByTagName("Command");
|
||
|
||
for (int i = 0; i < commands.count(); i++) {
|
||
QDomElement commandElement = commands.at(i).toElement();
|
||
QDomNamedNodeMap attributes = commandElement.attributes();
|
||
commandList.append(attributes);
|
||
}
|
||
|
||
// 添加命令列表组框
|
||
addGroupBox3(layout, "CommandList", QDomNamedNodeMap(), commandList, 2);
|
||
} else {
|
||
QGroupBox *otherGroupBox = new QGroupBox("Other Info", this);
|
||
otherGroupBox->setObjectName("Other Info");
|
||
layout->addWidget(otherGroupBox);
|
||
QGridLayout *otherGroupBoxLayout = new QGridLayout(otherGroupBox);
|
||
otherGroupBox->setLayout(otherGroupBoxLayout);
|
||
|
||
// 递归处理其他自定义元素
|
||
std::function<void(QGridLayout *, QDomNode &)> processChildNodes =
|
||
[&](QGridLayout *subLayout, QDomNode &subNode) {
|
||
QDomElement subElement = subNode.toElement();
|
||
QString subElementName = subElement.nodeName();
|
||
if (subNode.hasChildNodes() && !subNode.firstChild().isText()) {
|
||
QGroupBox *groupBox = new QGroupBox(subElementName, this);
|
||
subLayout->addWidget(groupBox);
|
||
QGridLayout *subGroupBoxLayout = new QGridLayout(groupBox);
|
||
groupBox->setLayout(subGroupBoxLayout);
|
||
QDomNode childNode = subNode.firstChild();
|
||
while (!childNode.isNull()) {
|
||
processChildNodes(subGroupBoxLayout, childNode);
|
||
childNode = childNode.nextSibling();
|
||
}
|
||
addPlusButton(subGroupBoxLayout);
|
||
} else {
|
||
addItemIntoGroupBox(subLayout, subElementName, subElement.text(),
|
||
99);
|
||
}
|
||
};
|
||
processChildNodes(otherGroupBoxLayout, node);
|
||
}
|
||
}
|
||
node = node.nextSibling();
|
||
}
|
||
|
||
// 添加"+"按钮用于添加新属性
|
||
addPlusButton(groupBoxLayout);
|
||
updateWindowTitle();
|
||
}
|
||
file.close();
|
||
}
|
||
|
||
// 打开服务文件
|
||
void MainWindow::openServiceFile()
|
||
{
|
||
// 获取右布局
|
||
QVBoxLayout *layout = findChild<QVBoxLayout *>("rightLayout");
|
||
if (!layout) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Software error."));
|
||
return;
|
||
}
|
||
// 打开文件
|
||
QFile file(currentFilePath);
|
||
if (!file.open(QIODevice::ReadOnly)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to open file."));
|
||
return;
|
||
}
|
||
// 创建DOM文档
|
||
QDomDocument doc;
|
||
if (!doc.setContent(&file)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to parse service config file."));
|
||
file.close();
|
||
return;
|
||
}
|
||
// 获取根元素
|
||
QDomElement root = doc.documentElement();
|
||
if (root.tagName() == "Service") {
|
||
// 添加组框
|
||
QGroupBox *groupBox = new QGroupBox("Service Info", this);
|
||
groupBox->setObjectName("Service Info");
|
||
layout->addWidget(groupBox);
|
||
QGridLayout *groupBoxLayout = new QGridLayout(groupBox);
|
||
groupBox->setLayout(groupBoxLayout);
|
||
|
||
// 遍历子元素
|
||
QDomNode node = root.firstChild();
|
||
while (!node.isNull()) {
|
||
QDomElement element = node.toElement();
|
||
if (!element.isNull()) {
|
||
QString elementName = element.tagName();
|
||
if (elementName == "Name" || elementName == "Description" || elementName == "Author"
|
||
|| elementName == "Version" || elementName == "Priority") {
|
||
// 添加基本信息
|
||
addItemIntoGroupBox(groupBoxLayout, elementName, element.text(), 0);
|
||
} else if (elementName == "CreateTime" || elementName == "ChangeTime") {
|
||
// 添加时间信息
|
||
addItemIntoGroupBox(groupBoxLayout, elementName, element.text(), 3);
|
||
} else if (elementName == "CommandList") {
|
||
// 处理命令列表
|
||
QVector<QDomNamedNodeMap> commandList;
|
||
QDomNodeList commands = element.elementsByTagName("Command");
|
||
|
||
for (int i = 0; i < commands.count(); i++) {
|
||
QDomElement commandElement = commands.at(i).toElement();
|
||
QDomNamedNodeMap attributes = commandElement.attributes();
|
||
|
||
commandList.append(attributes);
|
||
}
|
||
|
||
// 添加命令列表组框
|
||
addGroupBox3(layout, "CommandList", QDomNamedNodeMap(), commandList, 2);
|
||
} else {
|
||
QGroupBox *otherGroupBox = new QGroupBox("Other Info", this);
|
||
otherGroupBox->setObjectName("Other Info");
|
||
layout->addWidget(otherGroupBox);
|
||
QGridLayout *otherGroupBoxLayout = new QGridLayout(otherGroupBox);
|
||
otherGroupBox->setLayout(otherGroupBoxLayout);
|
||
// 递归处理其他自定义元素
|
||
std::function<void(QGridLayout *, QDomNode &)> processChildNodes =
|
||
[&](QGridLayout *subLayout, QDomNode &subNode) {
|
||
QDomElement subElement = subNode.toElement();
|
||
QString subElementName = subElement.nodeName();
|
||
if (subNode.hasChildNodes() && !subNode.firstChild().isText()) {
|
||
QGroupBox *groupBox = new QGroupBox(subElementName, this);
|
||
subLayout->addWidget(groupBox);
|
||
QGridLayout *subGroupBoxLayout = new QGridLayout(groupBox);
|
||
groupBox->setLayout(subGroupBoxLayout);
|
||
QDomNode childNode = subNode.firstChild();
|
||
while (!childNode.isNull()) {
|
||
processChildNodes(subGroupBoxLayout, childNode);
|
||
childNode = childNode.nextSibling();
|
||
}
|
||
addPlusButton(subGroupBoxLayout);
|
||
} else {
|
||
addItemIntoGroupBox(subLayout, subElementName, subElement.text(),
|
||
99);
|
||
}
|
||
};
|
||
processChildNodes(otherGroupBoxLayout, node);
|
||
}
|
||
}
|
||
node = node.nextSibling();
|
||
}
|
||
|
||
// 添加"+"按钮用于添加新属性
|
||
addPlusButton(groupBoxLayout);
|
||
updateWindowTitle();
|
||
}
|
||
file.close();
|
||
}
|
||
|
||
void MainWindow::addPlusButton(QGridLayout *layout)
|
||
{
|
||
QPushButton *addButton = new QPushButton("+", this);
|
||
addButton->setFlat(true);
|
||
layout->addWidget(addButton, layout->rowCount(), 0, 1, 3);
|
||
connect(addButton, &QPushButton::clicked, [this, layout, addButton]() {
|
||
bool ok;
|
||
QString attributeName = QInputDialog::getText(
|
||
this, tr("Add Attribute"), tr("Attribute Name:"), QLineEdit::Normal, "", &ok);
|
||
if (ok && !attributeName.isEmpty()) {
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
layout->removeWidget(addButton);
|
||
addItemIntoGroupBox(layout, attributeName, "", 99);
|
||
layout->addWidget(addButton, layout->rowCount(), 0, 1, 3);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 打开IDL文件
|
||
void MainWindow::openIDLFile()
|
||
{
|
||
// 打开文件
|
||
QFile file(currentFilePath);
|
||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to open file."));
|
||
return;
|
||
}
|
||
|
||
// 清空现有数据
|
||
namespaces.clear();
|
||
|
||
// 读取文件
|
||
QTextStream in(&file);
|
||
// 命名空间栈
|
||
QStack<QSharedPointer<NamespaceDefinition>> namespaceStack;
|
||
// 当前结构体
|
||
QSharedPointer<StructDefinition> currentStruct;
|
||
QString line;
|
||
bool inStruct = false;
|
||
int braceCount = 0;
|
||
|
||
// 逐行读取IDL文件
|
||
while (!in.atEnd()) {
|
||
line = in.readLine().trimmed();
|
||
|
||
// 跳过空行和注释
|
||
if (line.isEmpty() || line.startsWith("//"))
|
||
continue;
|
||
|
||
// 处理命名空间开始
|
||
if (line.startsWith("module")) {
|
||
braceCount++;
|
||
// 创建新的命名空间
|
||
QSharedPointer<NamespaceDefinition> newNS =
|
||
QSharedPointer<NamespaceDefinition>::create();
|
||
|
||
// 获取命名空间名称
|
||
QString nsNames = line.mid(7).trimmed();
|
||
newNS->namespaceName = nsNames.split(" ").first(); // 直接使用完整名称
|
||
namespaceStack.push(newNS);
|
||
continue;
|
||
}
|
||
|
||
// 处理结构体开始
|
||
if (line.startsWith("struct")) {
|
||
if (inStruct && currentStruct && !currentStruct->structName.isEmpty()) {
|
||
namespaceStack.top()->structDefinitions.append(currentStruct);
|
||
currentStruct = QSharedPointer<StructDefinition>::create();
|
||
}
|
||
inStruct = true;
|
||
braceCount++;
|
||
if (!currentStruct) {
|
||
currentStruct = QSharedPointer<StructDefinition>::create();
|
||
}
|
||
currentStruct->structName = line.split(" ").at(1);
|
||
continue;
|
||
}
|
||
|
||
// 处理成员变量
|
||
if (inStruct && currentStruct) {
|
||
if (!line.startsWith("{") && !line.startsWith("}")) {
|
||
QString description;
|
||
int commentPos = line.indexOf("//");
|
||
if (commentPos != -1) {
|
||
description = line.mid(commentPos + 2).trimmed();
|
||
line = line.left(commentPos).trimmed();
|
||
}
|
||
|
||
QStringList parts = line.split(";").first().split(" ", Qt::SkipEmptyParts);
|
||
if (parts[0].startsWith("@")) {
|
||
parts.pop_front();
|
||
}
|
||
if (parts.size() >= 2) {
|
||
QSharedPointer<MemberVariable> var = QSharedPointer<MemberVariable>::create();
|
||
var->dataType = parts[0];
|
||
QString varDecl = parts[1];
|
||
|
||
if (varDecl.contains("[")) {
|
||
var->isArray = true;
|
||
var->variableName = varDecl.split("[").first();
|
||
|
||
QRegularExpression re("\\[(\\d+)\\]");
|
||
QRegularExpressionMatchIterator i = re.globalMatch(varDecl);
|
||
while (i.hasNext()) {
|
||
QRegularExpressionMatch match = i.next();
|
||
var->arraySizes.append(match.captured(1).toInt());
|
||
}
|
||
} else {
|
||
var->isArray = false;
|
||
var->variableName = varDecl;
|
||
}
|
||
|
||
var->description = description;
|
||
currentStruct->memberVariables.append(var);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理花括号闭合
|
||
if (line.contains("}")) {
|
||
braceCount--;
|
||
if (inStruct) {
|
||
inStruct = false;
|
||
if (currentStruct && !currentStruct->structName.isEmpty()) {
|
||
namespaceStack.top()->structDefinitions.append(currentStruct);
|
||
currentStruct = QSharedPointer<StructDefinition>::create();
|
||
}
|
||
} else {
|
||
if (!namespaceStack.isEmpty()) {
|
||
QSharedPointer<NamespaceDefinition> completedNS = namespaceStack.pop();
|
||
if (namespaceStack.isEmpty()) {
|
||
namespaces.append(completedNS);
|
||
} else {
|
||
namespaceStack.top()->childNamespaces.append(completedNS);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
file.close();
|
||
// 更新IDL Tree
|
||
updateIDLTreeWidget();
|
||
|
||
updateWindowTitle();
|
||
}
|
||
|
||
// 根据namespaces更新树形控件
|
||
void MainWindow::updateIDLTreeWidget()
|
||
{
|
||
QTreeWidget *treeWidget = findChild<QTreeWidget *>("leftWidget");
|
||
if (!treeWidget)
|
||
return;
|
||
|
||
// 清空现有项
|
||
treeWidget->clear();
|
||
|
||
// 递归添加命名空间及其子命名空间的辅助函数
|
||
std::function<void(QTreeWidgetItem *, const QSharedPointer<NamespaceDefinition> &)>
|
||
addNamespaceToTree = [&addNamespaceToTree](QTreeWidgetItem *parentItem,
|
||
const QSharedPointer<NamespaceDefinition> &ns) {
|
||
// 添加结构体
|
||
for (const auto &structDef : ns->structDefinitions) {
|
||
if (structDef && !structDef->structName.isEmpty()) {
|
||
QTreeWidgetItem *structItem =
|
||
new QTreeWidgetItem(QStringList() << structDef->structName);
|
||
if (parentItem) {
|
||
parentItem->addChild(structItem);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 递归处理子命名空间
|
||
for (const auto &childNS : ns->childNamespaces) {
|
||
if (childNS && !childNS->namespaceName.isEmpty()) {
|
||
QTreeWidgetItem *childItem =
|
||
new QTreeWidgetItem(QStringList() << childNS->namespaceName);
|
||
if (parentItem) {
|
||
parentItem->addChild(childItem);
|
||
addNamespaceToTree(childItem, childNS);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
// 处理顶层命名空间
|
||
for (const auto &ns : namespaces) {
|
||
if (ns && !ns->namespaceName.isEmpty()) {
|
||
QTreeWidgetItem *nsItem = new QTreeWidgetItem(QStringList() << ns->namespaceName);
|
||
treeWidget->addTopLevelItem(nsItem);
|
||
addNamespaceToTree(nsItem, ns);
|
||
}
|
||
}
|
||
|
||
// 展开整个树形控件
|
||
treeWidget->expandAll();
|
||
|
||
// 连接树形控件的项目选择信号
|
||
disconnect(treeWidget, &QTreeWidget::itemClicked, this, &MainWindow::onTreeWidgetItemClicked);
|
||
connect(treeWidget, &QTreeWidget::itemClicked, this, &MainWindow::onTreeWidgetItemClicked);
|
||
}
|
||
|
||
void MainWindow::onTreeWidgetItemClicked(QTreeWidgetItem *item)
|
||
{
|
||
if (!item->parent())
|
||
return; // 如果是顶层项目(命名空间),则返回
|
||
|
||
QTableWidget *tableWidget = findChild<QTableWidget *>("rightWidget");
|
||
if (!tableWidget)
|
||
return;
|
||
|
||
// 清空表格
|
||
tableWidget->setRowCount(0);
|
||
|
||
// 获取选中的结构体名称
|
||
QString structName = item->text(0);
|
||
|
||
std::function<QSharedPointer<StructDefinition>(const QString &,
|
||
QSharedPointer<NamespaceDefinition> &)>
|
||
findStruct =
|
||
[&findStruct](
|
||
const QString &structName,
|
||
QSharedPointer<NamespaceDefinition> &ns) -> QSharedPointer<StructDefinition> {
|
||
// 在当前命名空间中查找结构体
|
||
for (auto &structDef : ns->structDefinitions) {
|
||
if (structDef && structDef->structName == structName) {
|
||
return structDef;
|
||
}
|
||
}
|
||
// 在子命名空间中递归查找
|
||
for (auto &childNS : ns->childNamespaces) {
|
||
auto result = findStruct(structName, childNS);
|
||
if (result) {
|
||
return result;
|
||
}
|
||
}
|
||
return QSharedPointer<StructDefinition>();
|
||
};
|
||
|
||
QSharedPointer<StructDefinition> structDef;
|
||
for (auto &ns : namespaces) {
|
||
structDef = findStruct(structName, ns);
|
||
if (structDef) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (structDef) {
|
||
// 找到对应的结构体,显示其成员变量
|
||
tableWidget->setRowCount(structDef->memberVariables.size());
|
||
int row = 0;
|
||
for (const auto &member : structDef->memberVariables) {
|
||
// 变量名
|
||
QTableWidgetItem *nameItem = new QTableWidgetItem(member->variableName);
|
||
tableWidget->setItem(row, 0, nameItem);
|
||
|
||
// 数据类型
|
||
QTableWidgetItem *typeItem = new QTableWidgetItem(member->dataType);
|
||
tableWidget->setItem(row, 1, typeItem);
|
||
|
||
// 数组大小
|
||
QString sizeStr;
|
||
if (member->isArray) {
|
||
QStringList sizes;
|
||
for (int size : member->arraySizes) {
|
||
sizes << QString::number(size);
|
||
}
|
||
sizeStr = "[" + sizes.join("][") + "]";
|
||
} else {
|
||
sizeStr = "1";
|
||
}
|
||
QTableWidgetItem *sizeItem = new QTableWidgetItem(sizeStr);
|
||
tableWidget->setItem(row, 2, sizeItem);
|
||
|
||
// 描述(注释)
|
||
QTableWidgetItem *descItem = new QTableWidgetItem(member->description);
|
||
tableWidget->setItem(row, 3, descItem);
|
||
|
||
row++;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加组框1
|
||
void MainWindow::addGroupBox(QVBoxLayout *layout, QString name, QDomNamedNodeMap attributes)
|
||
{
|
||
// 工作路径
|
||
QString workPath;
|
||
// 创建组框
|
||
QGroupBox *groupBox = new QGroupBox(name, this);
|
||
// 设置组框名称
|
||
groupBox->setObjectName(name);
|
||
// 添加到布局
|
||
layout->addWidget(groupBox);
|
||
// 创建网格布局
|
||
QGridLayout *gridLayout = new QGridLayout(groupBox);
|
||
// 设置布局
|
||
groupBox->setLayout(gridLayout);
|
||
// 遍历属性添加控件
|
||
for (int i = 0; i < attributes.count(); i++) {
|
||
QDomNode attr = attributes.item(i);
|
||
if (attr.nodeName() == "WorkPath") {
|
||
// 如果是WorkPath,多添加一个选择路径按钮
|
||
addItemIntoGroupBox(gridLayout, attr.nodeName(), attr.nodeValue(), 1);
|
||
} else if (attr.nodeName() == "ModelsPath") {
|
||
// 如果是ModelsPath,正常添加
|
||
addItemIntoGroupBox(gridLayout, attr.nodeName(), attr.nodeValue(), 0);
|
||
// 记录ModelsPath
|
||
modelPath = attr.nodeValue();
|
||
} else if (attr.nodeName() == "ServicesPath") {
|
||
// 如果是ServicesPath,正常添加
|
||
addItemIntoGroupBox(gridLayout, attr.nodeName(), attr.nodeValue(), 0);
|
||
// 记录ServicesPath
|
||
ServicePath = attr.nodeValue();
|
||
} else if (attr.nodeName() == "OSName" || attr.nodeName() == "Version"
|
||
|| attr.nodeName() == "RTXVersion" || attr.nodeName() == "CPUAffinity"
|
||
|| attr.nodeName() == "BaseFrequency" || attr.nodeName() == "DomainID") {
|
||
// 如果是OSName、Version、RTXVersion、CPUAffinity、BaseFrequency、DomainID,正常添加
|
||
addItemIntoGroupBox(gridLayout, attr.nodeName(), attr.nodeValue(), 0);
|
||
} else {
|
||
// 如果是其他属性,添加一个删除按钮
|
||
addItemIntoGroupBox(gridLayout, attr.nodeName(), attr.nodeValue(), 99);
|
||
}
|
||
}
|
||
// 如果工作路径不为空
|
||
if (!workPath.isEmpty()) {
|
||
// 如果工作路径不以"/"结尾
|
||
if (!workPath.endsWith("/")) {
|
||
// 添加"/"
|
||
workPath.append("/");
|
||
}
|
||
// 添加工作路径到模型路径和服务路径
|
||
modelPath = workPath + modelPath;
|
||
ServicePath = workPath + ServicePath;
|
||
}
|
||
// 添加"+"按钮
|
||
addPlusButton(gridLayout);
|
||
}
|
||
|
||
// 添加组框2
|
||
void MainWindow::addGroupBox2(QVBoxLayout *layout, QString name, QDomNamedNodeMap attributes)
|
||
{
|
||
// 创建组框
|
||
QGroupBox *groupBox = new QGroupBox(name, this);
|
||
// 设置组框名称
|
||
groupBox->setObjectName(name);
|
||
// 添加到布局
|
||
layout->addWidget(groupBox);
|
||
// 创建水平布局
|
||
QHBoxLayout *hLayout = new QHBoxLayout(groupBox);
|
||
// 设置布局
|
||
groupBox->setLayout(hLayout);
|
||
// 遍历属性添加控件
|
||
for (int i = 0; i < attributes.count(); i++) {
|
||
QDomNode attr = attributes.item(i);
|
||
// 创建复选框
|
||
QCheckBox *checkBox = new QCheckBox(attr.nodeName(), this);
|
||
// 如果属性值为true或1或True或TRUE,则设置为选中
|
||
if (attr.nodeValue() == "true" || attr.nodeValue() == "1" || attr.nodeValue() == "True"
|
||
|| attr.nodeValue() == "TRUE") {
|
||
checkBox->setChecked(true);
|
||
} else {
|
||
checkBox->setChecked(false);
|
||
}
|
||
// 添加到布局
|
||
hLayout->addWidget(checkBox);
|
||
// 连接修改信号
|
||
connectModificationSignals(checkBox);
|
||
}
|
||
}
|
||
|
||
// 添加组框3
|
||
void MainWindow::addGroupBox3(QVBoxLayout *layout, QString name, QDomNamedNodeMap groupAttributes,
|
||
QVector<QDomNamedNodeMap> vecAttributes, int type)
|
||
{
|
||
// 创建组框
|
||
QGroupBox *groupBox = new QGroupBox(name, this);
|
||
// 设置组框名称
|
||
groupBox->setObjectName(name);
|
||
// 添加到布局
|
||
layout->addWidget(groupBox);
|
||
// 创建垂直布局
|
||
QGridLayout *groupBoxLayout = new QGridLayout(groupBox);
|
||
|
||
// 遍历组属性
|
||
for (int i = 0; i < groupAttributes.count(); i++) {
|
||
QDomNode attr = groupAttributes.item(i);
|
||
addItemIntoGroupBox(groupBoxLayout, attr.nodeName(), attr.nodeValue(), 0);
|
||
}
|
||
// 创建水平布局
|
||
QHBoxLayout *buttonLayout = new QHBoxLayout();
|
||
// 创建添加按钮
|
||
QPushButton *addButton = new QPushButton("Add", this);
|
||
// 创建删除按钮
|
||
QPushButton *removeButton = new QPushButton("Remove", this);
|
||
// 添加按钮到水平布局
|
||
buttonLayout->addWidget(addButton);
|
||
// 添加按钮到水平布局
|
||
buttonLayout->addWidget(removeButton);
|
||
// 添加水平布局到垂直布局
|
||
groupBoxLayout->addLayout(buttonLayout, groupBoxLayout->rowCount(), 0, 1,
|
||
groupBoxLayout->columnCount());
|
||
// 创建表格
|
||
QTableWidget *tableWidget = new QTableWidget(groupBox);
|
||
// 设置表格拉伸模式
|
||
tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||
// 设置表格行数
|
||
tableWidget->setRowCount(vecAttributes.size());
|
||
// 设置表格选择模式
|
||
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
// 如果类型为0或1,即模型列表或服务列表
|
||
if (type == 0 || type == 1) {
|
||
// 设置表格列数
|
||
tableWidget->setColumnCount(2);
|
||
// 创建表头
|
||
QStringList headers;
|
||
// 设置表头
|
||
headers << "Name" << "ClassName";
|
||
// 设置表头
|
||
tableWidget->setHorizontalHeaderLabels(headers);
|
||
} else if (type == 2) { // 如果类型为2,即命令列表
|
||
// 设置表格列数
|
||
tableWidget->setColumnCount(3);
|
||
// 创建表头
|
||
QStringList headers;
|
||
// 设置表头
|
||
headers << "Name" << "Description" << "Call";
|
||
// 设置表头
|
||
tableWidget->setHorizontalHeaderLabels(headers);
|
||
}
|
||
|
||
// 添加表格到组框布局
|
||
groupBoxLayout->addWidget(tableWidget, groupBoxLayout->rowCount(), 0, 1,
|
||
groupBoxLayout->columnCount());
|
||
|
||
int rowCount = 0;
|
||
// 遍历属性
|
||
for (QDomNamedNodeMap &attributes : vecAttributes) {
|
||
// 遍历属性
|
||
for (int i = 0; i < attributes.count(); i++) {
|
||
// 如果类型为0或1,即模型列表或服务列表
|
||
if (type == 0 || type == 1) {
|
||
if (attributes.item(i).nodeName() == "ClassName") {
|
||
// 如果属性名称为ClassName,设置表格项第2列
|
||
tableWidget->setItem(rowCount, 1,
|
||
new QTableWidgetItem(attributes.item(i).nodeValue()));
|
||
} else if (attributes.item(i).nodeName() == "Name") {
|
||
// 如果属性名称为Name,设置表格项第1列
|
||
tableWidget->setItem(rowCount, 0,
|
||
new QTableWidgetItem(attributes.item(i).nodeValue()));
|
||
}
|
||
} else if (type == 2) { // 如果类型为2,即命令列表
|
||
if (attributes.item(i).nodeName() == "Name") {
|
||
// 如果属性名称为Name,设置表格项第1列
|
||
tableWidget->setItem(rowCount, 0,
|
||
new QTableWidgetItem(attributes.item(i).nodeValue()));
|
||
} else if (attributes.item(i).nodeName() == "Description") {
|
||
// 如果属性名称为Description,设置表格项第2列
|
||
tableWidget->setItem(rowCount, 1,
|
||
new QTableWidgetItem(attributes.item(i).nodeValue()));
|
||
} else if (attributes.item(i).nodeName() == "Call") {
|
||
// 如果属性名称为Call,设置表格项第3列
|
||
tableWidget->setItem(rowCount, 2,
|
||
new QTableWidgetItem(attributes.item(i).nodeValue()));
|
||
}
|
||
}
|
||
}
|
||
rowCount++;
|
||
}
|
||
|
||
// 更新模型列表或服务列表
|
||
if (type == 0) {
|
||
UpdateModelList(tableWidget);
|
||
} else if (type == 1) {
|
||
UpdateServiceList(tableWidget);
|
||
}
|
||
|
||
// 链接add按钮的槽
|
||
connect(addButton, &QPushButton::clicked, [tableWidget]() {
|
||
int row = tableWidget->rowCount();
|
||
tableWidget->insertRow(row);
|
||
});
|
||
|
||
// 链接remove的槽
|
||
connect(removeButton, &QPushButton::clicked, [this, tableWidget]() {
|
||
int currentRow = tableWidget->currentRow();
|
||
if (currentRow >= 0) {
|
||
if (!tableWidget->item(currentRow, 0)->text().isEmpty()) {
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
}
|
||
tableWidget->removeRow(currentRow);
|
||
tableWidget->clearSelection();
|
||
}
|
||
});
|
||
// 链接元素改变的槽
|
||
connect(tableWidget, &QTableWidget::itemChanged, [this, tableWidget, type]() {
|
||
if (type == 0) {
|
||
UpdateModelList(tableWidget);
|
||
} else if (type == 1) {
|
||
UpdateServiceList(tableWidget);
|
||
}
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
});
|
||
}
|
||
|
||
// 向组框1添加元素
|
||
void MainWindow::addItemIntoGroupBox(QGridLayout *gridLayout, QString itemName, QString itemValue,
|
||
int itemStyle)
|
||
{
|
||
int row = gridLayout->rowCount();
|
||
QLabel *label = new QLabel(itemName, this);
|
||
gridLayout->addWidget(label, row, 0);
|
||
if (itemStyle == 0) { //普通模式
|
||
QLineEdit *lineEdit = new QLineEdit(itemValue, this);
|
||
gridLayout->addWidget(lineEdit, row, 1);
|
||
setLineEditRules(lineEdit, itemName);
|
||
connectModificationSignals(lineEdit);
|
||
} else if (itemStyle == 1) { //路径模式
|
||
QLineEdit *lineEdit = new QLineEdit(itemValue, this);
|
||
gridLayout->addWidget(lineEdit, row, 1);
|
||
QPushButton *openPathButton = new QPushButton("...", this);
|
||
gridLayout->addWidget(openPathButton, row, 2);
|
||
connect(openPathButton, &QPushButton::clicked, [this, lineEdit]() {
|
||
QString newPath =
|
||
QFileDialog::getExistingDirectory(this, tr("Select Directory"), lineEdit->text());
|
||
if (!newPath.isEmpty()) {
|
||
lineEdit->setText(newPath);
|
||
}
|
||
});
|
||
connectModificationSignals(lineEdit);
|
||
} else if (itemStyle == 2) { //文件选择模式
|
||
QLineEdit *lineEdit = new QLineEdit(itemValue, this);
|
||
gridLayout->addWidget(lineEdit, row, 1);
|
||
QPushButton *openFileButton = new QPushButton("...", this);
|
||
gridLayout->addWidget(openFileButton, row, 2);
|
||
connect(openFileButton, &QPushButton::clicked, [this, lineEdit]() {
|
||
QString newPath = QFileDialog::getOpenFileName(this, tr("Select File"),
|
||
lineEdit->text(), tr("All Files (*)"));
|
||
if (!newPath.isEmpty()) {
|
||
lineEdit->setText(newPath);
|
||
}
|
||
});
|
||
connectModificationSignals(lineEdit);
|
||
} else if (itemStyle == 3) { //时间选择模式
|
||
QDateTimeEdit *dateTimeEdit = new QDateTimeEdit(this);
|
||
gridLayout->addWidget(dateTimeEdit, row, 1);
|
||
dateTimeEdit->setCalendarPopup(true);
|
||
dateTimeEdit->setDateTime(QDateTime::currentDateTime());
|
||
connectModificationSignals(dateTimeEdit);
|
||
} else if (itemStyle == 99) { //自定义模式,支持删除
|
||
QLineEdit *lineEdit = new QLineEdit(itemValue, this);
|
||
gridLayout->addWidget(lineEdit, row, 1);
|
||
QPushButton *minuseButton = new QPushButton("-", this);
|
||
minuseButton->setFixedSize(20, 20);
|
||
connect(minuseButton, &QPushButton::clicked,
|
||
[this, gridLayout, label, lineEdit, minuseButton]() {
|
||
QMessageBox::StandardButton reply;
|
||
reply =
|
||
QMessageBox::question(this, tr("Confirm Deletion"),
|
||
tr("Are you sure you want to delete this attribute?"),
|
||
QMessageBox::Yes | QMessageBox::No);
|
||
if (reply == QMessageBox::Yes) {
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
gridLayout->removeWidget(label);
|
||
gridLayout->removeWidget(lineEdit);
|
||
gridLayout->removeWidget(minuseButton);
|
||
delete label;
|
||
delete lineEdit;
|
||
delete minuseButton;
|
||
}
|
||
});
|
||
gridLayout->addWidget(minuseButton, row, 2);
|
||
connectModificationSignals(lineEdit);
|
||
}
|
||
}
|
||
|
||
void MainWindow::setLineEditRules(QLineEdit *lineEdit, QString name)
|
||
{
|
||
if (name == "WorkPath" || name == "ModelsPath" || name == "ServicesPath") {
|
||
// 实时验证目录是否存在
|
||
connect(lineEdit, &QLineEdit::textChanged, [this, lineEdit, name](const QString &text) {
|
||
QDir dir(text);
|
||
if (!dir.exists()) {
|
||
lineEdit->setStyleSheet("border: 2px solid red;");
|
||
lineEdit->setToolTip(tr("Directory does not exist"));
|
||
} else {
|
||
lineEdit->setStyleSheet("");
|
||
lineEdit->setToolTip("");
|
||
}
|
||
});
|
||
} else if (name == "Name") {
|
||
connect(lineEdit, &QLineEdit::textChanged, [this, lineEdit, name](const QString &text) {
|
||
// 检查是否是C++标准的名称
|
||
QRegularExpression regex("^[A-Za-z_][A-Za-z0-9_]*$");
|
||
if (!regex.match(text).hasMatch()) {
|
||
lineEdit->setStyleSheet("border: 2px solid red;");
|
||
lineEdit->setToolTip(tr("Invalid class name. Must start with letter/underscore and "
|
||
"contain only letters, numbers and underscores"));
|
||
} else {
|
||
lineEdit->setStyleSheet("");
|
||
lineEdit->setToolTip("");
|
||
}
|
||
});
|
||
} else if (name == "CPUAffinity") {
|
||
// 检查是否是逗号分割的数字列表
|
||
connect(lineEdit, &QLineEdit::textChanged, [this, lineEdit, name](const QString &text) {
|
||
QRegularExpression regex("^[0-9,]+$");
|
||
if (!regex.match(text).hasMatch()) {
|
||
lineEdit->setStyleSheet("border: 2px solid red;");
|
||
lineEdit->setToolTip(
|
||
tr("Invalid CPU affinity format. Must be a comma-separated list of numbers"));
|
||
} else {
|
||
lineEdit->setStyleSheet("");
|
||
lineEdit->setToolTip("");
|
||
}
|
||
});
|
||
} else if (name == "BaseFrequency" || name == "DomainID" || name == "Priority") {
|
||
// 检查是否是数字
|
||
connect(lineEdit, &QLineEdit::textChanged, [this, lineEdit, name](const QString &text) {
|
||
QRegularExpression regex("^[0-9]+$");
|
||
if (!regex.match(text).hasMatch()) {
|
||
lineEdit->setStyleSheet("border: 2px solid red;");
|
||
lineEdit->setToolTip(tr("Invalid base frequency. Must be a number"));
|
||
} else {
|
||
lineEdit->setStyleSheet("");
|
||
lineEdit->setToolTip("");
|
||
}
|
||
});
|
||
} else if (name == "Node") {
|
||
connect(lineEdit, &QLineEdit::textChanged, [this, lineEdit, name](const QString &text) {
|
||
// 验证"数字-数字"的格式
|
||
QRegularExpression regex("^\\d+-\\d+$");
|
||
if (!regex.match(text).hasMatch()) {
|
||
lineEdit->setStyleSheet("border: 2px solid red;");
|
||
lineEdit->setToolTip(
|
||
tr("Invalid format. Must be in the form of 'number-number' (e.g. 3-5)"));
|
||
} else {
|
||
// 进一步验证第一个数字是否小于第二个数字
|
||
QStringList parts = text.split('-');
|
||
int first = parts[0].toInt();
|
||
int second = parts[1].toInt();
|
||
if (first > 5) {
|
||
lineEdit->setStyleSheet("border: 2px solid red;");
|
||
lineEdit->setToolTip(tr("First number must be less than 5"));
|
||
} else {
|
||
if (second >= pow(2, first)) {
|
||
lineEdit->setStyleSheet("border: 2px solid red;");
|
||
lineEdit->setToolTip(tr(
|
||
"Second number must be less than 2 to the power of the first number"));
|
||
} else {
|
||
lineEdit->setStyleSheet("");
|
||
lineEdit->setToolTip("");
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 寻找模型
|
||
bool MainWindow::findModel(QString modelName, QString ModelPath)
|
||
{
|
||
//查找模型动态库是否存在
|
||
QFile modelsoFile(ModelPath + "/lib" + modelName + ".so");
|
||
if (!modelsoFile.exists()) {
|
||
return false;
|
||
}
|
||
|
||
//查找模型配置文件是否存在
|
||
QFile modelFile(ModelPath + "/" + modelName + ".mcfg");
|
||
if (!modelFile.exists()) {
|
||
return false;
|
||
}
|
||
|
||
QTreeWidget *modelWidget = findChild<QTreeWidget *>("ModelWidget");
|
||
if (modelWidget) {
|
||
// 检查模型列表中是否已存在同名模型
|
||
bool exists = false;
|
||
for (int i = 0; i < modelWidget->topLevelItemCount(); i++) {
|
||
if (modelWidget->topLevelItem(i)->text(0) == modelName + ".mcfg") {
|
||
exists = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!exists)
|
||
modelWidget->addTopLevelItem(new QTreeWidgetItem(QStringList() << modelName + ".mcfg"));
|
||
else
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// 寻找服务
|
||
bool MainWindow::findService(QString serviceName, QString ServicePath)
|
||
{
|
||
// 查找服务动态库是否存在
|
||
QFile servicesoFile(ServicePath + "/lib" + serviceName + ".so");
|
||
if (!servicesoFile.exists()) {
|
||
return false;
|
||
}
|
||
|
||
// 查找服务配置文件是否存在
|
||
QFile serviceFile(ServicePath + "/" + serviceName + ".scfg");
|
||
if (!serviceFile.exists()) {
|
||
return false;
|
||
}
|
||
|
||
QTreeWidget *serviceWidget = findChild<QTreeWidget *>("ServiceWidget");
|
||
if (serviceWidget) {
|
||
// 检查服务列表中是否已存在同名模型
|
||
bool exists = false;
|
||
for (int i = 0; i < serviceWidget->topLevelItemCount(); i++) {
|
||
if (serviceWidget->topLevelItem(i)->text(0) == serviceName + ".scfg") {
|
||
exists = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!exists)
|
||
serviceWidget->addTopLevelItem(
|
||
new QTreeWidgetItem(QStringList() << serviceName + ".scfg"));
|
||
else
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// 更新模型配置文件列表
|
||
void MainWindow::UpdateModelList(QTableWidget *tabWidget)
|
||
{
|
||
if (currentFileType != 0 || tabWidget == nullptr)
|
||
return;
|
||
QTreeWidget *modelWidget = findChild<QTreeWidget *>("ModelWidget");
|
||
if (modelWidget) {
|
||
modelWidget->clear();
|
||
}
|
||
for (int row = 0; row < tabWidget->rowCount(); row++) {
|
||
QTableWidgetItem *item = tabWidget->item(row, 1); // Get item from ClassName column
|
||
if (item) {
|
||
QString className = item->text();
|
||
if (!findModel(className, modelPath)) {
|
||
item->setForeground(Qt::red);
|
||
item->setToolTip("Model not found or already exists");
|
||
} else {
|
||
item->setForeground(Qt::white);
|
||
item->setToolTip("");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新服务配置文件列表
|
||
void MainWindow::UpdateServiceList(QTableWidget *tabWidget)
|
||
{
|
||
if (currentFileType != 0 || tabWidget == nullptr)
|
||
return;
|
||
QTreeWidget *serviceWidget = findChild<QTreeWidget *>("ServiceWidget");
|
||
if (serviceWidget) {
|
||
serviceWidget->clear();
|
||
}
|
||
for (int row = 0; row < tabWidget->rowCount(); row++) {
|
||
QTableWidgetItem *item = tabWidget->item(row, 1); // Get item from ClassName column
|
||
if (item) {
|
||
QString className = item->text();
|
||
if (!findService(className, ServicePath)) {
|
||
item->setForeground(Qt::red);
|
||
item->setToolTip("Service not found or already exists");
|
||
} else {
|
||
item->setForeground(Qt::white);
|
||
item->setToolTip("");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 保存文件
|
||
void MainWindow::saveFile(QString filePath)
|
||
{
|
||
if (!filePath.isEmpty()) {
|
||
if (currentFileType == 0) {
|
||
saveScenarioFile(filePath);
|
||
} else if (currentFileType == 1) {
|
||
saveModelFile(filePath);
|
||
} else if (currentFileType == 2) {
|
||
saveServiceFile(filePath);
|
||
} else if (currentFileType == 3) {
|
||
saveIDLFile(filePath);
|
||
}
|
||
isModified = false; // 文件保存后重置修改状态
|
||
updateWindowTitle();
|
||
}
|
||
}
|
||
|
||
// 保存运行环境配置文件
|
||
void MainWindow::saveScenarioFile(QString filePath)
|
||
{
|
||
QFile file(filePath);
|
||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to save file."));
|
||
return;
|
||
}
|
||
|
||
QXmlStreamWriter xmlWriter(&file);
|
||
xmlWriter.setAutoFormatting(true);
|
||
xmlWriter.writeStartDocument();
|
||
xmlWriter.writeStartElement("Scenario");
|
||
|
||
// 保存Environment部分
|
||
QGroupBox *envGroupBox = findChild<QGroupBox *>("Environment");
|
||
if (envGroupBox) {
|
||
xmlWriter.writeStartElement("Environment");
|
||
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(envGroupBox->layout());
|
||
if (gridLayout) {
|
||
for (int i = 0; i < gridLayout->rowCount(); ++i) {
|
||
QLayoutItem *labelItem = gridLayout->itemAtPosition(i, 0);
|
||
QLayoutItem *lineEditItem = gridLayout->itemAtPosition(i, 1);
|
||
if (labelItem && lineEditItem) {
|
||
QLabel *label = qobject_cast<QLabel *>(labelItem->widget());
|
||
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(lineEditItem->widget());
|
||
if (label && lineEdit) {
|
||
xmlWriter.writeAttribute(label->text(), lineEdit->text());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // Environment
|
||
}
|
||
|
||
// 保存ConsoleOutput和Log部分
|
||
QGroupBox *consoleGroupBox = findChild<QGroupBox *>("ConsoleOutput");
|
||
if (consoleGroupBox) {
|
||
xmlWriter.writeStartElement("ConsoleOutput");
|
||
QHBoxLayout *hLayout = qobject_cast<QHBoxLayout *>(consoleGroupBox->layout());
|
||
if (hLayout) {
|
||
for (int i = 0; i < hLayout->count(); ++i) {
|
||
QCheckBox *checkBox = qobject_cast<QCheckBox *>(hLayout->itemAt(i)->widget());
|
||
if (checkBox) {
|
||
xmlWriter.writeAttribute(checkBox->text(),
|
||
checkBox->isChecked() ? "true" : "false");
|
||
}
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // ConsoleOutput
|
||
}
|
||
|
||
QGroupBox *logGroupBox = findChild<QGroupBox *>("Log");
|
||
if (logGroupBox) {
|
||
xmlWriter.writeStartElement("Log");
|
||
QHBoxLayout *hLayout = qobject_cast<QHBoxLayout *>(logGroupBox->layout());
|
||
if (hLayout) {
|
||
for (int i = 0; i < hLayout->count(); ++i) {
|
||
QCheckBox *checkBox = qobject_cast<QCheckBox *>(hLayout->itemAt(i)->widget());
|
||
if (checkBox) {
|
||
xmlWriter.writeAttribute(checkBox->text(),
|
||
checkBox->isChecked() ? "true" : "false");
|
||
}
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // Log
|
||
}
|
||
|
||
// 保存ModelsList部分
|
||
QGroupBox *modelsGroupBox = findChild<QGroupBox *>("ModelsList");
|
||
if (modelsGroupBox) {
|
||
xmlWriter.writeStartElement("ModelsList");
|
||
QTableWidget *tableWidget = modelsGroupBox->findChild<QTableWidget *>();
|
||
if (tableWidget) {
|
||
for (int row = 0; row < tableWidget->rowCount(); ++row) {
|
||
if (!tableWidget->item(row, 1)->text().isEmpty()) {
|
||
xmlWriter.writeStartElement("Model");
|
||
for (int col = 0; col < tableWidget->columnCount(); ++col) {
|
||
QTableWidgetItem *item = tableWidget->item(row, col);
|
||
if (item) {
|
||
xmlWriter.writeAttribute(tableWidget->horizontalHeaderItem(col)->text(),
|
||
item->text());
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // Model
|
||
}
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // ModelsList
|
||
}
|
||
|
||
// 保存ServicesList部分
|
||
QGroupBox *servicesGroupBox = findChild<QGroupBox *>("ServicesList");
|
||
if (servicesGroupBox) {
|
||
xmlWriter.writeStartElement("ServicesList");
|
||
QTableWidget *tableWidget = servicesGroupBox->findChild<QTableWidget *>();
|
||
if (tableWidget) {
|
||
for (int row = 0; row < tableWidget->rowCount(); ++row) {
|
||
if (!tableWidget->item(row, 1)->text().isEmpty()) {
|
||
xmlWriter.writeStartElement("Service");
|
||
for (int col = 0; col < tableWidget->columnCount(); ++col) {
|
||
QTableWidgetItem *item = tableWidget->item(row, col);
|
||
if (item) {
|
||
xmlWriter.writeAttribute(tableWidget->horizontalHeaderItem(col)->text(),
|
||
item->text());
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // Service
|
||
}
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // ServicesList
|
||
}
|
||
|
||
xmlWriter.writeEndElement(); // Scenario
|
||
xmlWriter.writeEndDocument();
|
||
file.close();
|
||
}
|
||
|
||
// 保存模型配置文件
|
||
void MainWindow::saveModelFile(QString filePath)
|
||
{
|
||
QFile file(filePath);
|
||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to save file."));
|
||
return;
|
||
}
|
||
|
||
QXmlStreamWriter xmlWriter(&file);
|
||
xmlWriter.setAutoFormatting(true);
|
||
xmlWriter.writeStartDocument();
|
||
xmlWriter.writeStartElement("Model");
|
||
|
||
// 保存Model Info部分
|
||
QGroupBox *modelInfoBox = findChild<QGroupBox *>("Model Info");
|
||
if (modelInfoBox) {
|
||
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(modelInfoBox->layout());
|
||
if (gridLayout) {
|
||
for (int i = 0; i < gridLayout->rowCount(); ++i) {
|
||
QLayoutItem *labelItem = gridLayout->itemAtPosition(i, 0);
|
||
QLayoutItem *valueItem = gridLayout->itemAtPosition(i, 1);
|
||
|
||
if (labelItem && valueItem) {
|
||
QLabel *label = qobject_cast<QLabel *>(labelItem->widget());
|
||
if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(valueItem->widget())) {
|
||
if (label) {
|
||
xmlWriter.writeStartElement(label->text());
|
||
xmlWriter.writeCharacters(lineEdit->text());
|
||
xmlWriter.writeEndElement();
|
||
}
|
||
} else if (QDateTimeEdit *dateTimeEdit =
|
||
qobject_cast<QDateTimeEdit *>(valueItem->widget())) {
|
||
if (label) {
|
||
xmlWriter.writeStartElement(label->text());
|
||
xmlWriter.writeCharacters(
|
||
dateTimeEdit->dateTime().toString(Qt::ISODate));
|
||
xmlWriter.writeEndElement();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 保存CommandList部分
|
||
QGroupBox *commandListBox = findChild<QGroupBox *>("CommandList");
|
||
if (commandListBox) {
|
||
xmlWriter.writeStartElement("CommandList");
|
||
QTableWidget *tableWidget = commandListBox->findChild<QTableWidget *>();
|
||
if (tableWidget) {
|
||
for (int row = 0; row < tableWidget->rowCount(); ++row) {
|
||
if (!tableWidget->item(row, 0)->text().isEmpty()) {
|
||
xmlWriter.writeStartElement("Command");
|
||
for (int col = 0; col < tableWidget->columnCount(); ++col) {
|
||
QTableWidgetItem *item = tableWidget->item(row, col);
|
||
if (item) {
|
||
xmlWriter.writeAttribute(tableWidget->horizontalHeaderItem(col)->text(),
|
||
item->text());
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // Command
|
||
}
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // CommandList
|
||
}
|
||
|
||
// 保存Other Info部分
|
||
QGroupBox *otherInfoBox = findChild<QGroupBox *>("Other Info");
|
||
if (otherInfoBox) {
|
||
// 创建递归处理GroupBox的lambda函数
|
||
std::function<void(QGridLayout *)> processGroupBox = [&](QGridLayout *layout) {
|
||
for (int i = 0; i < layout->rowCount(); ++i) {
|
||
QLayoutItem *labelItem = layout->itemAtPosition(i, 0);
|
||
QLayoutItem *valueItem = layout->itemAtPosition(i, 1);
|
||
|
||
if (labelItem && valueItem) {
|
||
QLabel *label = qobject_cast<QLabel *>(labelItem->widget());
|
||
if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(valueItem->widget())) {
|
||
if (label && !lineEdit->text().isEmpty()) {
|
||
// 写入最终元素及其值
|
||
xmlWriter.writeStartElement(label->text());
|
||
xmlWriter.writeCharacters(lineEdit->text());
|
||
xmlWriter.writeEndElement();
|
||
}
|
||
}
|
||
} else if (QGroupBox *groupBox =
|
||
qobject_cast<QGroupBox *>(layout->itemAt(i)->widget())) {
|
||
// 递归处理子GroupBox
|
||
xmlWriter.writeStartElement(groupBox->title());
|
||
QGridLayout *subLayout = qobject_cast<QGridLayout *>(groupBox->layout());
|
||
if (subLayout) {
|
||
processGroupBox(subLayout);
|
||
}
|
||
xmlWriter.writeEndElement();
|
||
}
|
||
}
|
||
};
|
||
|
||
// 开始处理Other Info GroupBox
|
||
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(otherInfoBox->layout());
|
||
if (gridLayout) {
|
||
processGroupBox(gridLayout);
|
||
}
|
||
}
|
||
|
||
xmlWriter.writeEndElement(); // Model
|
||
xmlWriter.writeEndDocument();
|
||
file.close();
|
||
}
|
||
|
||
// 保存服务配置文件
|
||
void MainWindow::saveServiceFile(QString filePath)
|
||
{
|
||
QFile file(filePath);
|
||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||
QMessageBox::warning(this, tr("Error"), tr("Failed to save file."));
|
||
return;
|
||
}
|
||
|
||
QXmlStreamWriter xmlWriter(&file);
|
||
xmlWriter.setAutoFormatting(true);
|
||
xmlWriter.writeStartDocument();
|
||
xmlWriter.writeStartElement("Service");
|
||
|
||
// 保存Service Info部分
|
||
QGroupBox *serviceInfoBox = findChild<QGroupBox *>("Service Info");
|
||
if (serviceInfoBox) {
|
||
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(serviceInfoBox->layout());
|
||
if (gridLayout) {
|
||
for (int i = 0; i < gridLayout->rowCount(); ++i) {
|
||
QLayoutItem *labelItem = gridLayout->itemAtPosition(i, 0);
|
||
QLayoutItem *valueItem = gridLayout->itemAtPosition(i, 1);
|
||
|
||
if (labelItem && valueItem) {
|
||
QLabel *label = qobject_cast<QLabel *>(labelItem->widget());
|
||
if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(valueItem->widget())) {
|
||
if (label && !lineEdit->text().isEmpty()) {
|
||
xmlWriter.writeStartElement(label->text());
|
||
xmlWriter.writeCharacters(lineEdit->text());
|
||
xmlWriter.writeEndElement();
|
||
}
|
||
} else if (QDateTimeEdit *dateTimeEdit =
|
||
qobject_cast<QDateTimeEdit *>(valueItem->widget())) {
|
||
if (label) {
|
||
xmlWriter.writeStartElement(label->text());
|
||
xmlWriter.writeCharacters(
|
||
dateTimeEdit->dateTime().toString(Qt::ISODate));
|
||
xmlWriter.writeEndElement();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 保存CommandList部分
|
||
QGroupBox *commandListBox = findChild<QGroupBox *>("CommandList");
|
||
if (commandListBox) {
|
||
xmlWriter.writeStartElement("CommandList");
|
||
QTableWidget *tableWidget = commandListBox->findChild<QTableWidget *>();
|
||
if (tableWidget) {
|
||
for (int row = 0; row < tableWidget->rowCount(); ++row) {
|
||
// 只保存非空行
|
||
if (!tableWidget->item(row, 0)->text().isEmpty()) {
|
||
xmlWriter.writeStartElement("Command");
|
||
for (int col = 0; col < tableWidget->columnCount(); ++col) {
|
||
QTableWidgetItem *item = tableWidget->item(row, col);
|
||
if (item) {
|
||
xmlWriter.writeAttribute(tableWidget->horizontalHeaderItem(col)->text(),
|
||
item->text());
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // Command
|
||
}
|
||
}
|
||
}
|
||
xmlWriter.writeEndElement(); // CommandList
|
||
}
|
||
|
||
// 保存Other Info部分
|
||
QGroupBox *otherInfoBox = findChild<QGroupBox *>("Other Info");
|
||
if (otherInfoBox) {
|
||
// 创建递归处理GroupBox的lambda函数
|
||
std::function<void(QGridLayout *)> processGroupBox = [&](QGridLayout *layout) {
|
||
for (int i = 0; i < layout->rowCount(); ++i) {
|
||
QLayoutItem *labelItem = layout->itemAtPosition(i, 0);
|
||
QLayoutItem *valueItem = layout->itemAtPosition(i, 1);
|
||
|
||
if (labelItem && valueItem) {
|
||
QLabel *label = qobject_cast<QLabel *>(labelItem->widget());
|
||
if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(valueItem->widget())) {
|
||
if (label && !lineEdit->text().isEmpty()) {
|
||
// 写入最终元素及其值
|
||
xmlWriter.writeStartElement(label->text());
|
||
xmlWriter.writeCharacters(lineEdit->text());
|
||
xmlWriter.writeEndElement();
|
||
}
|
||
}
|
||
} else if (QGroupBox *groupBox =
|
||
qobject_cast<QGroupBox *>(layout->itemAt(i)->widget())) {
|
||
// 递归处理子GroupBox
|
||
xmlWriter.writeStartElement(groupBox->title());
|
||
QGridLayout *subLayout = qobject_cast<QGridLayout *>(groupBox->layout());
|
||
if (subLayout) {
|
||
processGroupBox(subLayout);
|
||
}
|
||
xmlWriter.writeEndElement();
|
||
}
|
||
}
|
||
};
|
||
|
||
// 开始处理Other Info GroupBox
|
||
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(otherInfoBox->layout());
|
||
if (gridLayout) {
|
||
processGroupBox(gridLayout);
|
||
}
|
||
}
|
||
|
||
xmlWriter.writeEndElement(); // Service
|
||
xmlWriter.writeEndDocument();
|
||
file.close();
|
||
}
|
||
|
||
// 保存IDL文件
|
||
void MainWindow::saveIDLFile(QString filePath)
|
||
{
|
||
//TODO 还没实现
|
||
}
|
||
|
||
// 在新窗口打开文件
|
||
void MainWindow::openFileInNewWindow(QString fileName)
|
||
{
|
||
MainWindow *newWindow = new MainWindow(fileName, nullptr);
|
||
newWindow->show();
|
||
}
|
||
|
||
// 添加一个辅助函数来连接控件的修改信号
|
||
void MainWindow::connectModificationSignals(QWidget *widget)
|
||
{
|
||
if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget)) {
|
||
connect(lineEdit, &QLineEdit::textChanged, [this]() {
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
});
|
||
} else if (QCheckBox *checkBox = qobject_cast<QCheckBox *>(widget)) {
|
||
connect(checkBox, &QCheckBox::checkStateChanged, [this]() {
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
});
|
||
} else if (QTableWidget *tableWidget = qobject_cast<QTableWidget *>(widget)) {
|
||
connect(tableWidget, &QTableWidget::itemChanged, [this]() {
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
});
|
||
} else if (QDateTimeEdit *dateTimeEdit = qobject_cast<QDateTimeEdit *>(widget)) {
|
||
connect(dateTimeEdit, &QDateTimeEdit::dateTimeChanged, [this]() {
|
||
isModified = true;
|
||
updateWindowTitle();
|
||
});
|
||
}
|
||
// 递归处理子控件
|
||
for (QObject *child : widget->children()) {
|
||
if (QWidget *childWidget = qobject_cast<QWidget *>(child)) {
|
||
connectModificationSignals(childWidget);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加一个辅助函数来更新标题
|
||
void MainWindow::updateWindowTitle()
|
||
{
|
||
QString title = "XNEditor";
|
||
if (!currentFilePath.isEmpty()) {
|
||
title += " - " + QFileInfo(currentFilePath).fileName();
|
||
}
|
||
if (isModified) {
|
||
title += " *";
|
||
}
|
||
setWindowTitle(title);
|
||
}
|
||
|
||
// 窗口关闭事件的响应
|
||
void MainWindow::closeEvent(QCloseEvent *event)
|
||
{
|
||
if (isModified) {
|
||
onCloseFileTriggered();
|
||
// 如果用户选择了Cancel,我们需要阻止窗口关闭
|
||
if (isModified) {
|
||
event->ignore();
|
||
return;
|
||
}
|
||
}
|
||
event->accept();
|
||
}
|