#include "nmWxAutomaticfittingStart.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #endif static const double kTargetCrossHalfSize = 1.0; static const double kTargetCircleRadius = 1.0; static bool nmAutoFitChartIsFinite(double value) { #ifdef Q_OS_WIN return _finite(value) != 0; #else return std::isfinite(value); #endif } nmAutomaticFittingCurveChart::nmAutomaticFittingCurveChart(QWidget* parent) : QWidget(parent) , m_logRange(0.0, 0.0, 1.0, 1.0) , m_bestIteration(0) , m_bestFitness(0.0) { setMinimumHeight(220); setMinimumWidth(420); } void nmAutomaticFittingCurveChart::setTargetLogLogData(const QVector >& data) { m_targetPressure = extractCurve(data, 1); m_targetDerivative = extractCurve(data, 2); rebuildRange(); update(); } void nmAutomaticFittingCurveChart::setBestLogLogData(const QVector >& data, int iteration, double fitness) { m_bestPressure = extractCurve(data, 1); m_bestDerivative = extractCurve(data, 2); m_bestIteration = iteration; m_bestFitness = fitness; rebuildRange(); update(); } QVector nmAutomaticFittingCurveChart::extractCurve(const QVector >& data, int yIndex) const { QVector points; if(data.size() <= yIndex || data[0].isEmpty() || data[yIndex].isEmpty()) { return points; } int count = qMin(data[0].size(), data[yIndex].size()); points.reserve(count); for(int i = 0; i < count; ++i) { double x = data[0][i]; double y = data[yIndex][i]; if(nmAutoFitChartIsFinite(x) && nmAutoFitChartIsFinite(y) && x > 0.0 && y > 0.0) { points.append(QPointF(x, y)); } } return points; } void nmAutomaticFittingCurveChart::rebuildRange() { bool hasPoint = false; double minX = 0.0; double maxX = 0.0; double minY = 0.0; double maxY = 0.0; QVector > allCurves; allCurves << m_targetPressure << m_targetDerivative << m_bestPressure << m_bestDerivative; for(int c = 0; c < allCurves.size(); ++c) { const QVector& points = allCurves[c]; for(int i = 0; i < points.size(); ++i) { double lx = qLn(points[i].x()) / qLn(10.0); double ly = qLn(points[i].y()) / qLn(10.0); if(!hasPoint) { minX = maxX = lx; minY = maxY = ly; hasPoint = true; } else { minX = qMin(minX, lx); maxX = qMax(maxX, lx); minY = qMin(minY, ly); maxY = qMax(maxY, ly); } } } if(!hasPoint) { m_logRange = QRectF(0.0, 0.0, 1.0, 1.0); return; } if(qAbs(maxX - minX) < 1e-8) { minX -= 0.5; maxX += 0.5; } if(qAbs(maxY - minY) < 1e-8) { minY -= 0.5; maxY += 0.5; } double padX = (maxX - minX) * 0.06; double padY = (maxY - minY) * 0.10; m_logRange = QRectF(minX - padX, minY - padY, (maxX - minX) + 2.0 * padX, (maxY - minY) + 2.0 * padY); } QPointF nmAutomaticFittingCurveChart::dataToScreen(const QPointF& point) const { double lx = qLn(point.x()) / qLn(10.0); double ly = qLn(point.y()) / qLn(10.0); double xRatio = (lx - m_logRange.left()) / qMax(1e-12, m_logRange.width()); double yRatio = (ly - m_logRange.top()) / qMax(1e-12, m_logRange.height()); return QPointF(m_chartRect.left() + xRatio * m_chartRect.width(), m_chartRect.bottom() - yRatio * m_chartRect.height()); } QString nmAutomaticFittingCurveChart::formatPowerTickLabel(int exponent) const { if(exponent == 0) { return "1"; } if(exponent == 1) { return "10"; } QString expStr = QString::number(exponent); expStr.replace("-", QString(QChar(0x207B))); // ⁻ expStr.replace("0", QString(QChar(0x2070))); // ⁰ expStr.replace("1", QString(QChar(0x00B9))); // ¹ expStr.replace("2", QString(QChar(0x00B2))); // ² expStr.replace("3", QString(QChar(0x00B3))); // ³ expStr.replace("4", QString(QChar(0x2074))); // ⁴ expStr.replace("5", QString(QChar(0x2075))); // ⁵ expStr.replace("6", QString(QChar(0x2076))); // ⁶ expStr.replace("7", QString(QChar(0x2077))); // ⁷ expStr.replace("8", QString(QChar(0x2078))); // ⁸ expStr.replace("9", QString(QChar(0x2079))); // ⁹ return QString("10") + expStr; } void nmAutomaticFittingCurveChart::paintEvent(QPaintEvent* event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.fillRect(rect(), QColor(240, 240, 240)); const int marginLeft = 62; const int marginTop = 18; const int marginRight = 16; const int marginBottom = 42; m_chartRect = QRectF(marginLeft, marginTop, qMax(10, width() - marginLeft - marginRight), qMax(10, height() - marginTop - marginBottom)); painter.fillRect(m_chartRect, QColor(252, 252, 252)); drawGrid(painter); drawAxes(painter); drawCurveLine(painter, m_bestPressure, QColor(250, 74, 74)); drawCurveLine(painter, m_bestDerivative, QColor(71, 252, 71)); drawCrossMarkers(painter, m_targetPressure, QColor(71, 252, 71)); drawCircleMarkers(painter, m_targetDerivative, QColor(250, 74, 74)); } void nmAutomaticFittingCurveChart::drawGrid(QPainter& painter) { painter.save(); int xStart = static_cast(qFloor(m_logRange.left())); int xEnd = static_cast(qCeil(m_logRange.right())); int yStart = static_cast(qFloor(m_logRange.top())); int yEnd = static_cast(qCeil(m_logRange.bottom())); painter.setPen(QPen(QColor(236, 236, 236), 1, Qt::SolidLine)); for(int decade = xStart; decade <= xEnd; ++decade) { for(int multiplier = 2; multiplier <= 9; ++multiplier) { double value = decade + qLn(static_cast(multiplier)) / qLn(10.0); if(value <= m_logRange.left() || value >= m_logRange.right()) { continue; } double ratio = (value - m_logRange.left()) / qMax(1e-12, m_logRange.width()); double sx = m_chartRect.left() + ratio * m_chartRect.width(); painter.drawLine(QPointF(sx, m_chartRect.top()), QPointF(sx, m_chartRect.bottom())); } } for(int decade = yStart; decade <= yEnd; ++decade) { for(int multiplier = 2; multiplier <= 9; ++multiplier) { double value = decade + qLn(static_cast(multiplier)) / qLn(10.0); if(value <= m_logRange.top() || value >= m_logRange.bottom()) { continue; } double ratio = (value - m_logRange.top()) / qMax(1e-12, m_logRange.height()); double sy = m_chartRect.bottom() - ratio * m_chartRect.height(); painter.drawLine(QPointF(m_chartRect.left(), sy), QPointF(m_chartRect.right(), sy)); } } painter.setPen(QPen(QColor(214, 214, 214), 1, Qt::SolidLine)); for(int x = xStart; x <= xEnd; ++x) { if(x < m_logRange.left() || x > m_logRange.right()) { continue; } double ratio = (x - m_logRange.left()) / qMax(1e-12, m_logRange.width()); double sx = m_chartRect.left() + ratio * m_chartRect.width(); painter.drawLine(QPointF(sx, m_chartRect.top()), QPointF(sx, m_chartRect.bottom())); } for(int y = yStart; y <= yEnd; ++y) { if(y < m_logRange.top() || y > m_logRange.bottom()) { continue; } double ratio = (y - m_logRange.top()) / qMax(1e-12, m_logRange.height()); double sy = m_chartRect.bottom() - ratio * m_chartRect.height(); painter.drawLine(QPointF(m_chartRect.left(), sy), QPointF(m_chartRect.right(), sy)); } painter.restore(); } void nmAutomaticFittingCurveChart::drawAxes(QPainter& painter) { painter.save(); painter.setPen(QPen(Qt::black, 1)); painter.drawRect(m_chartRect); QFontMetrics fm(painter.font()); int xStart = static_cast(qFloor(m_logRange.left())); int xEnd = static_cast(qCeil(m_logRange.right())); int yStart = static_cast(qFloor(m_logRange.top())); int yEnd = static_cast(qCeil(m_logRange.bottom())); for(int x = xStart; x <= xEnd; ++x) { if(x < m_logRange.left() || x > m_logRange.right()) { continue; } double ratio = (x - m_logRange.left()) / qMax(1e-12, m_logRange.width()); double sx = m_chartRect.left() + ratio * m_chartRect.width(); QString label = formatPowerTickLabel(x); painter.drawLine(QPointF(sx, m_chartRect.bottom()), QPointF(sx, m_chartRect.bottom() + 5)); painter.drawText(QPointF(sx - fm.width(label) / 2.0, m_chartRect.bottom() + 22), label); } for(int y = yStart; y <= yEnd; ++y) { if(y < m_logRange.top() || y > m_logRange.bottom()) { continue; } double ratio = (y - m_logRange.top()) / qMax(1e-12, m_logRange.height()); double sy = m_chartRect.bottom() - ratio * m_chartRect.height(); QString label = formatPowerTickLabel(y); painter.drawLine(QPointF(m_chartRect.left() - 5, sy), QPointF(m_chartRect.left(), sy)); painter.drawText(QPointF(m_chartRect.left() - fm.width(label) - 9, sy + fm.height() / 3.0), label); } QString xTitle = tr("Time (hr)"); painter.drawText(QPointF(m_chartRect.center().x() - fm.width(xTitle) / 2.0, height() - 8), xTitle); painter.save(); painter.translate(13, m_chartRect.center().y() + 46); painter.rotate(-90); painter.drawText(QPointF(0, 0), tr("Pressure (MPa)")); painter.restore(); painter.restore(); } void nmAutomaticFittingCurveChart::drawCurveLine(QPainter& painter, const QVector& points, const QColor& color) { if(points.size() < 2) { return; } painter.save(); painter.setClipRect(m_chartRect.adjusted(-1, -1, 1, 1)); painter.setPen(QPen(color, 2)); QPainterPath path; QPointF first = dataToScreen(points[0]); path.moveTo(first); for(int i = 1; i < points.size(); ++i) { path.lineTo(dataToScreen(points[i])); } painter.drawPath(path); painter.restore(); } void nmAutomaticFittingCurveChart::drawCrossMarkers(QPainter& painter, const QVector& points, const QColor& color) { if(points.isEmpty()) { return; } painter.save(); painter.setClipRect(m_chartRect.adjusted(-1, -1, 1, 1)); painter.setPen(QPen(color, 1)); int step = qMax(1, points.size() / 220); const double size = kTargetCrossHalfSize; for(int i = 0; i < points.size(); i += step) { QPointF p = dataToScreen(points[i]); painter.drawLine(QPointF(p.x() - size, p.y() - size), QPointF(p.x() + size, p.y() + size)); painter.drawLine(QPointF(p.x() - size, p.y() + size), QPointF(p.x() + size, p.y() - size)); } painter.restore(); } void nmAutomaticFittingCurveChart::drawCircleMarkers(QPainter& painter, const QVector& points, const QColor& color) { if(points.isEmpty()) { return; } painter.save(); painter.setClipRect(m_chartRect.adjusted(-1, -1, 1, 1)); painter.setPen(QPen(color, 1)); painter.setBrush(Qt::NoBrush); int step = qMax(1, points.size() / 220); for(int i = 0; i < points.size(); i += step) { painter.drawEllipse(dataToScreen(points[i]), kTargetCircleRadius, kTargetCircleRadius); } painter.restore(); } nmWxAutomaticfittingStart::nmWxAutomaticfittingStart(QWidget *parent) : QDialog(parent) , rightSplitter(nullptr) , chartGroup(nullptr) , curveChart(nullptr) , m_autoFitterPSO(nullptr) , m_autoFitterGA(nullptr) , m_algorithmType(FITTING_ALGORITHM_PSO) , m_maxIterations(100) , m_targetError(0.001) , m_wellName("") , m_isFinished(false) , m_bestFitnessEver(1e10) , m_startTime() { setupUI(); // 设置窗口属性 setWindowTitle(tr("Auto Fitting Progress")); setMinimumSize(1000, 650); setModal(false); } nmWxAutomaticfittingStart::~nmWxAutomaticfittingStart() { } void nmWxAutomaticfittingStart::setupUI() { // 创建主要布局 mainLayout = new QHBoxLayout(); leftLayout = new QVBoxLayout(); rightLayout = new QVBoxLayout(); // 设置左右布局 setupParameterArea(); setupTableArea(); setupChartArea(); setupLogArea(); setupControlArea(); // 左侧布局 leftLayout->addWidget(parameterGroup); leftLayout->addStretch(); leftLayout->addWidget(tableGroup); // 右侧布局 rightSplitter = new QSplitter(Qt::Vertical, this); rightSplitter->addWidget(chartGroup); rightSplitter->addWidget(logGroup); rightSplitter->setStretchFactor(0, 3); rightSplitter->setStretchFactor(1, 2); rightSplitter->setSizes(QList() << 320 << 260); rightLayout->addWidget(rightSplitter); // 主布局 mainLayout->addLayout(leftLayout, 1); mainLayout->addLayout(rightLayout, 2); // 控制区域布局 QHBoxLayout *controlLayout = new QHBoxLayout(); controlLayout->addWidget(progressBar); controlLayout->addWidget(stopButton); QVBoxLayout *overallLayout = new QVBoxLayout(); overallLayout->addLayout(mainLayout); overallLayout->addLayout(controlLayout); setLayout(overallLayout); } void nmWxAutomaticfittingStart::setupParameterArea() { parameterGroup = new QGroupBox(tr("Parameter Display"), this); parameterGroup->setFixedWidth(280); QGridLayout *paramLayout = new QGridLayout(parameterGroup); // 创建标签 algorithmTypeLabel = new QLabel(tr("Algorithm:")); currentIterationLabel = new QLabel(tr("Current Iteration:")); maxIterationLabel = new QLabel(tr("Max Iteration:")); currentComfortLabel = new QLabel(tr("Current Error:")); bestComfortLabel = new QLabel(tr("Best Error:")); targetPrecisionLabel = new QLabel(tr("Target Precision:")); targetWellLabel = new QLabel(tr("Target Well:")); // 创建数值显示标签 algorithmTypeValue = new QLabel("PSO"); currentIterationValue = new QLabel("0"); maxIterationValue = new QLabel("100"); currentComfortValue = new QLabel("N/A"); bestComfortValue = new QLabel("N/A"); targetPrecisionValue = new QLabel("0.001"); targetWellValue = new QLabel("N/A"); // 添加到布局 paramLayout->addWidget(algorithmTypeLabel, 0, 0); paramLayout->addWidget(algorithmTypeValue, 0, 1); paramLayout->addWidget(currentIterationLabel, 1, 0); paramLayout->addWidget(currentIterationValue, 1, 1); paramLayout->addWidget(maxIterationLabel, 2, 0); paramLayout->addWidget(maxIterationValue, 2, 1); paramLayout->addWidget(currentComfortLabel, 3, 0); paramLayout->addWidget(currentComfortValue, 3, 1); paramLayout->addWidget(bestComfortLabel, 4, 0); paramLayout->addWidget(bestComfortValue, 4, 1); paramLayout->addWidget(targetPrecisionLabel, 5, 0); paramLayout->addWidget(targetPrecisionValue, 5, 1); paramLayout->addWidget(targetWellLabel, 6, 0); paramLayout->addWidget(targetWellValue, 6, 1); parameterGroup->setLayout(paramLayout); } void nmWxAutomaticfittingStart::setupTableArea() { tableGroup = new QGroupBox(tr("Parameter Table"), this); tableGroup->setFixedWidth(300); QVBoxLayout *tableLayout = new QVBoxLayout(tableGroup); tableWidget = new QTableWidget(0, 2, this); tableWidget->setHorizontalHeaderLabels(QStringList() << tr("Parameter Name") << tr("Optimal Value")); // 设置表格属性 tableWidget->setAlternatingRowColors(true); tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); tableWidget->verticalHeader()->setVisible(false); tableWidget->setMinimumHeight(310); // 设置列宽 QHeaderView* header = tableWidget->horizontalHeader(); header->setResizeMode(0, QHeaderView::ResizeToContents); header->setResizeMode(1, QHeaderView::Stretch); // 设置表格填充整个区域 tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // 隐藏滚动条 tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); tableLayout->addWidget(tableWidget); tableGroup->setLayout(tableLayout); } void nmWxAutomaticfittingStart::setupChartArea() { chartGroup = new QGroupBox(tr("Fitting Curve"), this); QVBoxLayout* chartLayout = new QVBoxLayout(chartGroup); curveChart = new nmAutomaticFittingCurveChart(chartGroup); curveChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); chartLayout->setMargin(6); chartLayout->addWidget(curveChart); chartGroup->setLayout(chartLayout); chartGroup->setMinimumHeight(240); } void nmWxAutomaticfittingStart::setupLogArea() { logGroup = new QGroupBox(tr("Log Window"), this); QVBoxLayout *logLayout = new QVBoxLayout(logGroup); logTextEdit = new QTextEdit(this); logTextEdit->setReadOnly(true); logTextEdit->setFont(QFont("Consolas", 9)); // 添加初始日志 addLogMessage(tr("System initialization completed")); addLogMessage(tr("Waiting for fitting to start")); logLayout->addWidget(logTextEdit); logGroup->setLayout(logLayout); } void nmWxAutomaticfittingStart::setupControlArea() { // 进度条 progressBar = new QProgressBar(this); progressBar->setRange(0, 100); progressBar->setValue(0); progressBar->setTextVisible(true); // 设置进度条为绿色 progressBar->setStyleSheet( "QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; }" "QProgressBar::chunk { background-color: #4CAF50; }" ); // 停止按钮 stopButton = new QPushButton(tr("Stop"), this); stopButton->setFixedSize(80, 30); stopButton->setEnabled(false); // 连接信号槽 connect(stopButton, SIGNAL(clicked()), this, SLOT(onStopButtonClicked())); } void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitPSO* autoFitter) { m_autoFitterPSO = autoFitter; m_autoFitterGA = nullptr; // 清空GA实例 m_algorithmType = FITTING_ALGORITHM_PSO; // 更新算法类型显示 algorithmTypeValue->setText("PSO"); algorithmTypeValue->setStyleSheet("QLabel { color: blue; font-weight: bold; }"); if (m_autoFitterPSO) { connect(m_autoFitterPSO, SIGNAL(progressUpdated(int, double)), this, SLOT(onFittingProgress(int, double))); connect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)), this, SLOT(onFittingFinished(bool, QString))); connect(m_autoFitterPSO, SIGNAL(logMessageGenerated(QString)), this, SLOT(onLogMessageReceived(QString))); connect(m_autoFitterPSO, SIGNAL(bestCurveUpdated(QVector >,QVector >,int,double)), this, SLOT(onBestCurveUpdated(QVector >,QVector >,int,double))); // 启用停止按钮 stopButton->setEnabled(true); addLogMessage(tr("PSO auto fitting started")); } } void nmWxAutomaticfittingStart::setAutoFitterGA(nmCalculationAutoFitGA* autoFitter) { m_autoFitterGA = autoFitter; m_autoFitterPSO = nullptr; // 清空PSO实例 m_algorithmType = FITTING_ALGORITHM_GA; // 更新算法类型显示 algorithmTypeValue->setText("GA"); algorithmTypeValue->setStyleSheet("QLabel { color: red; font-weight: bold; }"); if (m_autoFitterGA) { connect(m_autoFitterGA, SIGNAL(progressUpdated(int, double)), this, SLOT(onFittingProgress(int, double))); connect(m_autoFitterGA, SIGNAL(fittingFinished(bool, QString)), this, SLOT(onFittingFinished(bool, QString))); connect(m_autoFitterGA, SIGNAL(logMessageGenerated(QString)), this, SLOT(onLogMessageReceived(QString))); // 启用停止按钮 stopButton->setEnabled(true); addLogMessage(tr("GA auto fitting started")); } } void nmWxAutomaticfittingStart::setFittingParameters(int maxIterations, double targetError, const QString& wellName) { m_maxIterations = maxIterations; m_targetError = targetError; m_wellName = wellName; maxIterationValue->setText(QString::number(maxIterations)); targetPrecisionValue->setText(formatScientific(targetError)); targetWellValue->setText(wellName); progressBar->setRange(0, maxIterations); QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA"; addLogMessage(tr("%1 fitting parameters set: MaxIterations=%2, TargetAccuracy=%3, TargetWell=%4") .arg(algorithmName).arg(maxIterations).arg(formatScientific(targetError)).arg(wellName)); } void nmWxAutomaticfittingStart::markFittingStarted() { m_startTime = QDateTime::currentDateTime(); QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA"; QString timestamp = m_startTime.toString("yyyy-MM-dd hh:mm:ss"); addLogMessage(tr("=== %1 Fitting Session Started at %2 ===") .arg(algorithmName) .arg(timestamp)); } void nmWxAutomaticfittingStart::setSelectedParameters(const QStringList& parameterNames) { m_selectedParameters = parameterNames; // 立即更新参数表格 updateParameterTable(); QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA"; addLogMessage(tr("%1 selected parameters: %2").arg(algorithmName).arg(parameterNames.join(", "))); } void nmWxAutomaticfittingStart::setTargetLogLogData(const QVector >& targetData) { if(curveChart) { curveChart->setTargetLogLogData(targetData); } } void nmWxAutomaticfittingStart::onFittingProgress(int iteration, double fitness) { // 确保iteration在合理范围内 int displayIteration = qMax(1, qMin(iteration, m_maxIterations)); // 更新进度条 progressBar->setValue(displayIteration); double progress = (double)displayIteration / m_maxIterations * 100; progressBar->setFormat(QString("%1%").arg(progress, 0, 'f', 1)); // 更新参数显示 currentIterationValue->setText(QString::number(displayIteration)); currentComfortValue->setText(formatScientific(fitness)); // 更新最佳适应度 if (fitness < m_bestFitnessEver) { m_bestFitnessEver = fitness; bestComfortValue->setText(formatScientific(fitness)); bestComfortValue->setStyleSheet("QLabel { color: green; font-weight: bold; }"); addLogMessage(tr("Better solution found: %1").arg(formatScientific(fitness))); // 更新参数表格 updateParameterTable(); } else { bestComfortValue->setStyleSheet("QLabel { color: black; }"); } } void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& message) { m_isFinished = true; QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA"; // 更新状态 if (success) { progressBar->setValue(m_maxIterations); // 区分正常完成和用户停止 if(message.contains("stopped by user", Qt::CaseInsensitive)) { addLogMessage(tr("%1 fitting stopped by user: %2").arg(algorithmName).arg(message)); // 更新进度条显示停止状态 progressBar->setFormat(tr("Stopped: 100%")); progressBar->setStyleSheet( "QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; }" "QProgressBar::chunk { background-color: #FF9800; }" // 橙色表示停止 ); } else if(message.contains("target achieved", Qt::CaseInsensitive)) { addLogMessage(tr("%1 fitting completed successfully - target achieved: %2").arg(algorithmName).arg(message)); // 更新进度条显示成功状态 progressBar->setFormat(tr("Target Achieved: 100%")); progressBar->setStyleSheet( "QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; }" "QProgressBar::chunk { background-color: #4CAF50; }" // 绿色表示成功 ); } else { addLogMessage(tr("%1 fitting completed successfully: %2").arg(algorithmName).arg(message)); // 更新进度条显示完成状态 progressBar->setFormat(tr("Completed: 100%")); progressBar->setStyleSheet( "QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; }" "QProgressBar::chunk { background-color: #2196F3; }" // 蓝色表示完成 ); } } else { // 真正的失败情况 addLogMessage(tr("%1 fitting failed: %2").arg(algorithmName).arg(message)); // 更新进度条显示失败状态 int currentProgress = progressBar->value(); progressBar->setFormat(tr("Failed (%1%)").arg((double)currentProgress / m_maxIterations * 100, 0, 'f', 1)); progressBar->setStyleSheet( "QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; }" "QProgressBar::chunk { background-color: #F44336; }" // 红色表示失败 ); } // 禁用停止按钮 stopButton->setEnabled(true); stopButton->setText(tr("Close")); // 连接关闭功能 disconnect(stopButton, SIGNAL(clicked()), this, SLOT(onStopButtonClicked())); connect(stopButton, SIGNAL(clicked()), this, SLOT(close())); // 最终更新参数表格 updateParameterTable(); // 添加完成时间戳 QDateTime endTime = QDateTime::currentDateTime(); QString timestamp = endTime.toString("yyyy-MM-dd hh:mm:ss"); addLogMessage(tr("=== %1 Fitting Session Ended at %2 ===") .arg(algorithmName).arg(timestamp)); // 如果有开始时间,则计算总耗时 if (m_startTime.isValid()) { qint64 ms = m_startTime.msecsTo(endTime); double seconds = ms / 1000.0; int minutes = static_cast(seconds / 60.0); double remainSec = seconds - minutes * 60.0; // 同时给总秒数和分秒 addLogMessage(tr("Total fitting time: %1 s (%2 min %3 s)") .arg(seconds, 0, 'f', 2) .arg(minutes) .arg(remainSec, 0, 'f', 2)); } } void nmWxAutomaticfittingStart::onStopButtonClicked() { bool isRunning = false; if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) { isRunning = m_autoFitterPSO->isRunning(); } else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) { isRunning = m_autoFitterGA->isRunning(); } if (isRunning) { QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA"; int ret = QMessageBox::question(this, tr("Confirm Stop"), tr("Are you sure you want to stop the %1 fitting process?").arg(algorithmName), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (ret == QMessageBox::Yes) { if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) { m_autoFitterPSO->stopFitting(); } else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) { m_autoFitterGA->stopFitting(); } addLogMessage(tr("User requested to stop %1 fitting").arg(algorithmName)); } } } void nmWxAutomaticfittingStart::updateParameterTable() { // 设置表格行数 tableWidget->setRowCount(m_selectedParameters.size()); // 更新表格内容 for (int i = 0; i < m_selectedParameters.size(); ++i) { // 参数名 QTableWidgetItem* nameItem = new QTableWidgetItem(m_selectedParameters.at(i)); nameItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); tableWidget->setItem(i, 0, nameItem); // 最优参数值 QString valueText = "N/A"; if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) { QVector bestSolution = m_autoFitterPSO->getBestSolution(); if (i < bestSolution.size()) { valueText = formatScientific(bestSolution[i]); } } else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) { QVector bestSolution = m_autoFitterGA->getBestSolution(); if (i < bestSolution.size()) { valueText = formatScientific(bestSolution[i]); } } QTableWidgetItem* valueItem = new QTableWidgetItem(valueText); valueItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); valueItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); tableWidget->setItem(i, 1, valueItem); } } void nmWxAutomaticfittingStart::addLogMessage(const QString& message) { // 获取滚动条 QScrollBar* scrollBar = logTextEdit->verticalScrollBar(); // 记录当前是否在底部 bool wasAtBottom = (scrollBar->value() == scrollBar->maximum()); // 添加消息 logTextEdit->append(message); // 只有当用户之前在底部时才自动滚动到底部 if (wasAtBottom) { scrollBar->setValue(scrollBar->maximum()); } } QString nmWxAutomaticfittingStart::formatScientific(double value) { return QString::number(value, 'e', 3); } void nmWxAutomaticfittingStart::closeEvent(QCloseEvent *event) { bool isRunning = false; QString algorithmName; if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) { isRunning = m_autoFitterPSO->isRunning(); algorithmName = "PSO"; } else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) { isRunning = m_autoFitterGA->isRunning(); algorithmName = "GA"; } if (isRunning && !m_isFinished) { int ret = QMessageBox::question(this, tr("Confirm Close"), tr("%1 fitting is running. Do you want to stop it and close the window?").arg(algorithmName), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (ret == QMessageBox::Yes) { if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) { m_autoFitterPSO->stopFitting(); } else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) { m_autoFitterGA->stopFitting(); } event->accept(); } else { event->ignore(); } } else { event->accept(); } } void nmWxAutomaticfittingStart::onLogMessageReceived(const QString& message) { addLogMessage(message); } void nmWxAutomaticfittingStart::onBestCurveUpdated(QVector > targetData, QVector > bestData, int iteration, double fitness) { if(!curveChart) { return; } if(!targetData.isEmpty()) { curveChart->setTargetLogLogData(targetData); } curveChart->setBestLogLogData(bestData, iteration, fitness); }