XNSim/XNRunner/mainwindow.cpp
2025-04-28 12:25:20 +08:00

463 lines
14 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QProcess>
#include "MonitorThread.h"
#include <QRegularExpression>
#include <QTextCharFormat>
#include <QTextCursor>
#include <QScrollBar>
#include <QTimer>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow), userScrolled(false)
{
ui->setupUi(this);
// 关闭菜单栏和状态栏
menuBar()->hide();
statusBar()->hide();
setWindowTitle("XNRunner");
//设置窗口图标
setWindowIcon(QIcon(":/icon/XNRunner.png"));
// 设置窗口字体
QFont appFont("Arial", 14);
QApplication::setFont(appFont);
// Create widgets
QLabel *label = new QLabel("Environment Configuration", this);
//label->setFixedSize(200, 30);
QComboBox *comboBox = new QComboBox(this);
QPushButton *runPauseButton = new QPushButton("Run", this);
runPauseButton->setFixedSize(100, 30);
runPauseButton->setObjectName("runPauseButton");
// 连接按钮点击信号到槽函数
connect(runPauseButton, &QPushButton::clicked, this, &MainWindow::OnRunPauseButtonClicked);
QPushButton *endButton = new QPushButton("Abort", this);
endButton->setFixedSize(100, 30);
endButton->setObjectName("endButton");
// 连接按钮点击信号到槽函数
connect(endButton, &QPushButton::clicked, this, &MainWindow::OnEndButtonClicked);
QTreeWidget *modelTree = new QTreeWidget(this);
modelTree->setObjectName("modelTree");
modelTree->setHeaderLabel("Models List");
modelTree->setMaximumWidth(200);
QTreeWidget *serviceTree = new QTreeWidget(this);
serviceTree->setObjectName("serviceTree");
serviceTree->setHeaderLabel("Services List");
serviceTree->setMaximumWidth(200);
QTextEdit *textEdit = new QTextEdit(this);
textEdit->setObjectName("textEdit");
textEdit->setReadOnly(true);
// 设置文本编辑器的编码格式
//QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
// 连接滚动条信号以检测用户滚动
connect(textEdit->verticalScrollBar(), &QScrollBar::valueChanged, this,
&MainWindow::onTextEditScrolled);
QProgressBar *progressBar = new QProgressBar(this);
progressBar->setObjectName("progressBar");
// 布局设置
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *topLayout = new QHBoxLayout;
QHBoxLayout *middleLayout = new QHBoxLayout;
// 上部分布局
topLayout->addWidget(label);
topLayout->addWidget(comboBox);
topLayout->addWidget(runPauseButton);
topLayout->addWidget(endButton);
// 中间部分布局
QVBoxLayout *treeLayout = new QVBoxLayout;
treeLayout->addWidget(modelTree);
treeLayout->addWidget(serviceTree);
middleLayout->addLayout(treeLayout);
middleLayout->addWidget(textEdit);
// 添加布局到主布局
mainLayout->addLayout(topLayout);
mainLayout->addLayout(middleLayout);
mainLayout->addWidget(progressBar);
// 设置中心窗口的布局
QWidget *centralWidget = new QWidget(this);
centralWidget->setLayout(mainLayout);
setCentralWidget(centralWidget);
populateComboBoxWithFiles(comboBox);
currentRunStatus = 0;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::populateComboBoxWithFiles(QComboBox *comboBox)
{
comboBox->setObjectName("comboBox");
// 获取当前目录
QDir dir(QDir::currentPath() + "/Scenario");
// 获取所有 .sce 和 .xml 文件
QStringList filters;
filters << "*.sce" << "*.xml";
dir.setNameFilters(filters);
dir.setFilter(QDir::Files);
QFileInfoList fileList = dir.entryInfoList();
// 将文件名添加到 comboBox
QFileInfo latestFile;
for (const QFileInfo &fileInfo : fileList) {
comboBox->addItem(fileInfo.fileName());
// 找到最新的文件
if (latestFile.lastModified() < fileInfo.lastModified()) {
latestFile = fileInfo;
}
}
// 默认选中最新的文件
QTimer::singleShot(1000, [this, comboBox, latestFile]() {
// 先设置一个不同的索引
if (comboBox->count() > 0) {
comboBox->setCurrentIndex(-1); // 设置为无选中项
}
connect(comboBox, &QComboBox::currentIndexChanged, this,
&MainWindow::OnComboBoxCurrentIndexChanged);
if (!latestFile.fileName().isEmpty()) {
comboBox->setCurrentText(latestFile.fileName());
}
});
}
void MainWindow::OnRunPauseButtonClicked()
{
if (currentRunStatus == 0) {
QComboBox *comboBox = findChild<QComboBox *>("comboBox");
if (comboBox) {
QTextEdit *textEdit = findChild<QTextEdit *>("textEdit");
if (textEdit) {
textEdit->clear();
}
QProcess *process = new QProcess(this);
process->setObjectName("engineProcess");
QString selectedFile = comboBox->currentText();
QDir dir(QDir::currentPath() + "/Scenario");
QStringList args;
args << dir.filePath(selectedFile);
process->start("./XNEngine", args);
// Connect standard output
connect(process, &QProcess::readyReadStandardOutput, this, &MainWindow::OnEngineOutput);
// Connect standard error
connect(process, &QProcess::readyReadStandardError, this,
&MainWindow::OnEngineErrorOutput);
MonitorThread *monitorThread = new MonitorThread(this);
monitorThread->setObjectName("monitorThread");
connect(monitorThread, &MonitorThread::runStatusChanged, this,
&MainWindow::OnRunStatusChanged);
connect(this, &MainWindow::Pause, monitorThread, &MonitorThread::OnPause);
connect(this, &MainWindow::Continue, monitorThread, &MonitorThread::OnContinue);
connect(this, &MainWindow::Abort, monitorThread, &MonitorThread::OnAbort);
connect(monitorThread, &MonitorThread::abortProcess, this, &MainWindow::OnEndProcess);
monitorThread->start();
}
} else if (currentRunStatus == 1) {
emit Pause();
} else if (currentRunStatus == 2) {
emit Continue();
}
OnRunStatusChanged(currentRunStatus);
}
void MainWindow::OnEngineOutput()
{
QProcess *process = findChild<QProcess *>("engineProcess");
if (process) {
QString output = QString::fromUtf8(process->readAllStandardOutput());
QTextEdit *textEdit = findChild<QTextEdit *>("textEdit");
if (textEdit) {
applyAnsiColors(textEdit, output);
}
}
}
void MainWindow::applyAnsiColors(QTextEdit *textEdit, const QString &text)
{
QRegularExpression regex("\033\\[([0-9;]+)m");
QRegularExpressionMatchIterator i = regex.globalMatch(text);
QTextCursor cursor(textEdit->textCursor());
cursor.movePosition(QTextCursor::End);
int lastPos = 0;
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
QStringList codes = match.captured(1).split(';');
QString color = "white"; // Default color
for (const QString &code : codes) {
if (code == "31")
color = "red";
else if (code == "32")
color = "green";
else if (code == "33")
color = "yellow";
else if (code == "34")
color = "blue";
else if (code == "35")
color = "magenta";
else if (code == "36")
color = "cyan";
else if (code == "37")
color = "white";
else if (code == "0")
color = "white"; // Reset to default
}
// Insert text before the ANSI code
cursor.insertText(text.mid(lastPos, match.capturedStart() - lastPos));
// Set the color for the text after the ANSI code
QTextCharFormat format;
format.setForeground(QColor(color));
cursor.setCharFormat(format);
lastPos = match.capturedEnd();
}
// Insert remaining text
cursor.insertText(text.mid(lastPos));
// 删除多余的行
limitTextEditLines(textEdit, 500);
// 如果用户没有手动滚动,则滚动到末尾
if (!userScrolled) {
cursor.movePosition(QTextCursor::End);
textEdit->setTextCursor(cursor);
textEdit->ensureCursorVisible();
}
}
void MainWindow::limitTextEditLines(QTextEdit *textEdit, int maxLines)
{
QTextDocument *doc = textEdit->document();
while (doc->blockCount() > maxLines) {
QTextCursor cursor(doc);
cursor.select(QTextCursor::BlockUnderCursor);
cursor.removeSelectedText();
cursor.deleteChar(); // Remove the newline character
}
}
void MainWindow::OnEngineErrorOutput()
{
QProcess *process = findChild<QProcess *>("engineProcess");
if (process) {
// 直接使用 fromUtf8 处理错误输出
QString errorOutput = QString::fromUtf8(process->readAllStandardError());
QTextEdit *textEdit = findChild<QTextEdit *>("textEdit");
if (textEdit) {
applyAnsiColors(textEdit, errorOutput);
}
}
}
void MainWindow::OnRunStatusChanged(int status)
{
QPushButton *runPauseButton = findChild<QPushButton *>("runPauseButton");
if (runPauseButton) {
if (status == 0) {
runPauseButton->setText("Run");
runPauseButton->setEnabled(true);
currentRunStatus = 0;
} else if (status == 1) {
runPauseButton->setText("Pause");
runPauseButton->setEnabled(true);
currentRunStatus = 1;
} else if (status == 2) {
runPauseButton->setText("Continue");
runPauseButton->setEnabled(true);
currentRunStatus = 2;
} else if (status == 3) {
runPauseButton->setText("Run");
runPauseButton->setEnabled(true);
currentRunStatus = 0;
} else {
runPauseButton->setEnabled(false);
currentRunStatus = 0;
}
}
}
void MainWindow::OnEndButtonClicked()
{
if (currentRunStatus != 0) {
emit Abort();
OnEndProcess();
}
}
void MainWindow::OnEndProcess()
{
QProcess *process = this->findChild<QProcess *>("engineProcess");
QTextEdit *textEdit = this->findChild<QTextEdit *>("textEdit");
if (process) {
QTimer::singleShot(30000, [this, process, textEdit]() {
if (textEdit) {
textEdit->append("XNEngine has been forcibly terminated!");
}
process->kill();
});
process->waitForFinished();
if (textEdit) {
textEdit->append("XNEngine exited normally!");
}
delete process;
}
MonitorThread *monitorThread = this->findChild<MonitorThread *>("monitorThread");
if (monitorThread) {
monitorThread->wait();
delete monitorThread;
}
OnRunStatusChanged(3);
}
void MainWindow::OnComboBoxCurrentIndexChanged(int index)
{
// 处理 comboBox 的当前索引改变事件
QComboBox *comboBox = findChild<QComboBox *>("comboBox");
if (comboBox) {
comboBox->setEnabled(false); // Disable the combo box
QTreeWidget *modelTree = findChild<QTreeWidget *>("modelTree");
if (modelTree) {
modelTree->clear();
}
QTreeWidget *serviceTree = findChild<QTreeWidget *>("serviceTree");
if (serviceTree) {
serviceTree->clear();
}
QString selectedFile = comboBox->currentText();
// 更新文本编辑器或进度条
QPushButton *runPauseButton = findChild<QPushButton *>("runPauseButton");
if (runPauseButton) {
runPauseButton->setEnabled(false); // Disable the run/pause button
}
QDir dir(QDir::currentPath() + "/Scenario");
QFile file(dir.filePath(selectedFile));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::warning(this, "Warning", "Failed to open the file!");
return;
}
QString xmlContent = file.readAll();
file.close();
// Process the XML content as needed
// For example, you could parse it using QDomDocument or any other XML parser
QDomDocument xmlDoc;
if (!xmlDoc.setContent(xmlContent)) {
// Handle error parsing the XML
comboBox->setEnabled(true); // Enable the combo box
if (runPauseButton) {
runPauseButton->setEnabled(true); // Enable the run/pause button
}
QMessageBox::warning(this, "Warning", "Failed to parse the XML file!");
return;
}
// TODO: 解析xml文件获取模型和服务的清单,并启动校验
QDomElement root = xmlDoc.documentElement();
if (modelTree) {
for (int i = 0; i < root.elementsByTagName("ModelGroup").count(); i++) {
QDomElement models = root.elementsByTagName("ModelGroup").at(i).toElement();
QString modelGroupName = models.attribute("Name");
modelTree->addTopLevelItem(new QTreeWidgetItem(QStringList() << modelGroupName));
for (QDomElement model = models.firstChildElement("Model"); !model.isNull();
model = model.nextSiblingElement("Model")) {
QString modelName = model.attribute("Name");
//添加到modelTree的子节点
modelTree->topLevelItem(i)->addChild(
new QTreeWidgetItem(QStringList() << modelName));
}
//默认展开modelTree的第一个子节点
modelTree->topLevelItem(i)->setExpanded(true);
}
}
if (serviceTree) {
QDomElement services = root.firstChildElement("ServicesList");
for (QDomElement service = services.firstChildElement("Service"); !service.isNull();
service = service.nextSiblingElement("Service")) {
QString serviceName = service.attribute("Name");
serviceTree->addTopLevelItem(new QTreeWidgetItem(QStringList() << serviceName));
}
}
QTextEdit *textEdit = findChild<QTextEdit *>("textEdit");
if (textEdit) {
textEdit->clear();
}
QProcess *process = new QProcess(this);
process->setObjectName("engineProcess");
QStringList args;
args << dir.filePath(selectedFile) << "-test";
connect(process, &QProcess::readyReadStandardOutput, this, &MainWindow::OnEngineOutput);
connect(process, &QProcess::readyReadStandardError, this, &MainWindow::OnEngineErrorOutput);
QTimer::singleShot(1000, [=]() {
process->start("./XNEngine", args);
process->waitForFinished();
comboBox->setEnabled(true); // Enable the combo box
if (runPauseButton) {
runPauseButton->setEnabled(true); // Enable the run/pause button
}
delete process;
});
}
}
void MainWindow::onTextEditScrolled(int value)
{
QScrollBar *scrollBar = qobject_cast<QScrollBar *>(sender());
if (scrollBar) {
int max = scrollBar->maximum();
int threshold = 5; // 允许的误差值
// 如果滚动条接近底部,设置 userScrolled 为 false
userScrolled = (value < max - threshold);
// 如果用户滚动到接近末尾,重置 userScrolled
if (value >= max - threshold) {
userScrolled = false;
}
}
}