|
@@ -0,0 +1,250 @@
|
|
|
|
|
+#include "MainWindow.h"
|
|
|
|
|
+#include "utils/StyleHelper.h"
|
|
|
|
|
+
|
|
|
|
|
+#include <QApplication>
|
|
|
|
|
+#include <QGroupBox>
|
|
|
|
|
+#include <QHBoxLayout>
|
|
|
|
|
+#include <QScrollBar>
|
|
|
|
|
+#include <QVBoxLayout>
|
|
|
|
|
+
|
|
|
|
|
+MainWindow::MainWindow(QWidget *parent)
|
|
|
|
|
+ : QMainWindow(parent), m_process(new QProcess(this)) {
|
|
|
|
|
+ initUI();
|
|
|
|
|
+
|
|
|
|
|
+ // 信号连接
|
|
|
|
|
+ connect(m_bootTypeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
|
|
|
+ this, &MainWindow::onBootTypeChanged);
|
|
|
|
|
+ connect(m_numberInput, &QLineEdit::textChanged, this,
|
|
|
|
|
+ &MainWindow::onNumberChanged);
|
|
|
|
|
+ connect(m_executeBtn, &QPushButton::clicked, this,
|
|
|
|
|
+ &MainWindow::onExecuteClicked);
|
|
|
|
|
+ connect(m_process, &QProcess::readyReadStandardOutput, this,
|
|
|
|
|
+ &MainWindow::onProcessOutput);
|
|
|
|
|
+ connect(m_process, &QProcess::readyReadStandardError, this,
|
|
|
|
|
+ &MainWindow::onProcessOutput);
|
|
|
|
|
+ connect(m_process,
|
|
|
|
|
+ QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
|
|
|
|
|
+ &MainWindow::onProcessFinished);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+MainWindow::~MainWindow() {
|
|
|
|
|
+ if (m_process->state() != QProcess::NotRunning) {
|
|
|
|
|
+ m_process->kill();
|
|
|
|
|
+ m_process->waitForFinished(3000);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::initUI() {
|
|
|
|
|
+ setWindowTitle(QString::fromUtf8("RunTest 命令启动器"));
|
|
|
|
|
+ setMinimumSize(700, 550);
|
|
|
|
|
+ resize(800, 600);
|
|
|
|
|
+
|
|
|
|
|
+ // 应用全局样式
|
|
|
|
|
+ qApp->setStyleSheet(StyleHelper::getGlobalStyle());
|
|
|
|
|
+
|
|
|
|
|
+ // 根 Widget
|
|
|
|
|
+ QWidget *rootWidget = new QWidget(this);
|
|
|
|
|
+ setCentralWidget(rootWidget);
|
|
|
|
|
+ QVBoxLayout *rootLayout = new QVBoxLayout(rootWidget);
|
|
|
|
|
+ rootLayout->setContentsMargins(24, 24, 24, 24);
|
|
|
|
|
+ rootLayout->setSpacing(16);
|
|
|
|
|
+
|
|
|
|
|
+ // ================== 标题 ==================
|
|
|
|
|
+ QLabel *titleLabel = new QLabel(QString::fromUtf8("🖥 RunTest 命令启动器"));
|
|
|
|
|
+ titleLabel->setStyleSheet(
|
|
|
|
|
+ "font-size: 26px; font-weight: bold; color: #00d4ff; padding: 8px 0;");
|
|
|
|
|
+ titleLabel->setAlignment(Qt::AlignCenter);
|
|
|
|
|
+ rootLayout->addWidget(titleLabel);
|
|
|
|
|
+
|
|
|
|
|
+ // ================== 配置区域 ==================
|
|
|
|
|
+ QHBoxLayout *configLayout = new QHBoxLayout();
|
|
|
|
|
+ configLayout->setSpacing(16);
|
|
|
|
|
+
|
|
|
|
|
+ // -- 启动类型 --
|
|
|
|
|
+ QVBoxLayout *bootTypeLayout = new QVBoxLayout();
|
|
|
|
|
+ QLabel *bootTypeTitle = new QLabel(QString::fromUtf8("⚙ 启动类型(必选)"));
|
|
|
|
|
+ bootTypeTitle->setStyleSheet(StyleHelper::getSectionTitleStyle());
|
|
|
|
|
+
|
|
|
|
|
+ m_bootTypeCombo = new QComboBox();
|
|
|
|
|
+ m_bootTypeCombo->addItem(QString::fromUtf8("— 请选择启动类型 —"), "");
|
|
|
|
|
+ m_bootTypeCombo->addItem("warnboot", "warnboot");
|
|
|
|
|
+ m_bootTypeCombo->addItem("coldboot", "coldboot");
|
|
|
|
|
+ m_bootTypeCombo->setStyleSheet(StyleHelper::getComboBoxStyle());
|
|
|
|
|
+ m_bootTypeCombo->setCurrentIndex(0);
|
|
|
|
|
+
|
|
|
|
|
+ bootTypeLayout->addWidget(bootTypeTitle);
|
|
|
|
|
+ bootTypeLayout->addWidget(m_bootTypeCombo);
|
|
|
|
|
+
|
|
|
|
|
+ // -- 数字参数 --
|
|
|
|
|
+ QVBoxLayout *numberLayout = new QVBoxLayout();
|
|
|
|
|
+ QLabel *numberTitle =
|
|
|
|
|
+ new QLabel(QString::fromUtf8("🔢 参数(手动输入数字)"));
|
|
|
|
|
+ numberTitle->setStyleSheet(StyleHelper::getSectionTitleStyle());
|
|
|
|
|
+
|
|
|
|
|
+ m_numberInput = new QLineEdit();
|
|
|
|
|
+ m_numberInput->setPlaceholderText(QString::fromUtf8("输入数字,例如 5"));
|
|
|
|
|
+ m_numberInput->setStyleSheet(StyleHelper::getInputStyle());
|
|
|
|
|
+
|
|
|
|
|
+ numberLayout->addWidget(numberTitle);
|
|
|
|
|
+ numberLayout->addWidget(m_numberInput);
|
|
|
|
|
+
|
|
|
|
|
+ configLayout->addLayout(bootTypeLayout, 1);
|
|
|
|
|
+ configLayout->addLayout(numberLayout, 1);
|
|
|
|
|
+ rootLayout->addLayout(configLayout);
|
|
|
|
|
+
|
|
|
|
|
+ // ================== 命令预览 ==================
|
|
|
|
|
+ QLabel *previewTitle = new QLabel(QString::fromUtf8("📋 命令预览"));
|
|
|
|
|
+ previewTitle->setStyleSheet(StyleHelper::getSectionTitleStyle());
|
|
|
|
|
+
|
|
|
|
|
+ m_commandPreview =
|
|
|
|
|
+ new QLabel(QString::fromUtf8("请先选择启动类型并输入数字"));
|
|
|
|
|
+ m_commandPreview->setStyleSheet(StyleHelper::getPreviewStyle());
|
|
|
|
|
+ m_commandPreview->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
|
+ m_commandPreview->setMinimumHeight(50);
|
|
|
|
|
+ m_commandPreview->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
|
|
|
|
+
|
|
|
|
|
+ rootLayout->addWidget(previewTitle);
|
|
|
|
|
+ rootLayout->addWidget(m_commandPreview);
|
|
|
|
|
+
|
|
|
|
|
+ // ================== 执行按钮 + 状态 ==================
|
|
|
|
|
+ QHBoxLayout *actionLayout = new QHBoxLayout();
|
|
|
|
|
+
|
|
|
|
|
+ m_executeBtn = new QPushButton(QString::fromUtf8("▶ 执行命令"));
|
|
|
|
|
+ m_executeBtn->setStyleSheet(StyleHelper::getExecuteButtonStyle());
|
|
|
|
|
+ m_executeBtn->setEnabled(false); // 初始禁用,需要先选择类型
|
|
|
|
|
+
|
|
|
|
|
+ m_statusLabel = new QLabel(QString::fromUtf8("⏳ 等待配置..."));
|
|
|
|
|
+ m_statusLabel->setStyleSheet(StyleHelper::getStatusReadyStyle());
|
|
|
|
|
+
|
|
|
|
|
+ actionLayout->addWidget(m_executeBtn);
|
|
|
|
|
+ actionLayout->addStretch();
|
|
|
|
|
+ actionLayout->addWidget(m_statusLabel);
|
|
|
|
|
+ rootLayout->addLayout(actionLayout);
|
|
|
|
|
+
|
|
|
|
|
+ // ================== 输出区域 ==================
|
|
|
|
|
+ QLabel *outputTitle = new QLabel(QString::fromUtf8("📟 命令输出"));
|
|
|
|
|
+ outputTitle->setStyleSheet(StyleHelper::getSectionTitleStyle());
|
|
|
|
|
+
|
|
|
|
|
+ m_outputArea = new QTextEdit();
|
|
|
|
|
+ m_outputArea->setReadOnly(true);
|
|
|
|
|
+ m_outputArea->setStyleSheet(StyleHelper::getOutputStyle());
|
|
|
|
|
+ m_outputArea->setPlaceholderText(
|
|
|
|
|
+ QString::fromUtf8("命令输出将显示在这里..."));
|
|
|
|
|
+
|
|
|
|
|
+ rootLayout->addWidget(outputTitle);
|
|
|
|
|
+ rootLayout->addWidget(m_outputArea, 1);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::updateCommandPreview() {
|
|
|
|
|
+ QString bootType = m_bootTypeCombo->currentData().toString();
|
|
|
|
|
+ QString number = m_numberInput->text().trimmed();
|
|
|
|
|
+
|
|
|
|
|
+ if (bootType.isEmpty()) {
|
|
|
|
|
+ m_commandPreview->setText(QString::fromUtf8("请先选择启动类型"));
|
|
|
|
|
+ m_executeBtn->setEnabled(false);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ QString cmd;
|
|
|
|
|
+ if (number.isEmpty()) {
|
|
|
|
|
+ cmd = QString("sudo ./RunTest.sh %1").arg(bootType);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ cmd = QString("sudo ./RunTest.sh %1 %2").arg(bootType, number);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ m_commandPreview->setText(cmd);
|
|
|
|
|
+ m_executeBtn->setEnabled(true);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::onBootTypeChanged() { updateCommandPreview(); }
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::onNumberChanged() { updateCommandPreview(); }
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::onExecuteClicked() {
|
|
|
|
|
+ if (m_process->state() != QProcess::NotRunning) {
|
|
|
|
|
+ // 正在运行 → 停止
|
|
|
|
|
+ m_process->kill();
|
|
|
|
|
+ m_statusLabel->setText(QString::fromUtf8("⛔ 已停止"));
|
|
|
|
|
+ m_statusLabel->setStyleSheet(StyleHelper::getStatusErrorStyle());
|
|
|
|
|
+ m_executeBtn->setText(QString::fromUtf8("▶ 执行命令"));
|
|
|
|
|
+ m_executeBtn->setStyleSheet(StyleHelper::getExecuteButtonStyle());
|
|
|
|
|
+ m_bootTypeCombo->setEnabled(true);
|
|
|
|
|
+ m_numberInput->setEnabled(true);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 清空输出
|
|
|
|
|
+ m_outputArea->clear();
|
|
|
|
|
+
|
|
|
|
|
+ QString bootType = m_bootTypeCombo->currentData().toString();
|
|
|
|
|
+ QString number = m_numberInput->text().trimmed();
|
|
|
|
|
+
|
|
|
|
|
+ // 构建参数列表
|
|
|
|
|
+ QStringList args;
|
|
|
|
|
+ args << "./RunTest.sh" << bootType;
|
|
|
|
|
+ if (!number.isEmpty()) {
|
|
|
|
|
+ args << number;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 追加命令到输出区显示
|
|
|
|
|
+ QString fullCmd = "sudo " + args.join(" ");
|
|
|
|
|
+ m_outputArea->append(
|
|
|
|
|
+ QString::fromUtf8("<span style='color:#ffd700;'>$ %1</span>")
|
|
|
|
|
+ .arg(fullCmd));
|
|
|
|
|
+ m_outputArea->append("");
|
|
|
|
|
+
|
|
|
|
|
+ // 更新 UI 状态
|
|
|
|
|
+ m_statusLabel->setText(QString::fromUtf8("🔄 正在执行..."));
|
|
|
|
|
+ m_statusLabel->setStyleSheet(StyleHelper::getStatusRunningStyle());
|
|
|
|
|
+ m_executeBtn->setText(QString::fromUtf8("⏹ 停止"));
|
|
|
|
|
+ m_executeBtn->setStyleSheet(StyleHelper::getStopButtonStyle());
|
|
|
|
|
+ m_bootTypeCombo->setEnabled(false);
|
|
|
|
|
+ m_numberInput->setEnabled(false);
|
|
|
|
|
+
|
|
|
|
|
+ // 启动进程
|
|
|
|
|
+ m_process->start("sudo", args);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::onProcessOutput() {
|
|
|
|
|
+ // 读取标准输出
|
|
|
|
|
+ QByteArray stdOut = m_process->readAllStandardOutput();
|
|
|
|
|
+ if (!stdOut.isEmpty()) {
|
|
|
|
|
+ m_outputArea->append(QString::fromUtf8(stdOut).trimmed());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 读取标准错误
|
|
|
|
|
+ QByteArray stdErr = m_process->readAllStandardError();
|
|
|
|
|
+ if (!stdErr.isEmpty()) {
|
|
|
|
|
+ m_outputArea->append(QString("<span style='color:#f44336;'>%1</span>")
|
|
|
|
|
+ .arg(QString::fromUtf8(stdErr).trimmed()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 自动滚动到底部
|
|
|
|
|
+ m_outputArea->verticalScrollBar()->setValue(
|
|
|
|
|
+ m_outputArea->verticalScrollBar()->maximum());
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::onProcessFinished(int exitCode,
|
|
|
|
|
+ QProcess::ExitStatus exitStatus) {
|
|
|
|
|
+ // 恢复 UI
|
|
|
|
|
+ m_executeBtn->setText(QString::fromUtf8("▶ 执行命令"));
|
|
|
|
|
+ m_executeBtn->setStyleSheet(StyleHelper::getExecuteButtonStyle());
|
|
|
|
|
+ m_bootTypeCombo->setEnabled(true);
|
|
|
|
|
+ m_numberInput->setEnabled(true);
|
|
|
|
|
+
|
|
|
|
|
+ if (exitStatus == QProcess::NormalExit && exitCode == 0) {
|
|
|
|
|
+ m_statusLabel->setText(QString::fromUtf8("✅ 执行完成 (exit code: 0)"));
|
|
|
|
|
+ m_statusLabel->setStyleSheet(StyleHelper::getStatusDoneStyle());
|
|
|
|
|
+ m_outputArea->append(QString::fromUtf8(
|
|
|
|
|
+ "\n<span style='color:#00ff88;'>--- 命令执行成功 ---</span>"));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ m_statusLabel->setText(
|
|
|
|
|
+ QString::fromUtf8("❌ 执行失败 (exit code: %1)").arg(exitCode));
|
|
|
|
|
+ m_statusLabel->setStyleSheet(StyleHelper::getStatusErrorStyle());
|
|
|
|
|
+ m_outputArea->append(
|
|
|
|
|
+ QString::fromUtf8(
|
|
|
|
|
+ "\n<span style='color:#f44336;'>--- 命令执行失败 (exit code: "
|
|
|
|
|
+ "%1) ---</span>")
|
|
|
|
|
+ .arg(exitCode));
|
|
|
|
|
+ }
|
|
|
|
|
+}
|