diff --git a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp index 2725142..6b0b7b8 100644 --- a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp +++ b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp @@ -181,6 +181,7 @@ void EnergyCountPeakFitView::setupPlot() _plot->setAxisAutoScale(QwtPlot::xBottom, true); _plot->setAxisAutoScale(QwtPlot::yLeft, true); + _plot->enableAxis(QwtPlot::xBottom); _plot->enableAxis(QwtPlot::yLeft); @@ -483,45 +484,51 @@ void EnergyCountPeakFitView::finishSelection() } } - if (roiX.size() >= 3) { + if (roiX.size() >= 3) + { QStringList algorithms; algorithms << QStringLiteral(u"y=A*exp(-pow(x-P,2)/(2*pow(delt,2))) + H/(1+exp((x-P)/W)) + C"); - bool ok = false; - QString selected = QInputDialog::getItem(this, - QStringLiteral(u"选择拟合算法"), - QStringLiteral(u"请选择用于当前框选区域的峰拟合算法:"), - algorithms, - 0, - false, - &ok); - if (ok && selected == QStringLiteral(u"y=A*exp(-pow(x-P,2)/(2*pow(delt,2))) + H/(1+exp((x-P)/W)) + C")) - { - arma::vec armaRoiX(roiX.size()), armaRoiY(roiY.size()); + + // 关键修改:实例化QInputDialog并设置窗口标志(去除问号按钮) + QInputDialog* algorithmDlg = new QInputDialog(this, Qt::Dialog | Qt::WindowCloseButtonHint); + algorithmDlg->setWindowTitle(QStringLiteral(u"选择拟合算法")); + algorithmDlg->setLabelText(QStringLiteral(u"请选择用于当前框选区域的峰拟合算法:")); + algorithmDlg->setComboBoxItems(algorithms); + algorithmDlg->setComboBoxEditable(false); + algorithmDlg->setModal(false); // 保持非模态 + algorithmDlg->setAttribute(Qt::WA_DeleteOnClose); // 关闭自动释放内存 + + // 异步处理用户选择(原if(ok)逻辑移到这里) + connect(algorithmDlg, &QInputDialog::accepted, this, [=]() { + QString selected = algorithmDlg->textValue(); + if (selected == algorithms.first()) + { + arma::vec armaRoiX(roiX.size()), armaRoiY(roiY.size()); for (int i = 0; i < roiX.size(); ++i) { armaRoiX(i) = roiX[i]; armaRoiY(i) = roiY[i]; - } - // 3. 执行拟合(曲线自动添加到主图) - QList results = performPeakFitting(roiX, roiY/*, &userP0*/); - if (results.isEmpty()) { - qDebug()<< QStringLiteral(u"拟合结果:") << QStringLiteral(u"未检测到有效峰或拟合失败。"); - } else { - addSelectionRect(plotRect); - fadeSelectionRectBorders(); // 框选边框变为虚线(与原逻辑一致) - if (!_selectionRectItems.isEmpty() && !results.isEmpty()) - { - PlotRectItem* lastRect = _selectionRectItems.last(); - const PeakFitResult& r = results.first(); - // 传入:峰位、FWHM、面积、本底 - lastRect->setPeakData(r.center, r.fwhm, r.area, r.baseline); - _plot->replot(); // 刷新显示 - } - saveCurrentFitToHistory(); + // 执行拟合(曲线自动添加到主图) + QList results = performPeakFitting(roiX, roiY); + if (results.isEmpty()) { + qDebug()<< QStringLiteral(u"拟合结果:") << QStringLiteral(u"未检测到有效峰或拟合失败。"); + } else { + addSelectionRect(plotRect); + fadeSelectionRectBorders(); // 框选边框变为虚线(与原逻辑一致) + if (!_selectionRectItems.isEmpty() && !results.isEmpty()) + { + PlotRectItem* lastRect = _selectionRectItems.last(); + const PeakFitResult& r = results.first(); + // 传入:峰位、FWHM、面积、本底 + lastRect->setPeakData(r.center, r.fwhm, r.area, r.baseline); + _plot->replot(); // 刷新显示 + } + saveCurrentFitToHistory(); + } } - } - } else { - qDebug() << QStringLiteral(u"框选区域内数据点不足3个,无法拟合。"); + }); + + algorithmDlg->show(); // 非模态显示 } } } @@ -872,34 +879,41 @@ void EnergyCountPeakFitView::onActionShowFitHistory() } } if (!hasCurveData) { - QMessageBox::information(this, - QStringLiteral(u"峰拟合结果"), - QStringLiteral(u"暂无历史拟合数据,请先进行拟合并保存。")); + QMessageBox* msgBox = new QMessageBox(this); + msgBox->setWindowTitle(QStringLiteral(u"峰拟合结果")); + msgBox->setText(QStringLiteral(u"暂无历史拟合数据,请先进行拟合并保存。")); + msgBox->setStandardButtons(QMessageBox::Ok); + msgBox->setModal(false); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); + msgBox->show(); return; } - QDialog dlg(this); - dlg.setWindowTitle(QStringLiteral(u"峰拟合结果历史")); - dlg.setMinimumSize(1000, 500); - QVBoxLayout* layout = new QVBoxLayout(&dlg); + // 替换栈变量为堆上非模态对话框 + QDialog* historyDlg = new QDialog(this , Qt::Dialog | Qt::WindowCloseButtonHint); + historyDlg->setWindowTitle(QStringLiteral(u"峰拟合结果")); + historyDlg->setMinimumSize(1000, 500); + historyDlg->setModal(false); + historyDlg->setAttribute(Qt::WA_DeleteOnClose); - QStandardItemModel* model = new QStandardItemModel(&dlg); + QVBoxLayout* layout = new QVBoxLayout(historyDlg); + QStandardItemModel* model = new QStandardItemModel(historyDlg); QStringList headers; headers << "" << "振幅(A)" << "高斯宽度(delt)" << "Sigmoid幅度(H)" << "Sigmoid宽度(W)" << "基线(C)" << "峰中心(P)"; model->setHorizontalHeaderLabels(headers); QVector> rowToDataIndices; - model->blockSignals(true); for (int historyIdx = 0; historyIdx < _fitHistoryList.size(); ++historyIdx) { const auto& historyItem = _fitHistoryList[historyIdx]; for (int curveIdx = 0; curveIdx < historyItem.curveList.size(); ++curveIdx) { const auto& curve = historyItem.curveList[curveIdx]; QList rowItems; - QStandardItem* checkItem = new QStandardItem(); checkItem->setCheckable(true); checkItem->setEditable(false); + bool isCurrentlyDisplayed = false; for (const auto& ref : _displayedHistoryCurves) { if (ref.historyIndex == historyIdx && ref.curveIndex == curveIdx) { @@ -910,35 +924,12 @@ void EnergyCountPeakFitView::onActionShowFitHistory() checkItem->setCheckState(isCurrentlyDisplayed ? Qt::Checked : Qt::Unchecked); rowItems.append(checkItem); - // 2. 振幅 (A) - QStandardItem* itemA = new QStandardItem(QString::number(curve.amplitude, 'f', 4)); - itemA->setEditable(false); - rowItems.append(itemA); - - // 3. Sigma (delt) - QStandardItem* itemDelt = new QStandardItem(QString::number(curve.sigma, 'f', 4)); - itemDelt->setEditable(false); - rowItems.append(itemDelt); - - // 4. SigmoidH (H) - QStandardItem* itemH = new QStandardItem(QString::number(curve.sigmoidH, 'f', 4)); - itemH->setEditable(false); - rowItems.append(itemH); - - // 5. SigmoidW (W) - QStandardItem* itemW = new QStandardItem(QString::number(curve.sigmoidW, 'f', 4)); - itemW->setEditable(false); - rowItems.append(itemW); - - // 6. 本底 (C) - QStandardItem* itemC = new QStandardItem(QString::number(curve.baseline, 'f', 4)); - itemC->setEditable(false); - rowItems.append(itemC); - - // 7. 峰位 (P) - QStandardItem* itemP = new QStandardItem(QString::number(curve.center, 'f', 4)); - itemP->setEditable(false); - rowItems.append(itemP); + rowItems.append(new QStandardItem(QString::number(curve.amplitude, 'f', 4))); + rowItems.append(new QStandardItem(QString::number(curve.sigma, 'f', 4))); + rowItems.append(new QStandardItem(QString::number(curve.sigmoidH, 'f', 4))); + rowItems.append(new QStandardItem(QString::number(curve.sigmoidW, 'f', 4))); + rowItems.append(new QStandardItem(QString::number(curve.baseline, 'f', 4))); + rowItems.append(new QStandardItem(QString::number(curve.center, 'f', 4))); model->appendRow(rowItems); rowToDataIndices.append(qMakePair(historyIdx, curveIdx)); @@ -947,14 +938,14 @@ void EnergyCountPeakFitView::onActionShowFitHistory() model->blockSignals(false); QHBoxLayout* topBtnLayout = new QHBoxLayout(); - QPushButton* btnSelectAll = new QPushButton(QStringLiteral(u"全选"), &dlg); - QPushButton* btnInvertSelection = new QPushButton(QStringLiteral(u"反选"), &dlg); + QPushButton* btnSelectAll = new QPushButton(QStringLiteral(u"全选"), historyDlg); + QPushButton* btnInvertSelection = new QPushButton(QStringLiteral(u"反选"), historyDlg); topBtnLayout->addWidget(btnSelectAll); topBtnLayout->addWidget(btnInvertSelection); topBtnLayout->addStretch(); layout->addLayout(topBtnLayout); - QTableView* tableView = new QTableView(&dlg); + QTableView* tableView = new QTableView(historyDlg); tableView->setModel(model); tableView->setSelectionBehavior(QAbstractItemView::SelectRows); tableView->setSelectionMode(QAbstractItemView::SingleSelection); @@ -964,16 +955,16 @@ void EnergyCountPeakFitView::onActionShowFitHistory() tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); layout->addWidget(tableView); - QPushButton* btnDelete = new QPushButton(QStringLiteral(u"删除选中记录"), &dlg); - QPushButton* btnClose = new QPushButton(QStringLiteral(u"关闭"), &dlg); + QPushButton* btnDelete = new QPushButton(QStringLiteral(u"删除选中记录"), historyDlg); + QPushButton* btnClose = new QPushButton(QStringLiteral(u"关闭"), historyDlg); QHBoxLayout* bottomBtnLayout = new QHBoxLayout(); bottomBtnLayout->addStretch(); bottomBtnLayout->addWidget(btnDelete); bottomBtnLayout->addWidget(btnClose); layout->addLayout(bottomBtnLayout); - // --- 全选按钮 --- - connect(btnSelectAll, &QPushButton::clicked, [&]() { + // 全选按钮逻辑保持不变 + connect(btnSelectAll, &QPushButton::clicked, [=]() { model->blockSignals(true); for (int row = 0; row < model->rowCount(); ++row) { model->item(row, 0)->setCheckState(Qt::Checked); @@ -996,8 +987,8 @@ void EnergyCountPeakFitView::onActionShowFitHistory() displaySelectedCurves(selectedCurves, selectedRefs); }); - // --- 反选按钮 --- - connect(btnInvertSelection, &QPushButton::clicked, [&]() { + // 反选按钮逻辑保持不变 + connect(btnInvertSelection, &QPushButton::clicked, [=]() { model->blockSignals(true); for (int row = 0; row < model->rowCount(); ++row) { QStandardItem* item = model->item(row, 0); @@ -1021,8 +1012,8 @@ void EnergyCountPeakFitView::onActionShowFitHistory() displaySelectedCurves(selectedCurves, selectedRefs); }); - // --- 复选框状态改变 --- - connect(model, &QStandardItemModel::itemChanged, [&](QStandardItem* /*item*/) { + // 复选框状态改变逻辑保持不变 + connect(model, &QStandardItemModel::itemChanged, [=](QStandardItem* /*item*/) { QList selectedCurves; QList selectedRefs; for (int row = 0; row < model->rowCount(); ++row) { @@ -1038,9 +1029,8 @@ void EnergyCountPeakFitView::onActionShowFitHistory() displaySelectedCurves(selectedCurves, selectedRefs); }); - // --- 删除选中记录按钮(核心精准删除逻辑) --- - connect(btnDelete, &QPushButton::clicked, [&]() { - // 1. 收集要删除的历史索引 + // 删除按钮逻辑(改为非模态确认框) + connect(btnDelete, &QPushButton::clicked, [=]() { QSet historyIndicesToDelete; for (int row = 0; row < model->rowCount(); ++row) { if (model->item(row, 0)->checkState() == Qt::Checked) { @@ -1048,95 +1038,101 @@ void EnergyCountPeakFitView::onActionShowFitHistory() historyIndicesToDelete.insert(indices.first); } } - if (historyIndicesToDelete.isEmpty()) { - QMessageBox::warning(&dlg, QStringLiteral(u"提示"), QStringLiteral(u"请先勾选要删除的记录。")); + QMessageBox* msgBox = new QMessageBox(historyDlg); + msgBox->setWindowTitle(QStringLiteral(u"提示")); + msgBox->setText(QStringLiteral(u"请先勾选要删除的记录。")); + msgBox->setStandardButtons(QMessageBox::Ok); + msgBox->setModal(false); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->show(); return; } - // 2. 确认删除 QString msg = QStringLiteral(u"确定要删除选中的 %1 条历史记录吗?").arg(historyIndicesToDelete.size()); - QMessageBox::StandardButton reply = QMessageBox::question(&dlg, QStringLiteral(u"确认删除"), msg, QMessageBox::Yes | QMessageBox::No); - if (reply != QMessageBox::Yes) return; + QMessageBox* confirmBox = new QMessageBox(historyDlg); + confirmBox->setWindowTitle(QStringLiteral(u"确认删除")); + confirmBox->setText(msg); + confirmBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + confirmBox->setModal(false); + confirmBox->setAttribute(Qt::WA_DeleteOnClose); - // 3. 先从界面上精准删除显示(从后往前删) - QList sortedDisplayIndices; - for (int i = 0; i < _displayedHistoryCurves.size(); ++i) { - if (historyIndicesToDelete.contains(_displayedHistoryCurves[i].historyIndex)) { - sortedDisplayIndices.append(i); + // 保存临时数据 + confirmBox->setProperty("historyIndicesToDelete", QVariant::fromValue(historyIndicesToDelete)); + confirmBox->setProperty("model", QVariant::fromValue(model)); + confirmBox->setProperty("rowToDataIndices", QVariant::fromValue(rowToDataIndices)); + + connect(confirmBox, &QMessageBox::buttonClicked, this, [=](QAbstractButton* button) { + if (confirmBox->standardButton(button) == QMessageBox::Yes) { + QSet historyIndicesToDelete = confirmBox->property("historyIndicesToDelete").value>(); + + // 原有删除逻辑保持不变 + QList sortedDisplayIndices; + for (int i = 0; i < _displayedHistoryCurves.size(); ++i) { + if (historyIndicesToDelete.contains(_displayedHistoryCurves[i].historyIndex)) { + sortedDisplayIndices.append(i); + } + } + std::sort(sortedDisplayIndices.begin(), sortedDisplayIndices.end(), std::greater()); + for (int dispIdx : sortedDisplayIndices) { + const auto& ref = _displayedHistoryCurves[dispIdx]; + if (ref.curveStartIndex + 1 < _fitCurves.size()) { + _fitCurves[ref.curveStartIndex + 1]->detach(); + delete _fitCurves[ref.curveStartIndex + 1]; + _fitCurves.removeAt(ref.curveStartIndex + 1); + _fitCurves[ref.curveStartIndex]->detach(); + delete _fitCurves[ref.curveStartIndex]; + _fitCurves.removeAt(ref.curveStartIndex); + } + if (ref.rectIndex < _selectionRectItems.size()) { + _selectionRectItems[ref.rectIndex]->detach(); + delete _selectionRectItems[ref.rectIndex]; + _selectionRectItems.removeAt(ref.rectIndex); + } + _displayedHistoryCurves.removeAt(dispIdx); + } + + QList remainingCurves; + QList remainingRefs; + for (const auto& ref : _displayedHistoryCurves) { + remainingCurves.append(_fitHistoryList[ref.historyIndex].curveList[ref.curveIndex]); + remainingRefs.append(ref); + } + + QList sortedHistoryIndices = historyIndicesToDelete.values(); + std::sort(sortedHistoryIndices.begin(), sortedHistoryIndices.end(), std::greater()); + for (auto& ref : remainingRefs) { + int offset = 0; + for (int delIdx : sortedHistoryIndices) { + if (delIdx < ref.historyIndex) offset++; + } + ref.historyIndex -= offset; + } + + for (int historyIdx : sortedHistoryIndices) { + _fitHistoryList.removeAt(historyIdx); + } + + clearFitCurves(); + clearAllSelectionRects(); + _displayedHistoryCurves.clear(); + if (!remainingCurves.isEmpty()) { + displaySelectedCurves(remainingCurves, remainingRefs); + } else { + _plot->replot(); + } + + saveHistoryToFile(); + historyDlg->accept(); + onActionShowFitHistory(); // 重新打开更新后的历史窗口 } - } - std::sort(sortedDisplayIndices.begin(), sortedDisplayIndices.end(), std::greater()); + }); - for (int dispIdx : sortedDisplayIndices) { - const auto& ref = _displayedHistoryCurves[dispIdx]; - - // 删除曲线(先删本底,再删拟合) - if (ref.curveStartIndex + 1 < _fitCurves.size()) { - _fitCurves[ref.curveStartIndex + 1]->detach(); - delete _fitCurves[ref.curveStartIndex + 1]; - _fitCurves.removeAt(ref.curveStartIndex + 1); - - _fitCurves[ref.curveStartIndex]->detach(); - delete _fitCurves[ref.curveStartIndex]; - _fitCurves.removeAt(ref.curveStartIndex); - } - - // 删除框选 - if (ref.rectIndex < _selectionRectItems.size()) { - _selectionRectItems[ref.rectIndex]->detach(); - delete _selectionRectItems[ref.rectIndex]; - _selectionRectItems.removeAt(ref.rectIndex); - } - - // 移除引用 - _displayedHistoryCurves.removeAt(dispIdx); - } - - // 4. 收集剩余显示的数据(用于重建) - QList remainingCurves; - QList remainingRefs; - for (const auto& ref : _displayedHistoryCurves) { - remainingCurves.append(_fitHistoryList[ref.historyIndex].curveList[ref.curveIndex]); - remainingRefs.append(ref); - } - - // 5. 更新剩余引用的 historyIndex(处理索引偏移) - QList sortedHistoryIndices = historyIndicesToDelete.values(); - std::sort(sortedHistoryIndices.begin(), sortedHistoryIndices.end(), std::greater()); - - for (auto& ref : remainingRefs) { - int offset = 0; - for (int delIdx : sortedHistoryIndices) { - if (delIdx < ref.historyIndex) offset++; - } - ref.historyIndex -= offset; - } - - // 6. 删除历史数据 - for (int historyIdx : sortedHistoryIndices) { - _fitHistoryList.removeAt(historyIdx); - } - - // 7. 重建界面显示(确保索引正确) - clearFitCurves(); - clearAllSelectionRects(); - _displayedHistoryCurves.clear(); - - if (!remainingCurves.isEmpty()) { - displaySelectedCurves(remainingCurves, remainingRefs); - } else { - _plot->replot(); - } - - // 8. 保存并刷新 - saveHistoryToFile(); - dlg.accept(); - onActionShowFitHistory(); + confirmBox->show(); }); - connect(btnClose, &QPushButton::clicked, &dlg, &QDialog::accept); - dlg.exec(); + connect(btnClose, &QPushButton::clicked, historyDlg, &QDialog::close); + historyDlg->show(); // 非模态显示 } void EnergyCountPeakFitView::onActionDeleteHoveredRect() diff --git a/src/EnergyCountPeakFitView/PeakFitParamsDialog.cpp b/src/EnergyCountPeakFitView/PeakFitParamsDialog.cpp index 942cae6..c909c02 100644 --- a/src/EnergyCountPeakFitView/PeakFitParamsDialog.cpp +++ b/src/EnergyCountPeakFitView/PeakFitParamsDialog.cpp @@ -1,11 +1,10 @@ #include "PeakFitParamsDialog.h" PeakFitParamsDialog::PeakFitParamsDialog(const FitParams& defaultParams, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::Dialog | Qt::WindowCloseButtonHint) { setWindowTitle(QStringLiteral(u"光子峰拟合参数设置")); setModal(true); - QVBoxLayout* mainLayout = new QVBoxLayout(this); QFormLayout* form = new QFormLayout();