diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cd40e24 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,26 @@ +{ + "cmake.sourceDirectory": "D:/gitfile/analysis_tool/src", + "files.associations": { + "new": "cpp", + "__locale": "cpp", + "ios": "cpp", + "queue": "cpp", + "stack": "cpp", + "__config": "cpp", + "__bit_reference": "cpp", + "__hash_table": "cpp", + "__split_buffer": "cpp", + "__tree": "cpp", + "array": "cpp", + "deque": "cpp", + "initializer_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "string_view": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp" + } +} \ No newline at end of file diff --git a/DSCAnalysisTool-release/bin/log/20251201.log b/DSCAnalysisTool-release/bin/log/20251201.log new file mode 100644 index 0000000..f2c04df --- /dev/null +++ b/DSCAnalysisTool-release/bin/log/20251201.log @@ -0,0 +1,5 @@ +[2025-12-01 10:46:10,897] main... +[2025-12-01 10:46:10,899] config file existed. +[2025-12-01 10:46:10,936] version:1.3.5.1 +[2025-12-01 10:46:10,980] setEventHandlerEnable...0 +[2025-12-01 10:46:31,805] serialport destructor. diff --git a/experiment_data/analysis_state/03-111.xlsx b/experiment_data/analysis_state/03-111.xlsx deleted file mode 100644 index 063fbd7..0000000 Binary files a/experiment_data/analysis_state/03-111.xlsx and /dev/null differ diff --git a/experiment_data/analysis_state/13-2-new.xlsx b/experiment_data/analysis_state/13-2-new.xlsx deleted file mode 100644 index 76ba3b7..0000000 Binary files a/experiment_data/analysis_state/13-2-new.xlsx and /dev/null differ diff --git a/experiment_data/analysis_state/16-2-new.xlsx b/experiment_data/analysis_state/16-2-new.xlsx deleted file mode 100644 index 144edec..0000000 Binary files a/experiment_data/analysis_state/16-2-new.xlsx and /dev/null differ diff --git a/experiment_data/sample_data/1111new.xlsx b/experiment_data/sample_data/1111new.xlsx deleted file mode 100644 index 2bc3252..0000000 Binary files a/experiment_data/sample_data/1111new.xlsx and /dev/null differ diff --git a/experiment_data/sample_data/17-new.xlsx b/experiment_data/sample_data/17-new.xlsx deleted file mode 100644 index c47c0c1..0000000 Binary files a/experiment_data/sample_data/17-new.xlsx and /dev/null differ diff --git a/experiment_data/sample_data/19-6.xlsx b/experiment_data/sample_data/19-6.xlsx deleted file mode 100644 index a7cefc6..0000000 Binary files a/experiment_data/sample_data/19-6.xlsx and /dev/null differ diff --git a/experiment_data/sample_data/20升温.xlsx b/experiment_data/sample_data/20升温.xlsx deleted file mode 100644 index ca8d096..0000000 Binary files a/experiment_data/sample_data/20升温.xlsx and /dev/null differ diff --git a/experiment_data/sample_data/22222222.xlsx b/experiment_data/sample_data/22222222.xlsx deleted file mode 100644 index 3262af1..0000000 Binary files a/experiment_data/sample_data/22222222.xlsx and /dev/null differ diff --git a/experiment_data/sample_data/555.xlsx b/experiment_data/sample_data/555.xlsx deleted file mode 100644 index a7adb07..0000000 Binary files a/experiment_data/sample_data/555.xlsx and /dev/null differ diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index d0997cb..a3f8fa1 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -18,7 +18,7 @@ ], "externalConsole": false, "miDebuggerPath": "D:/qt/Qt5.14.2/5.14.2/mingw73_64/bin/gdb.exe", - "visualizerFile": "c:\\Users\\sunqu\\AppData\\Roaming\\Code\\User\\workspaceStorage\\7f93734217a2e383fe34beaf49d497fe\\tonka3000.qtvsctools\\qt.natvis.xml" + "visualizerFile": "c:\\Users\\sunqu\\AppData\\Roaming\\Trae CN\\User\\workspaceStorage\\7f93734217a2e383fe34beaf49d497fe\\tonka3000.qtvsctools\\qt.natvis.xml" } ] } \ No newline at end of file diff --git a/src/DSCAnalysisTool.pro b/src/DSCAnalysisTool.pro index c8e4fea..c41f6a6 100644 --- a/src/DSCAnalysisTool.pro +++ b/src/DSCAnalysisTool.pro @@ -9,7 +9,7 @@ CONFIG+=precompile_header PRECOMPILED_HEADER=stable.h # -VERSION = 1.3.7 +VERSION = 1.4.0 # 设置目标文件名,包含版本号 TARGET = DSCAnalysisTool_$${VERSION} diff --git a/src/global.cpp b/src/global.cpp index 7ab757a..55cd039 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -3,23 +3,24 @@ #include "global.h" -namespace Global { +namespace Global +{ Mode _mode = Mode::Analysis; AxisMode _axisMode = AxisMode::SingleY; QVector _curveFileDataVtr; - int _currentPhase = 0; - ExperimentInfo _experimentInfo; + int _currentPhase = 0; + ExperimentInfo _experimentInfo; QVector _curveExperimentDataVtr; - CurveExperimentData *_currentCurveExperimentDataPtr = nullptr; + CurveExperimentData *_currentCurveExperimentDataPtr = nullptr; - bool _smoothnessFlag = false; + bool _smoothnessFlag = false; QVector _curveSmoothnessDataVtr; - QString _smoothnessFileName; + QString _smoothnessFileName; - bool _enthalpyCoefficientEnableFlag = false; + bool _enthalpyCoefficientEnableFlag = false; QVector _enthalpyCoefficientVtr; bool _displayTimeValue = false; @@ -29,18 +30,21 @@ namespace Global { bool _experimentOITFlag = false; // OIT auto mode. - bool _OITAutoAnalysisModeFlag = false; + bool _OITAutoAnalysisModeFlag = false; double _OITAutoAnalysisCoefficient = OITAutoAnalysisDefaultCoefficient; - double _OITAutoAnalysisThreshold = OITAutoAnalysisDefaultThreshold; + double _OITAutoAnalysisThreshold = OITAutoAnalysisDefaultThreshold; // 实验文件 QStringList _fileList; - QString converDoubleToStr(const double num) { + QString converDoubleToStr(const double num) + { return QString::number(num, 'f', 3); } - void quadraticLeastSquaresFit(double x[], double y[], int n, double coeff[]) { - if (n < 3) { + void quadraticLeastSquaresFit(double x[], double y[], int n, double coeff[]) + { + if (n < 3) + { throw std::invalid_argument("At least 3 data points are required for quadratic fitting"); } @@ -48,12 +52,13 @@ namespace Global { double sum_y = 0, sum_xy = 0, sum_x2y = 0; // 计算各项累加和 - for (int i = 0; i < n; ++i) { - double xi = x[i]; + for (int i = 0; i < n; ++i) + { + double xi = x[i]; double xi2 = xi * xi; double xi3 = xi2 * xi; double xi4 = xi3 * xi; - double yi = y[i]; + double yi = y[i]; sum_x += xi; sum_x2 += xi2; @@ -66,50 +71,61 @@ namespace Global { // 构建正规方程组的增广矩阵 double matrix[3][4] = { - { sum_x4, sum_x3, sum_x2, sum_x2y }, // 对应方程: a*Σx⁴ + b*Σx³ + c*Σx² = Σx²y - { sum_x3, sum_x2, sum_x, sum_xy }, // 对应方程: a*Σx³ + b*Σx² + c*Σx = Σxy - { sum_x2, sum_x, static_cast(n), sum_y } // 对应方程: a*Σx² + b*Σx + c*n = Σy + {sum_x4, sum_x3, sum_x2, sum_x2y}, // 对应方程: a*Σx⁴ + b*Σx³ + c*Σx² = Σx²y + {sum_x3, sum_x2, sum_x, sum_xy}, // 对应方程: a*Σx³ + b*Σx² + c*Σx = Σxy + {sum_x2, sum_x, static_cast(n), sum_y} // 对应方程: a*Σx² + b*Σx + c*n = Σy }; // 高斯消元法解方程组 - for (int i = 0; i < 3; ++i) { + for (int i = 0; i < 3; ++i) + { // 部分主元选择 int maxRow = i; - for (int k = i + 1; k < 3; ++k) { - if (std::abs(matrix[k][i]) > std::abs(matrix[maxRow][i])) { + for (int k = i + 1; k < 3; ++k) + { + if (std::abs(matrix[k][i]) > std::abs(matrix[maxRow][i])) + { maxRow = k; } } // 交换行 - for (int k = i; k < 4; ++k) { + for (int k = i; k < 4; ++k) + { std::swap(matrix[i][k], matrix[maxRow][k]); } // 消元 - for (int k = i + 1; k < 3; ++k) { + for (int k = i + 1; k < 3; ++k) + { double factor = matrix[k][i] / matrix[i][i]; - for (int j = i; j < 4; ++j) { + for (int j = i; j < 4; ++j) + { matrix[k][j] -= factor * matrix[i][j]; } } } // 回代求解 - coeff[2] = matrix[2][3] / matrix[2][2]; // c - coeff[1] = (matrix[1][3] - matrix[1][2] * coeff[2]) / matrix[1][1]; // b - coeff[0] = (matrix[0][3] - matrix[0][2] * coeff[2] - matrix[0][1] * coeff[1]) / matrix[0][0]; // a + coeff[2] = matrix[2][3] / matrix[2][2]; // c + coeff[1] = (matrix[1][3] - matrix[1][2] * coeff[2]) / matrix[1][1]; // b + coeff[0] = (matrix[0][3] - matrix[0][2] * coeff[2] - matrix[0][1] * coeff[1]) / matrix[0][0]; // a } // 计算一元一次函数在给定区间内开始变成负值的点 - double findNegativeStartPoint(double m, double b, double start, double end) { + double findNegativeStartPoint(double m, double b, double start, double end) + { // 处理斜率为零的情况 - if (m == 0) { + if (m == 0) + { // 当斜率为 0 时,函数值为常数 b - if (b < 0) { - return start; // 若 b 为负,函数在区间起始点就为负 - } else { - return std::numeric_limits::infinity(); // 若 b 非负,函数在区间内不会变负 + if (b < 0) + { + return start; // 若 b 为负,函数在区间起始点就为负 + } + else + { + return std::numeric_limits::infinity(); // 若 b 非负,函数在区间内不会变负 } } @@ -117,20 +133,26 @@ namespace Global { double zeroPoint = -b / m; // 检查零点是否在给定区间内 - if (zeroPoint >= start && zeroPoint <= end) { - return zeroPoint; // 零点在区间内,函数在此点开始变负 + if (zeroPoint >= start && zeroPoint <= end) + { + return zeroPoint; // 零点在区间内,函数在此点开始变负 } // 根据斜率正负判断函数单调性,进而判断函数在区间内是否会变负 - if (m > 0) { + if (m > 0) + { // 斜率大于 0,函数单调递增 - if (m * start + b < 0) { - return start; // 起始点函数值为负,函数在起始点就为负 + if (m * start + b < 0) + { + return start; // 起始点函数值为负,函数在起始点就为负 } - } else { + } + else + { // 斜率小于 0,函数单调递减 - if (m * end + b < 0) { - return end; // 结束点函数值为负,函数在结束点开始为负 + if (m * end + b < 0) + { + return end; // 结束点函数值为负,函数在结束点开始为负 } } @@ -138,22 +160,26 @@ namespace Global { return std::numeric_limits::infinity(); } - void clearExperimentData() { + void clearExperimentData() + { _curveExperimentDataVtr.clear(); _currentCurveExperimentDataPtr = nullptr; - _currentPhase = -1; + _currentPhase = -1; } - bool isZero(double value, double epsilon) { + bool isZero(double value, double epsilon) + { return std::abs(value) < epsilon; } - bool isEqual(const double a, const double b) { - const double tolerance = 1e-5; // 容差值,可以根据需要调整 + bool isEqual(const double a, const double b) + { + const double tolerance = 1e-5; // 容差值,可以根据需要调整 return std::fabs(a - b) < tolerance; } - QString getFileName(const QString filePath) { + QString getFileName(const QString filePath) + { QFileInfo fileInfo(filePath); // 获取文件的后缀名并转换为小写,方便比较 @@ -162,35 +188,37 @@ namespace Global { return fileInfo.fileName(); } - double converStrToDouble(const QString str) { - bool ok; - double value = str.toDouble(&ok); // 将字符串转换为 double,并检查是否成功 - if (!ok) { + double converStrToDouble(const QString str) + { + bool ok; + double value = str.toDouble(&ok); // 将字符串转换为 double,并检查是否成功 + if (!ok) + { qDebug() << "转换失败,输入的字符串不是有效的数字"; - return 0.0; // 如果转换失败,返回默认值 + return 0.0; // 如果转换失败,返回默认值 } return value; } - bool isFileExist(const QString &fileName) { + bool isFileExist(const QString &fileName) + { return _fileList.contains(fileName); } - -void updateFileList() -{ - _fileList.clear(); + void updateFileList() + { + _fileList.clear(); - QDir dir(SampleDataFloder); - _fileList.append(dir.entryList(QDir::Files)); + QDir dir(SampleDataFloder); + _fileList.append(dir.entryList(QDir::Files)); #if 0 dir.setPath(BaseLineFolder); _fileList.append(dir.entryList(QDir::Files)); #endif - dir.setPath(AnalysisStateFolder); - _fileList.append(dir.entryList(QDir::Files)); -} + dir.setPath(AnalysisStateFolder); + _fileList.append(dir.entryList(QDir::Files)); + } -} // namespace Global +} // namespace Global #if 0 Global::Global() diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c89293a..f31eec1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -31,7 +31,8 @@ MainWindow::MainWindow(QWidget *parent) _coefficientSelectionForm(new CoefficientSelectionForm(this)), _printPreviewForm(new PrintPreviewForm(this)), _axisSettingForm(new AxisSettingForm(this)), - _manuallyStopTheExperimentFlag(false) { + _manuallyStopTheExperimentFlag(false) +{ ui->setupUi(this); ui->actionOITAutoAnalysisMode->setVisible(false); ui->toolBar->setWindowTitle("工具栏"); @@ -84,44 +85,58 @@ MainWindow::MainWindow(QWidget *parent) // ui->actionStop->setEnabled(false); } -MainWindow::~MainWindow() { +MainWindow::~MainWindow() +{ delete ui; } -void MainWindow::slotContextMenuShow(const QPoint point) { +void MainWindow::slotContextMenuShow(const QPoint point) +{ _contextMenu->exec(point); } -void MainWindow::slotUpdateStatusbarMsg(const QString msg) { +void MainWindow::slotUpdateStatusbarMsg(const QString msg) +{ ui->statusbar->showMessage(msg); } -void MainWindow::slotOITAutoAnalysis(const double x1, const double x2) { +void MainWindow::slotOITAutoAnalysis(const double x1, const double x2) +{ on_actionStop_triggered(); _centralWidget->setAnalysisMode(CentralWidget::AnalysisMode::OIT); _centralWidget->setVerticalLineRange(x1, x2); } -void MainWindow::slotStartExperiment() { +void MainWindow::slotStartExperiment() +{ startExperimentByDeviceInfo(); } -void MainWindow::closeEvent(QCloseEvent *event) { +void MainWindow::closeEvent(QCloseEvent *event) +{ // 弹出确认对话框 QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "确认退出", "你确定要退出吗?", QMessageBox::Yes | QMessageBox::No); - if (reply == QMessageBox::Yes) { - event->accept(); // 接受关闭事件,关闭窗口 - } else { - event->ignore(); // 忽略关闭事件,不关闭窗口 + if (reply == QMessageBox::Yes) + { + event->accept(); // 接受关闭事件,关闭窗口 + } + else + { + event->ignore(); // 忽略关闭事件,不关闭窗口 } } -void MainWindow::connections() { +void MainWindow::connections() +{ // ui connect(_expertmentSettingForm, &ExperimentSettingForm::sigDeliverData, SerialPort::instance(), &SerialPort::slotDeliverData); + + // 左侧文件管理子窗口相关函数 + connect(_leftWidget, &LeftWidget::sigDeleteActionTriggered, + this, &MainWindow::slotDeleteActionTriggered); #if 1 // SerialPort. connect(SerialPort::instance(), &SerialPort::sigSendCommonData, @@ -172,7 +187,8 @@ void MainWindow::connections() { _centralWidget, &CentralWidget::slotAnalysisSettingCancel); connect(_centralWidget, &CentralWidget::sigRightDockWidgetHide, - [&]() { _rightWidget->hide(); }); + [&]() + { _rightWidget->hide(); }); connect(_degreeOfCrystallinityForm, &DegreeOfCrystallinityForm::sigDrawCustomText, _centralWidget, &CentralWidget::slotDrawCustomText); @@ -196,13 +212,17 @@ void MainWindow::connections() { this, &MainWindow::slotOITAutoAnalysis); } -void MainWindow::setActionEnable(const bool flag) { - if (flag) { +void MainWindow::setActionEnable(const bool flag) +{ + if (flag) + { ui->actionNew->setEnabled(true); ui->actionStart->setEnabled(true); ui->actionStop->setEnabled(true); ui->actionRealTimeWidget->setEnabled(true); - } else { + } + else + { ui->actionNew->setEnabled(false); ui->actionStart->setEnabled(false); ui->actionStop->setEnabled(false); @@ -210,7 +230,8 @@ void MainWindow::setActionEnable(const bool flag) { } } -void MainWindow::setSubWidgetAttribute(QWidget *widget) { +void MainWindow::setSubWidgetAttribute(QWidget *widget) +{ widget->setWindowModality(Qt::ApplicationModal); widget->setWindowFlags(Qt::Dialog); @@ -262,48 +283,63 @@ bool MainWindow::saveAnalysisFile(const QString fileName) #endif bool MainWindow::saveFile(const QString fileName, const Global::Mode mode, - QString &finalFileName, const bool autoSaveFlag) { + QString &finalFileName, const bool autoSaveFlag) +{ logde << "save file..."; QString localFileName = fileName; - if (fileName.isEmpty()) { + if (fileName.isEmpty()) + { localFileName = "new"; } QString folder; - if (mode == Global::Mode::None) { + if (mode == Global::Mode::None) + { folder = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - } else if (mode == Global::Mode::Analysis) { + } + else if (mode == Global::Mode::Analysis) + { folder = Global::AnalysisStateFolder; - } else if (mode == Global::Mode::Experiment) { + } + else if (mode == Global::Mode::Experiment) + { folder = Global::SampleDataFloder; } QString filePath; - if (autoSaveFlag) { + if (autoSaveFlag) + { localFileName = _leftWidget->filePathCheck(fileName, folder); - filePath = folder + "/" + localFileName + ".xlsx"; - } else { + filePath = folder + "/" + localFileName + ".xlsx"; + } + else + { QString xlsxfilePath = folder + "/" + localFileName + ".xlsx"; - filePath = QFileDialog::getSaveFileName(nullptr, "Save experiment file", - xlsxfilePath, "Excel Files (*.xlsx)"); + filePath = QFileDialog::getSaveFileName(nullptr, "Save experiment file", + xlsxfilePath, "Excel Files (*.xlsx)"); } // finalFileName = localFileName + ".xlsx"; logde << "filePath:" << filePath.toStdString(); - if (filePath.isEmpty()) { + if (filePath.isEmpty()) + { qDebug() << "User cancel the operation."; return false; } // Save file. - if (mode == Global::Mode::Analysis) { - if (Global::_curveFileDataVtr.empty()) { + if (mode == Global::Mode::Analysis) + { + if (Global::_curveFileDataVtr.empty()) + { // 分析模式下,但是文件数据为空,说明做完实验没有保存数据。 XlsxHandler::writeExperimentFile(filePath); - } else { + } + else + { // 分析模式下,存在文件数据,将文件数据写入文件。 XlsxHandler::writeXlsxFile(filePath); #if 0 @@ -316,57 +352,72 @@ bool MainWindow::saveFile(const QString fileName, const Global::Mode mode, } #endif } - } else if (mode == Global::Mode::Experiment) { + } + else if (mode == Global::Mode::Experiment) + { XlsxHandler::writeExperimentFile(filePath); } return true; } -void MainWindow::smoothness(const int level) { +void MainWindow::smoothness(const int level) +{ logde << "smoothness..."; slotUpdateStatusbarMsg("数据平滑计算中..."); - if (!Global::_curveExperimentDataVtr.empty()) { + if (!Global::_curveExperimentDataVtr.empty()) + { // 当前数据为实验数据时,需要把所有的当前实验数据都进行平滑处理。 // 删除所有objectName为experiment的curve. _centralWidget->deleteCurveByObjectName(Global::ObjectNameExperiemnt); // 添加所有平滑后的curve. - for (auto &item : Global::_curveExperimentDataVtr) { + for (auto &item : Global::_curveExperimentDataVtr) + { item.smoothDataVtr = smoothnessDetail(level, item.dataVtr); - item.curve = _centralWidget->addCurveData(item.smoothDataVtr, - Global::ObjectNameExperiemnt); + item.curve = _centralWidget->addCurveData(item.smoothDataVtr, + Global::ObjectNameExperiemnt); } - } else { + } + else + { // 当前数据为文件分析数据时,需要把当前文件下的所有数据都进行平滑处理。 QString selectedCurveObjectName = _centralWidget->getCurrentCurve()->objectName(); _centralWidget->deleteCurveByObjectName(selectedCurveObjectName); - for (Global::CurveFileData &cfd : Global::_curveFileDataVtr) { - if (selectedCurveObjectName.contains(cfd.filePath)) { - for (Global::PhaseTotalInfo &pti : cfd.phaseTotalVtr) { + for (Global::CurveFileData &cfd : Global::_curveFileDataVtr) + { + if (selectedCurveObjectName.contains(cfd.filePath)) + { + for (Global::PhaseTotalInfo &pti : cfd.phaseTotalVtr) + { pti.smoothDataVtr = smoothnessDetail(level, pti.dataVtr); - pti.curve = _centralWidget->addCurveData(pti.smoothDataVtr, - cfd.filePath); + pti.curve = _centralWidget->addCurveData(pti.smoothDataVtr, + cfd.filePath); } } } } } -QVector MainWindow::smoothnessDetail(const int level, const QVector &dataVtr) { +QVector MainWindow::smoothnessDetail(const int level, const QVector &dataVtr) +{ Lowess::Config config; - config.smoothingFactor = level * 0.01; + config.smoothingFactor = level * 0.01; config.robustnessIterations = 3; std::vector x; std::vector y; - for (const Global::ExperimentData &ed : dataVtr) { - if (Global::_axisMode == Global::AxisMode::SingleY) { + for (const Global::ExperimentData &ed : dataVtr) + { + if (Global::_axisMode == Global::AxisMode::SingleY) + { x.push_back(ed.sampleTemp); - } else { + } + else + { x.push_back(ed.runTime); } @@ -385,7 +436,8 @@ QVector MainWindow::smoothnessDetail(const int level, co ui->statusbar->showMessage("数据平滑计算中..."); std::vector yest = Lowess::smooth(x, y, config); - if (yest.empty()) { + if (yest.empty()) + { slotUpdateStatusbarMsg("数据平滑完成."); return resultVtr; } @@ -396,17 +448,21 @@ QVector MainWindow::smoothnessDetail(const int level, co // result data vector. - for (int i = 0; i < x.size(); i++) { + for (int i = 0; i < x.size(); i++) + { Global::ExperimentData ed; - if (Global::_axisMode == Global::AxisMode::SingleY) { + if (Global::_axisMode == Global::AxisMode::SingleY) + { ed.sampleTemp = x.at(i); - ed.runTime = dataVtr.at(i).runTime; - } else { - ed.runTime = x.at(i); + ed.runTime = dataVtr.at(i).runTime; + } + else + { + ed.runTime = x.at(i); ed.sampleTemp = dataVtr.at(i).sampleTemp; } - ed.dsc = yest.at(i); + ed.dsc = yest.at(i); ed.constantTempTime = dataVtr.at(i).constantTempTime; resultVtr.push_back(ed); @@ -415,7 +471,8 @@ QVector MainWindow::smoothnessDetail(const int level, co return resultVtr; } -void MainWindow::smoothnessExperimentData(const int level) { +void MainWindow::smoothnessExperimentData(const int level) +{ #if 0 for(int i = 0;i < Global::_curveExperimentDataVtr.size();i++){ Global::CurveExperimentData ced = Global::_curveExperimentDataVtr.at(i); @@ -423,19 +480,21 @@ void MainWindow::smoothnessExperimentData(const int level) { } #endif - for (auto &item : Global::_curveExperimentDataVtr) { + for (auto &item : Global::_curveExperimentDataVtr) + { smoothnessDetail(level, item.dataVtr); } } -void MainWindow::startExperiment() { +void MainWindow::startExperiment() +{ QByteArray ba = DataParser::setDeviceStartStop(DeviceStartMode::Start); QString hexData = ba.toHex(' '); SerialPort::instance()->slotSendData(ba); - Global::_mode = Global::Mode::Experiment; + Global::_mode = Global::Mode::Experiment; _manuallyStopTheExperimentFlag = false; _centralWidget->startExperiment(); @@ -445,8 +504,9 @@ void MainWindow::startExperiment() { ui->menu_4->menuAction()->setEnabled(false); } -void MainWindow::startExperimentByDeviceInfo() { - Global::_mode = Global::Mode::Experiment; +void MainWindow::startExperimentByDeviceInfo() +{ + Global::_mode = Global::Mode::Experiment; _manuallyStopTheExperimentFlag = false; _centralWidget->startExperiment(); @@ -454,15 +514,18 @@ void MainWindow::startExperimentByDeviceInfo() { Global::clearExperimentData(); } // 停止实验 -void MainWindow::on_actionStop_triggered() { +void MainWindow::on_actionStop_triggered() +{ logde << " Stop experiment ...++++++++++++++++++"; - if (!SerialPort::instance()->isOpen()) { + if (!SerialPort::instance()->isOpen()) + { showMesgBox("设备未连接,请先连接设备。"); return; } - if (Global::_mode == Global::Mode::Experiment) { + if (Global::_mode == Global::Mode::Experiment) + { QByteArray ba = DataParser::setDeviceStartStop(DeviceStartMode::Stop); SerialPort::instance()->slotSendData(ba); @@ -477,7 +540,8 @@ void MainWindow::on_actionStop_triggered() { logde << "on_actionStop_triggered saveFile ..."; QString finalFileName; - if (saveFile(Global::_experimentInfo.sampleName, Global::Mode::Experiment, finalFileName, true)) { + if (saveFile(Global::_experimentInfo.sampleName, Global::Mode::Experiment, finalFileName, true)) + { _leftWidget->reloadFileName(); QString str = QString("%1 文件保存成功。").arg(finalFileName); @@ -492,19 +556,23 @@ void MainWindow::on_actionStop_triggered() { ui->menu_4->menuAction()->setEnabled(true); } -void MainWindow::on_actionNew_triggered() { - if (!SerialPort::instance()->isOpen()) { +void MainWindow::on_actionNew_triggered() +{ + if (!SerialPort::instance()->isOpen()) + { showMesgBox("设备未连接,请先连接设备。"); return; } _expertmentSettingForm->show(); } -void MainWindow::on_actionStart_triggered() { +void MainWindow::on_actionStart_triggered() +{ logde << "start experiment,set soft into experiment mode."; logde << "```````````````````````````````"; - if (!SerialPort::instance()->isOpen()) { + if (!SerialPort::instance()->isOpen()) + { showMesgBox("设备未连接,请先连接设备。"); return; } @@ -514,19 +582,23 @@ void MainWindow::on_actionStart_triggered() { startExperiment(); } -void MainWindow::on_actionReadOnly_triggered() { +void MainWindow::on_actionReadOnly_triggered() +{ Global::_mode = Global::Mode::Experiment; SerialPort::instance()->openSp(); } -void MainWindow::on_actionRealTimeWidget_triggered() { +void MainWindow::on_actionRealTimeWidget_triggered() +{ _realTimeDataForm->show(); } -void MainWindow::slotSaveExperimentalDataMsgBox() { +void MainWindow::slotSaveExperimentalDataMsgBox() +{ logde << "_manuallyStopTheExperimentFlag:" << _manuallyStopTheExperimentFlag; - if (_manuallyStopTheExperimentFlag) { + if (_manuallyStopTheExperimentFlag) + { logde << "_manuallyStopTheExperimentFlag..."; return; } @@ -537,7 +609,8 @@ void MainWindow::slotSaveExperimentalDataMsgBox() { if (saveFile(Global::_experimentInfo.sampleName, Global::Mode::Experiment, finalFileName, - true)) { + true)) + { _leftWidget->reloadFileName(); QString str = QString("%1 文件保存成功。").arg(finalFileName); @@ -564,10 +637,12 @@ void MainWindow::slotSaveExperimentalDataMsgBox() { #endif } -void MainWindow::on_actionConnectToDev_triggered() { +void MainWindow::on_actionConnectToDev_triggered() +{ logde << "connect to device..."; - if (SerialPort::instance()->isOpen()) { + if (SerialPort::instance()->isOpen()) + { logde << "close device."; SerialPort::instance()->closeSp(); @@ -580,10 +655,13 @@ void MainWindow::on_actionConnectToDev_triggered() { QString str("设备已断开。"); slotUpdateStatusbarMsg(str); showMesgBox(str); - } else { + } + else + { logde << "open device."; - if (SerialPort::instance()->openSp()) { + if (SerialPort::instance()->openSp()) + { setActionEnable(true); // Global::instance()->setMode(Global::Mode::ConnectedToDev); Global::_mode = Global::Mode::ConnectedToDev; @@ -600,38 +678,46 @@ void MainWindow::on_actionConnectToDev_triggered() { showMesgBox(str); logde << "open serial port success. "; - } else { + } + else + { // QMessageBox::warning(this, "warnning", "Serial Port open failed."); showMesgBox("设备打开失败。"); } } } -void MainWindow::on_actionStartPoint_triggered() { +void MainWindow::on_actionStartPoint_triggered() +{ logde << "start experiment..."; _rightWidget->show(); _centralWidget->setAnalysisMode(CentralWidget::AnalysisMode::StartPoint); } -void MainWindow::on_actionStopPoint_triggered() { +void MainWindow::on_actionStopPoint_triggered() +{ _rightWidget->show(); _centralWidget->setAnalysisMode(CentralWidget::AnalysisMode::StopPoint); } -void MainWindow::on_actionNumericalLabel_triggered() { +void MainWindow::on_actionNumericalLabel_triggered() +{ _rightWidget->show(); _centralWidget->setAnalysisMode(CentralWidget::AnalysisMode::NumericalLabel); } -void MainWindow::on_actionPeakSynthesisAnalysis_triggered() { +void MainWindow::on_actionPeakSynthesisAnalysis_triggered() +{ _rightWidget->show(); _centralWidget->setAnalysisMode(CentralWidget::AnalysisMode::PeakSynthesisAnalysis); } -void MainWindow::on_actionClearAllData_triggered() { +void MainWindow::on_actionClearAllData_triggered() +{ // 实验过程中,不允许清除数据。 - if (Global::_mode == Global::Mode::Experiment) { + if (Global::_mode == Global::Mode::Experiment) + { return; } @@ -641,62 +727,75 @@ void MainWindow::on_actionClearAllData_triggered() { _centralWidget->clearAllData(); } -void MainWindow::on_actionGlassTransition_triggered() { +void MainWindow::on_actionGlassTransition_triggered() +{ _rightWidget->show(); _centralWidget->setAnalysisMode(CentralWidget::AnalysisMode::GlassTransition); } -void MainWindow::on_actionOIT_triggered() { +void MainWindow::on_actionOIT_triggered() +{ _rightWidget->show(); _centralWidget->setAnalysisMode(CentralWidget::AnalysisMode::OIT); } -void MainWindow::on_actionSpecificHeatCompMethod_triggered() { +void MainWindow::on_actionSpecificHeatCompMethod_triggered() +{ _specificHeatComparisonMethodForm->show(); } -void MainWindow::on_actionDegreeOfCrystallinity_triggered() { +void MainWindow::on_actionDegreeOfCrystallinity_triggered() +{ // QMessageBox::warning(this, "warnning", "结晶度."); _degreeOfCrystallinityForm->show(); } -void MainWindow::on_actionInstrumentParameter_triggered() { +void MainWindow::on_actionInstrumentParameter_triggered() +{ _instrumentCoefficientForm->show(); } - -void MainWindow::on_actionOITAutoAnalysisParam_triggered() { +void MainWindow::on_actionOITAutoAnalysisParam_triggered() +{ _OITAutoAnalysisParamForm->show(); } -void MainWindow::on_actionOITAutoAnalysisMode_triggered() { +void MainWindow::on_actionOITAutoAnalysisMode_triggered() +{ } -void MainWindow::on_actionTimeAxisAnalysisPCTMode_triggered() { +void MainWindow::on_actionTimeAxisAnalysisPCTMode_triggered() +{ Global::_displayTimeValue = ui->actionTimeAxisAnalysisPCTMode->isChecked(); } -void MainWindow::on_actionDegreeOfCuring_triggered() { +void MainWindow::on_actionDegreeOfCuring_triggered() +{ // _degreeOfCureForm->show(); } -void MainWindow::on_actionAbout_triggered() { +void MainWindow::on_actionAbout_triggered() +{ _aboutForm->show(); } -void MainWindow::on_actionEnthalpyCorrectionEdit_triggered() { +void MainWindow::on_actionEnthalpyCorrectionEdit_triggered() +{ _enthalpyDataCorrectionForm->show(); } -void MainWindow::on_actionEnthalpyCorrectionSelection_triggered() { +void MainWindow::on_actionEnthalpyCorrectionSelection_triggered() +{ _coefficientSelectionForm->show(); } -void MainWindow::on_actionPrintPreview_triggered() { +void MainWindow::on_actionPrintPreview_triggered() +{ logde << "print preview..."; - if (Global::_curveFileDataVtr.empty()) { + if (Global::_curveFileDataVtr.empty()) + { showMesgBox("请先打开数据。"); return; } @@ -706,103 +805,132 @@ void MainWindow::on_actionPrintPreview_triggered() { // _printPreviewForm->setOrientation(); _printPreviewForm->_customPrintPreviewDialog->exec(); - _printPreviewForm->_customPrintPreviewDialog->update(); // 可选,强制刷新 + _printPreviewForm->_customPrintPreviewDialog->update(); // 可选,强制刷新 } -void MainWindow::on_actionOnsetTemperaturePoint_triggered() { +void MainWindow::on_actionOnsetTemperaturePoint_triggered() +{ _rightWidget->show(); _centralWidget->setAnalysisMode(CentralWidget::AnalysisMode::OnsetTemperaturePoint); } -void MainWindow::on_actionEndsetTemperaturePoint_triggered() { +void MainWindow::on_actionEndsetTemperaturePoint_triggered() +{ _rightWidget->show(); _centralWidget->setAnalysisMode(CentralWidget::AnalysisMode::EndsetTemperaturePoint); } -void MainWindow::on_actionYAxis_triggered() { +void MainWindow::on_actionYAxis_triggered() +{ _centralWidget->switchAxisMode(); } -void MainWindow::on_actionAxisSetting_triggered() { +void MainWindow::on_actionAxisSetting_triggered() +{ _axisSettingForm->show(); } -void MainWindow::on_actionSaveData_triggered() { +void MainWindow::on_actionSaveData_triggered() +{ QString finaleFileName; saveFile(Global::_experimentInfo.sampleName, Global::_mode, finaleFileName); _leftWidget->reloadFileName(); } -void MainWindow::on_actionSaveas_triggered() { +void MainWindow::on_actionSaveas_triggered() +{ QString finaleFileName; saveFile(Global::_experimentInfo.sampleName, Global::Mode::None, finaleFileName); _leftWidget->reloadFileName(); } -void MainWindow::on_actionLanguage_triggered() { - if (Global::_languageType == Global::LanguageType::Chinese) { +void MainWindow::on_actionLanguage_triggered() +{ + if (Global::_languageType == Global::LanguageType::Chinese) + { Global::_languageType = Global::LanguageType::English; ui->actionLanguage->setText(Global::EnglishStr); - } else { + } + else + { Global::_languageType = Global::LanguageType::Chinese; ui->actionLanguage->setText(Global::ChineseStr); } } -void MainWindow::on_actionSmoothness1_triggered() { +void MainWindow::on_actionSmoothness1_triggered() +{ smoothness(1); } -void MainWindow::on_actionSmoothness2_triggered() { +void MainWindow::on_actionSmoothness2_triggered() +{ smoothness(2); } -void MainWindow::on_actionSmoothness3_triggered() { +void MainWindow::on_actionSmoothness3_triggered() +{ smoothness(3); } -void MainWindow::on_actionSmoothness4_triggered() { +void MainWindow::on_actionSmoothness4_triggered() +{ smoothness(4); } -void MainWindow::on_actionSmoothness5_triggered() { +void MainWindow::on_actionSmoothness5_triggered() +{ smoothness(5); } -void MainWindow::on_actionSmoothness6_triggered() { +void MainWindow::on_actionSmoothness6_triggered() +{ smoothness(6); } -void MainWindow::on_actionSmoothness7_triggered() { +void MainWindow::on_actionSmoothness7_triggered() +{ smoothness(7); } -void MainWindow::on_actionSmoothness8_triggered() { +void MainWindow::on_actionSmoothness8_triggered() +{ smoothness(8); } -void MainWindow::on_actionSmoothness9_triggered() { +void MainWindow::on_actionSmoothness9_triggered() +{ smoothness(9); } -void MainWindow::on_actionSmoothness10_triggered() { +void MainWindow::on_actionSmoothness10_triggered() +{ smoothness(10); } // 原始数据 -void MainWindow::on_actionOriginalData_triggered() { - if (!Global::_curveExperimentDataVtr.empty()) { +void MainWindow::on_actionOriginalData_triggered() +{ + if (!Global::_curveExperimentDataVtr.empty()) + { // 删除所有objectName为experiment的curve. _centralWidget->deleteCurveByObjectName(Global::ObjectNameExperiemnt); - for (auto &item : Global::_curveExperimentDataVtr) { + for (auto &item : Global::_curveExperimentDataVtr) + { item.smoothDataVtr.clear(); item.curve = _centralWidget->addCurveData(item.dataVtr, Global::ObjectNameExperiemnt); } - } else { + } + else + { // 当前数据为文件分析数据时,需要把当前文件下的所有数据都进行平滑处理。 - if(_centralWidget->getCurrentCurve() == nullptr){ + if (_centralWidget->getCurrentCurve() == nullptr) + { return; } QString selectedCurveObjectName = _centralWidget->getCurrentCurve()->objectName(); _centralWidget->deleteCurveByObjectName(selectedCurveObjectName); - for (Global::CurveFileData &cfd : Global::_curveFileDataVtr) { - if (selectedCurveObjectName.contains(cfd.filePath)) { - for (Global::PhaseTotalInfo &pti : cfd.phaseTotalVtr) { + for (Global::CurveFileData &cfd : Global::_curveFileDataVtr) + { + if (selectedCurveObjectName.contains(cfd.filePath)) + { + for (Global::PhaseTotalInfo &pti : cfd.phaseTotalVtr) + { pti.smoothDataVtr.clear(); pti.curve = _centralWidget->addCurveData(pti.dataVtr, cfd.filePath); @@ -847,19 +975,23 @@ void MainWindow::on_actionOriginalData_triggered() { #endif } -void MainWindow::showMesgBox(const QString str) { +void MainWindow::showMesgBox(const QString str) +{ static bool isShow = false; - if (!isShow) { + if (!isShow) + { isShow = true; QMessageBox::information(this, "提示", str); isShow = false; } } -void MainWindow::slotDeviceDisconnected() { +void MainWindow::slotDeviceDisconnected() +{ // logde<<"slotDeviceDisconnected...1"; // 如果当前是实验模式时,需要先停止实验。 - if (SerialPort::instance()->isOpen()) { + if (SerialPort::instance()->isOpen()) + { // logde<<"slotDeviceDisconnected...2"; // ui更新,断开连接 ui->actionConnectToDev->setIcon(QIcon(":/images/connect.png")); @@ -870,3 +1002,17 @@ void MainWindow::slotDeviceDisconnected() { SerialPort::instance()->closeSp(); } } + +void MainWindow::slotDeleteActionTriggered(const QString &filePath) +{ + logde<<"slotDeleteActionTriggered...1:"<confirmDelete(ret == QMessageBox::Ok ? true : false); +} + diff --git a/src/mainwindow.h b/src/mainwindow.h index 57ec3ad..274a02d 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -45,6 +45,9 @@ public slots: void slotStartExperiment(); void slotDeviceDisconnected(); + + // 左侧文件管理子窗口操作函数 + void slotDeleteActionTriggered(const QString&); protected: void closeEvent(QCloseEvent *event) override; private slots: @@ -52,6 +55,7 @@ private slots: void on_actionConnectToDev_triggered(); void on_actionNew_triggered(); void on_actionStart_triggered(); + // 停止实验 void on_actionStop_triggered(); void on_actionReadOnly_triggered(); diff --git a/src/ui/leftwidget.cpp b/src/ui/leftwidget.cpp index 863f877..787d248 100644 --- a/src/ui/leftwidget.cpp +++ b/src/ui/leftwidget.cpp @@ -1,63 +1,59 @@ -#include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "leftwidget.h" #include "filemanager.h" #include "global.h" +#include "logger.h" -LeftWidget::LeftWidget(QWidget *parent) : QDockWidget(parent) { +LeftWidget::LeftWidget(QWidget *parent) : QDockWidget(parent) +{ setWindowTitle("文件浏览"); _treeWidget = new QTreeWidget(); _treeWidget->setHeaderHidden(true); - - _sampleDataItem = new QTreeWidgetItem(_treeWidget); - _sampleDataItem->setText(0, "样品数据"); -#if 0 - _baseLineItem = new QTreeWidgetItem(_treeWidget); - _baseLineItem->setText(0,"基线"); -#endif - _analysisStateItem = new QTreeWidgetItem(_treeWidget); - _analysisStateItem->setText(0, "分析状态"); - _treeWidget->setSortingEnabled(false); - _treeWidget->insertTopLevelItem(0, _sampleDataItem); - // _treeWidget->insertTopLevelItem(1, _baseLineItem); - _treeWidget->insertTopLevelItem(2, _analysisStateItem); - setWidget(_treeWidget); - // init file name. - initFileName(_sampleDataItem, Global::SampleDataFloder); -#if 0 - initFileName(_baseLineItem,Global::BaseLineFolder); -#endif - initFileName(_analysisStateItem, Global::AnalysisStateFolder); - - expandAll(_sampleDataItem); - // expandAll(_baseLineItem); - expandAll(_analysisStateItem); - - Global::updateFileList(); - + // 初始化文件树 + reloadFileTree(); // connections connect(_treeWidget, &QTreeWidget::itemDoubleClicked, this, &LeftWidget::slotTreeWidgetItemClicked); // 右键菜单 - _contextMenu = new QMenu(_treeWidget); - _deleteAction = new QAction("删除", this); - _contextMenu->addAction(_deleteAction); + _contextMenu = new QMenu(_treeWidget); + _deleteFileAction = new QAction("删除文件", this); + _createFolderAction = new QAction("新建文件夹", this); + _contextMenu->addAction(_deleteFileAction); + _contextMenu->addAction(_createFolderAction); _treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(_treeWidget, &QTreeWidget::customContextMenuRequested, this, &LeftWidget::slotShowContextMenu); + + connect(_deleteFileAction, &QAction::triggered, + this, &LeftWidget::slotDeleteActionTriggered); + connect(_createFolderAction, &QAction::triggered, + this, &LeftWidget::slotCreateFolderActionTriggered); } -void LeftWidget::reloadFileName() { +void LeftWidget::reloadFileName() +{ clearAllChildItems(_sampleDataItem); // clearAllChildItems(_baseLineItem); clearAllChildItems(_analysisStateItem); @@ -66,21 +62,25 @@ void LeftWidget::reloadFileName() { #if 0 initFileName(_baseLineItem,Global::BaseLineFolder); #endif + initFileName(_analysisStateItem, Global::AnalysisStateFolder); // 更新文件列表 Global::updateFileList(); } -QString LeftWidget::filePathCheck(const QString fileName, const QString folderPath) { +QString LeftWidget::filePathCheck(const QString fileName, const QString folderPath) +{ QString resultFileName = fileName; - QDir dir(folderPath); + QDir dir(folderPath); QStringList files = dir.entryList(QDir::Files); - for (const QString &existedFileName : files) { + for (const QString &existedFileName : files) + { QFileInfo fileInfo(existedFileName); - if (fileName == fileInfo.baseName()) { + if (fileName == fileInfo.baseName()) + { QDateTime currentDateTime = QDateTime::currentDateTime(); - QString formattedTime = currentDateTime.toString("yyyy_MM_dd_HH_mm_ss"); + QString formattedTime = currentDateTime.toString("yyyy_MM_dd_HH_mm_ss"); resultFileName = fileName + QString("_") + formattedTime; break; @@ -90,70 +90,89 @@ QString LeftWidget::filePathCheck(const QString fileName, const QString folderPa return resultFileName; } -void LeftWidget::initData() { +void LeftWidget::initData() +{ // const QString folderPath = QDir::currentPath()+"/../experiment_data"; const QString folderPath = Global::SampleDataFloder; #if 1 QDir dir(folderPath); - if (!dir.exists()) { + if (!dir.exists()) + { qWarning() << "文件夹不存在: " << folderPath; return; } // 遍历文件 QFileInfoList fileList = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot); - for (const QFileInfo &fileInfo : fileList) { + for (const QFileInfo &fileInfo : fileList) + { QFile file(fileInfo.absoluteFilePath()); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + { QString content = file.readAll(); qDebug() << "读取文件 " << fileInfo.absoluteFilePath() << " 内容: " << content; file.close(); // 写入文件操作示例:这里简单在文件末尾追加一行内容 - if (file.open(QIODevice::Append | QIODevice::Text)) { + if (file.open(QIODevice::Append | QIODevice::Text)) + { file.write("\n这是通过递归写入添加的内容"); qDebug() << "已向文件 " << fileInfo.absoluteFilePath() << " 写入内容"; file.close(); - } else { + } + else + { qWarning() << "无法打开文件进行写入: " << fileInfo.absoluteFilePath(); } - } else { + } + else + { qWarning() << "无法打开文件进行读取: " << fileInfo.absoluteFilePath(); } } #endif } -void LeftWidget::slotTreeWidgetItemClicked(QTreeWidgetItem *item, int column) { +void LeftWidget::slotTreeWidgetItemClicked(QTreeWidgetItem *item, int column) +{ qDebug() << "item clicked:" << item->text(0) << column; - if (Global::Mode::Analysis != Global::_mode) { + if (Global::Mode::Analysis != Global::_mode) + { return; } - QString fileName; - // 获取父节点 - QTreeWidgetItem *parentItem = item->parent(); - if (parentItem) { - qDebug() << "parent item text:" << parentItem->text(0); - if (parentItem == _sampleDataItem) { - fileName = Global::SampleDataFloder + "/" + item->text(0); -#if 0 - }else if(parentItem == _baseLineItem){ - fileName =Global::BaseLineFolder + "/" +item->text(0); -#endif - } else if (parentItem == _analysisStateItem) { - fileName = Global::AnalysisStateFolder + "/" + item->text(0); - } - } else { - qDebug() << "item has no parent (it is a top-level item)"; + // 检查是否是文件项(有用户数据) + QVariant userData = item->data(0, Qt::UserRole); + if (userData.isNull() || !userData.isValid()) + { + qDebug() << "点击的是目录项或无效项"; return; } - emit sigSendAnalysisFileName(fileName); + QString filePath = userData.toString(); + if (filePath.isEmpty()) + { + qDebug() << "文件路径为空"; + return; + } + + // 检查文件是否存在 + QFileInfo fileInfo(filePath); + if (!fileInfo.exists()) + { + qWarning() << "文件不存在: " << filePath; + return; + } + + // 发送文件路径信号 + emit sigSendAnalysisFileName(filePath); } -void LeftWidget::initFileName(QTreeWidgetItem *parentItem, const QString &folderPath) { - QDir dir(folderPath); + +void LeftWidget::initFileName(QTreeWidgetItem *parentItem, const QString &folderPath) +{ + QDir dir(folderPath); QStringList files = dir.entryList(QDir::Files); - for (const QString &fileName : files) { + for (const QString &fileName : files) + { QTreeWidgetItem *subItem = new QTreeWidgetItem(); subItem->setText(0, fileName); subItem->setData(0, Qt::UserRole, QVariant::fromValue(folderPath + "/" + fileName)); @@ -161,22 +180,25 @@ void LeftWidget::initFileName(QTreeWidgetItem *parentItem, const QString &folder } } -void LeftWidget::expandAll(QTreeWidgetItem *item) { +void LeftWidget::expandAll(QTreeWidgetItem *item) +{ item->setExpanded(true); - for (int i = 0; i < item->childCount(); ++i) { + for (int i = 0; i < item->childCount(); ++i) + { expandAll(item->child(i)); } } -void LeftWidget::clearAllChildItems(QTreeWidgetItem *parentItem) { +void LeftWidget::clearAllChildItems(QTreeWidgetItem *parentItem) +{ int childCount = parentItem->childCount(); - for (int i = 0; i < childCount; ++i) { + for (int i = 0; i < childCount; ++i) + { QTreeWidgetItem *childItem = parentItem->takeChild(0); delete childItem; } } - #if 0 void LeftWidget::recursiveFolderOperation(const QString& folderPath) { QDir dir(folderPath); @@ -215,26 +237,334 @@ void LeftWidget::recursiveFolderOperation(const QString& folderPath) { } #endif -void LeftWidget::slotShowContextMenu(const QPoint &pos) { - QPoint globalPos = _treeWidget->mapToGlobal(pos); - _contextMenu->exec(globalPos); - - // 获取当前选中的项 - QTreeWidgetItem *currentItem = _treeWidget->itemAt(pos); - if (currentItem == nullptr) { - return; +void LeftWidget::slotShowContextMenu(const QPoint &pos) +{ + QTreeWidgetItem *item = _treeWidget->itemAt(pos); + if (item) { + // 获取文件路径 + QString filePath = item->data(0, Qt::UserRole).toString(); + QString dirPath = item->data(0, Qt::UserRole + 1).toString(); + + // 如果是文件 + if (!filePath.isEmpty()) { + _deleteFileActionFilePath = filePath; + _deleteFileAction->setVisible(true); + _deleteFileAction->setText(tr("删除文件")); + } + // 如果是目录 + else if (!dirPath.isEmpty()) { + // 暂时不要删除文件件功能 + _deleteFileAction->setVisible(false); + #if 0 + _deleteFileAction->setVisible(true); + _deleteFileActionFilePath = dirPath; + _deleteFileAction->setText(tr("删除文件夹")); + #endif + } else { + _deleteFileAction->setVisible(false); + } + + // 只有当点击的是目录时才显示"新建文件夹"选项 + bool isDir = !dirPath.isEmpty(); + _createFolderAction->setVisible(isDir); + + _contextMenu->exec(_treeWidget->mapToGlobal(pos)); } - - QString filePath = currentItem->data(0, Qt::UserRole).toString(); - if (filePath.isEmpty()) { - return; - } - // qDebug() << "file:" << filePath; - if (QFile::remove(filePath)) { - qDebug() << "文件删除成功:" << filePath; - } else { - qWarning() << "文件删除失败:" << filePath; - } - - reloadFileName(); +} + +void LeftWidget::slotDeleteActionTriggered() +{ + if (_deleteFileActionFilePath.isEmpty()) { + return; + } + + QFileInfo fileInfo(_deleteFileActionFilePath); + bool isDir = fileInfo.isDir(); + + // 检查文件/目录是否存在 + if (!fileInfo.exists()) { + QMessageBox::warning(this, tr("警告"), tr("文件/目录不存在!")); + return; + } + + // 确认删除对话框 + int ret = QMessageBox::question(this, tr("确认删除"), + tr("确定要删除%1\n%2?").arg(isDir ? tr("文件夹及其所有内容") : tr("文件")).arg(_deleteFileActionFilePath), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (ret == QMessageBox::Yes) { + bool success; + if (isDir) { + // 删除目录及其内容 + success = removeDir(_deleteFileActionFilePath); + } else { + // 删除文件 + success = QFile::remove(_deleteFileActionFilePath); + } + + if (success) { + // 重新加载文件树 + reloadFileTree(); + } else { + QMessageBox::critical(this, tr("错误"), tr("删除失败!")); + } + } + + _deleteFileActionFilePath.clear(); +} + +bool LeftWidget::removeDir(const QString &dirPath) +{ + bool result = true; + QDir dir(dirPath); + + if (dir.exists(dirPath)) { + Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) { + if (info.isDir()) { + result = removeDir(info.absoluteFilePath()); + } else { + result = QFile::remove(info.absoluteFilePath()); + } + + if (!result) { + return result; + } + } + result = dir.rmdir(dirPath); + } + + return result; +} + +void LeftWidget::slotCreateFolderActionTriggered() +{ + // 获取当前选中的项目 + QTreeWidgetItem *currentItem = _treeWidget->currentItem(); + if (!currentItem) { + return; + } + + // 获取当前目录路径 + QString dirPath; + if (currentItem->data(0, Qt::UserRole + 1).toString().isEmpty()) { + // 如果是文件,获取其父目录 + dirPath = QFileInfo(currentItem->data(0, Qt::UserRole).toString()).absolutePath(); + } else { + // 如果是目录,直接使用其路径 + dirPath = currentItem->data(0, Qt::UserRole + 1).toString(); + } + + // 弹出对话框,输入新文件夹名称 + bool ok; + QString folderName = QInputDialog::getText(this, tr("新建文件夹"), + tr("请输入文件夹名称:"), + QLineEdit::Normal, + tr("新建文件夹"), &ok); + + if (!ok || folderName.isEmpty()) { + return; + } + + // 检查文件夹名称是否有效 + if (folderName.contains(QRegExp("[\\/:*?\"<>|]"))) { + QMessageBox::warning(this, tr("错误"), tr("文件夹名称包含非法字符!")); + return; + } + + // 创建新文件夹路径 + QString newFolderPath = dirPath + "/" + folderName; + + // 检查文件夹是否已存在 + if (QDir(newFolderPath).exists()) { + QMessageBox::warning(this, tr("错误"), tr("文件夹已存在!")); + return; + } + + // 创建文件夹 + if (QDir().mkdir(newFolderPath)) { + // 保存当前展开状态 + saveExpandedState(); + + // 重新加载文件树 + reloadFileTree(); + + // 恢复展开状态 + restoreExpandedState(); + + // 展开当前目录并选中新创建的文件夹 + expandAndSelectItem(newFolderPath); + } else { + QMessageBox::critical(this, tr("错误"), tr("创建文件夹失败!")); + } +} + +void LeftWidget::expandAndSelectItem(const QString &path) +{ + // 递归展开到指定路径并选中该项 + QTreeWidgetItemIterator it(_treeWidget); + while (*it) { + QTreeWidgetItem *item = *it; + QString itemPath = item->data(0, Qt::UserRole + 1).toString(); + + if (itemPath == path) { + // 展开所有父节点 + QTreeWidgetItem *parent = item->parent(); + while (parent) { + parent->setExpanded(true); + parent = parent->parent(); + } + + // 选中新创建的文件夹 + _treeWidget->setCurrentItem(item); + break; + } + ++it; + } +} + +void LeftWidget::confirmDelete(bool enabled) +{ + if (enabled) + { + if (QFile::remove(_deleteFileActionFilePath)) + { + // qDebug() << "文件删除成功:" << filePath; + logde << "delete succ:" << _deleteFileActionFilePath.toStdString(); + } + else + { + // qWarning() << "文件删除失败:" << filePath; + logde << "delete fail:" << _deleteFileActionFilePath.toStdString(); + } + // 重新加载文件树 + reloadFileTree(); + } +} + +void LeftWidget::reloadFileTree() +{ + // 保存当前展开状态 + saveExpandedState(); + + // 清空现有的树形结构 + _treeWidget->clear(); + + // 创建根节点 + QTreeWidgetItem *rootItem = new QTreeWidgetItem(_treeWidget); + rootItem->setText(0, "实验数据"); + rootItem->setData(0, Qt::UserRole, Global::ExperimentDirPath); + // 保存根目录路径,用于恢复展开状态 + rootItem->setData(0, Qt::UserRole + 1, Global::ExperimentDirPath); + + // 递归扫描目录并构建树形结构 + buildFileTree(rootItem, Global::ExperimentDirPath); + + // 展开根节点 + rootItem->setExpanded(true); + + // 恢复展开状态 + restoreExpandedState(); + + // 更新全局文件列表 + Global::updateFileList(); +} + +QFileInfoList LeftWidget::scanDirRecursively(const QString &rootPath) +{ + QFileInfoList result; // 1. 结果容器 + QDirIterator it(rootPath, // 2. 迭代器:从 rootPath 开始 + QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, // 3. 要哪些条目 + QDirIterator::Subdirectories); // 4. 递归标志 + while (it.hasNext()) + { // 5. 还有下一条吗? + it.next(); // 6. 推进到下一个条目 + result.append(it.fileInfo()); // 7. 把当前条目塞进列表 + } + return result; // 8. 返回 +} + +void LeftWidget::buildFileTree(QTreeWidgetItem *parentItem, const QString &dirPath) +{ + QDir dir(dirPath); + if (!dir.exists()) { + qWarning() << "目录不存在: " << dirPath; + return; + } + + // 获取所有子目录和文件,按名称排序 + dir.setSorting(QDir::Name | QDir::DirsFirst); + QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + + for (const QFileInfo &fileInfo : list) { + QTreeWidgetItem *item = new QTreeWidgetItem(parentItem); + + if (fileInfo.isDir()) { + // 设置目录项 + item->setText(0, fileInfo.fileName()); + // 使用系统默认的目录图标 + item->setIcon(0, _treeWidget->style()->standardIcon(QStyle::SP_DirIcon)); + // 保存目录路径,用于恢复展开状态 + item->setData(0, Qt::UserRole + 1, fileInfo.absoluteFilePath()); + + // 递归处理子目录 + buildFileTree(item, fileInfo.absoluteFilePath()); + } else { + // 设置文件项 + item->setText(0, fileInfo.fileName()); + item->setData(0, Qt::UserRole, fileInfo.absoluteFilePath()); + + // 根据文件类型设置不同的图标 + QString suffix = fileInfo.suffix().toLower(); + if (suffix == "xlsx" || suffix == "xls") { + // 使用系统默认的文档图标 + item->setIcon(0, _treeWidget->style()->standardIcon(QStyle::SP_FileIcon)); + } else { + // 使用默认文件图标 + item->setIcon(0, _treeWidget->style()->standardIcon(QStyle::SP_FileIcon)); + } + } + } +} + +void LeftWidget::saveExpandedState() +{ + _expandedPaths.clear(); + + // 递归遍历所有项,保存展开的目录路径 + QTreeWidgetItemIterator it(_treeWidget); + while (*it) { + QTreeWidgetItem *item = *it; + if (item->isExpanded()) { + QVariant pathData = item->data(0, Qt::UserRole + 1); + if (!pathData.isNull()) { + _expandedPaths.append(pathData.toString()); + } + } + ++it; + } + + qDebug() << "保存了" << _expandedPaths.size() << "个展开状态:" << _expandedPaths; +} + +void LeftWidget::restoreExpandedState() +{ + int restoredCount = 0; + + // 递归遍历所有项,恢复展开状态 + QTreeWidgetItemIterator it(_treeWidget); + while (*it) { + QTreeWidgetItem *item = *it; + QVariant pathData = item->data(0, Qt::UserRole + 1); + if (!pathData.isNull()) { + QString path = pathData.toString(); + if (_expandedPaths.contains(path)) { + item->setExpanded(true); + restoredCount++; + } + } + ++it; + } + + qDebug() << "恢复了" << restoredCount << "个展开状态"; } diff --git a/src/ui/leftwidget.h b/src/ui/leftwidget.h index eb5c3f3..263bf62 100644 --- a/src/ui/leftwidget.h +++ b/src/ui/leftwidget.h @@ -19,18 +19,30 @@ public: void reloadFileName(); + void reloadFileTree(); + QString filePathCheck(const QString fileName,const QString folderPath); + // 确认删除文件 + void confirmDelete(bool enabled); signals: void sigSendAnalysisFileName(const QString&); + void sigDeleteActionTriggered(const QString&); private: void initData(); void initFileName(QTreeWidgetItem*,const QString &folderPath); void expandAll(QTreeWidgetItem* item); void clearAllChildItems(QTreeWidgetItem* parentItem); - void removeFile(const QString& filePath); + QFileInfoList scanDirRecursively(const QString &rootPath); + void buildFileTree(QTreeWidgetItem *parentItem, const QString &dirPath); + void saveExpandedState(); + void restoreExpandedState(); + void expandAndSelectItem(const QString &path); + bool removeDir(const QString &dirPath); private slots: void slotTreeWidgetItemClicked(QTreeWidgetItem *item, int column); void slotShowContextMenu(const QPoint &pos); + void slotDeleteActionTriggered(); + void slotCreateFolderActionTriggered(); private: QTreeWidget *_treeWidget; @@ -39,7 +51,14 @@ private: *_sampleDataItem; QMenu *_contextMenu; - QAction *_deleteAction; + + QAction *_deleteFileAction; + QAction *_createFolderAction; + QString _deleteFileActionFilePath; + + // 保存展开状态的列表 + QStringList _expandedPaths; }; #endif // LEFTWIDGET_H +