优化加载动画

This commit is contained in:
徐海 2026-05-13 11:29:36 +08:00
parent d0be9a8654
commit 814b85d40b
8 changed files with 122 additions and 109 deletions

View File

@ -68,12 +68,14 @@ void TwoDSpectralCompliance::SetAnalyzeDataFilename(const QMap<QString, QVariant
QString csvFile = data_files_set.first().toString(); QString csvFile = data_files_set.first().toString();
if (csvFile.isEmpty()) if (csvFile.isEmpty())
return; return;
_busy_indicator->Start();
auto functionToRun = [this, csvFile]() { auto functionToRun = [this, csvFile]() {
_busy_indicator->Start();
readCsv(csvFile); readCsv(csvFile);
generateSurfaceData(); generateSurfaceData();
updateSpectrogram(); QMetaObject::invokeMethod(this, [this]() {
_busy_indicator->Stop(); updateSpectrogram();
_busy_indicator->Stop();
}, Qt::QueuedConnection);
}; };
QThread* load_data_thread = QThread::create(functionToRun); QThread* load_data_thread = QThread::create(functionToRun);
load_data_thread->start(); load_data_thread->start();

View File

@ -83,11 +83,14 @@ void AntiConformEnergySpectrumView::SetAnalyzeDataFilename(const QMap<QString, Q
void AntiConformEnergySpectrumView::loadAndProcess() void AntiConformEnergySpectrumView::loadAndProcess()
{ {
_busy_indicator->Start();
auto functionToRun = [this]() { auto functionToRun = [this]() {
if (_data_filename.isEmpty()) if (_data_filename.isEmpty()) {
QMetaObject::invokeMethod(this, [this]() {
_busy_indicator->Stop();
}, Qt::QueuedConnection);
return; return;
}
_busy_indicator->Start();
std::vector<SpectrumData> rawData; std::vector<SpectrumData> rawData;
io::CSVReader<4> in(QStrToSysPath(_data_filename)); io::CSVReader<4> in(QStrToSysPath(_data_filename));
@ -110,7 +113,9 @@ void AntiConformEnergySpectrumView::loadAndProcess()
} }
if (rawData.empty()) { if (rawData.empty()) {
_busy_indicator->Stop(); QMetaObject::invokeMethod(this, [this]() {
_busy_indicator->Stop();
}, Qt::QueuedConnection);
return; return;
} }
@ -129,7 +134,9 @@ void AntiConformEnergySpectrumView::loadAndProcess()
} }
if (vx.isEmpty()) { if (vx.isEmpty()) {
_busy_indicator->Stop(); QMetaObject::invokeMethod(this, [this]() {
_busy_indicator->Stop();
}, Qt::QueuedConnection);
return; return;
} }

View File

@ -89,13 +89,15 @@ void CoincidenceEventTimeView::SetAnalyzeDataFilename(const QMap<QString, QVaria
void CoincidenceEventTimeView::loadAndProcess() void CoincidenceEventTimeView::loadAndProcess()
{ {
_busy_indicator->Start();
auto functionToRun = [this]() { auto functionToRun = [this]() {
if (_data_filenames.isEmpty()) return; if (_data_filenames.isEmpty()) {
QMetaObject::invokeMethod(this, [this]() {
_busy_indicator->Start(); _busy_indicator->Stop();
}, Qt::QueuedConnection);
return;
}
std::vector<SpectrumData> rawData; std::vector<SpectrumData> rawData;
for (const QString& filename : _data_filenames) { for (const QString& filename : _data_filenames) {
io::CSVReader<5> in(QStrToSysPath(filename)); io::CSVReader<5> in(QStrToSysPath(filename));
in.read_header(io::ignore_extra_column, in.read_header(io::ignore_extra_column,
@ -126,17 +128,15 @@ void CoincidenceEventTimeView::loadAndProcess()
}); });
} }
if (rawData.empty()) { if (rawData.empty()) {
_busy_indicator->Stop(); QMetaObject::invokeMethod(this, [this]() {
_busy_indicator->Stop();
}, Qt::QueuedConnection);
return; return;
} }
int eventId = 0; int eventId = 0;
QList<SpectrumData> SpectrumDataList; QList<SpectrumData> SpectrumDataList;
for (const auto& spdt : rawData) for (const auto& spdt : rawData) {
{
// 第一个事件是初级粒子 // 第一个事件是初级粒子
const auto& firstParticle = spdt; const auto& firstParticle = spdt;
int boardId = firstParticle.board_id + 1; int boardId = firstParticle.board_id + 1;
@ -150,15 +150,16 @@ void CoincidenceEventTimeView::loadAndProcess()
QVector<double> vx, vy; QVector<double> vx, vy;
int ncount = 0; int ncount = 0;
for(const auto &spdt : SpectrumDataList) for(const auto &spdt : SpectrumDataList) {
{
ncount++; ncount++;
vx << spdt.timestamp; vx << spdt.timestamp;
vy << ncount; vy << ncount;
} }
if (vx.isEmpty()) { if (vx.isEmpty()) {
_busy_indicator->Stop(); QMetaObject::invokeMethod(this, [this]() {
_busy_indicator->Stop();
}, Qt::QueuedConnection);
return; return;
} }

View File

@ -44,14 +44,14 @@ void CountRateAnalysisView::InitViewWorkspace(const QString &project_name)
} }
void CountRateAnalysisView::SetAnalyzeDataFilename(const QMap<QString, QVariant> &data_files_set) void CountRateAnalysisView::SetAnalyzeDataFilename(const QMap<QString, QVariant> &data_files_set)
{ {
if(!data_files_set.isEmpty()) if(!data_files_set.isEmpty()) {
{ _busy_indicator->Start();
auto functionToRun = [this,data_files_set] auto functionToRun = [this,data_files_set] {
{ QVector<ParticleInjectTime> data = getParticleInjectTimeData(data_files_set.first().toString());
_busy_indicator->Start(); QMetaObject::invokeMethod(this, [this, data]() {
m_AllData = getParticleInjectTimeData(data_files_set.first().toString()); setData(data);
setData(m_AllData); _busy_indicator->Stop();
_busy_indicator->Stop(); }, Qt::QueuedConnection);
}; };
QThread* load_data_thread = QThread::create(functionToRun); QThread* load_data_thread = QThread::create(functionToRun);
load_data_thread->start(); load_data_thread->start();

View File

@ -44,7 +44,6 @@ private:
BusyIndicator* _busy_indicator = nullptr; BusyIndicator* _busy_indicator = nullptr;
CustomQwtPlot *plot; CustomQwtPlot *plot;
QMenu* _menu = nullptr; QMenu* _menu = nullptr;
QVector<ParticleInjectTime> m_AllData;//存储的所有的粒子入射时间数据
}; };
#endif //COUNTRATEANALYSIS_H #endif //COUNTRATEANALYSIS_H

View File

@ -1,74 +1,76 @@
#ifndef ENERGYCOUNTPEAKFITVIEW_H #ifndef ENERGYCOUNTPEAKFITVIEW_H
#define ENERGYCOUNTPEAKFITVIEW_H #define ENERGYCOUNTPEAKFITVIEW_H
#include <QObject> #include "PeakFitParamsDialog.h"
#include <QWidget>
#include <MeasureAnalysisView.h> #include <MeasureAnalysisView.h>
#include <QwtPlotPicker>
#include <QwtPickerDragRectMachine>
#include <QRubberBand> // 新增
#include <qwt_plot_shapeitem.h> // 新增
#include <QDateTime> #include <QDateTime>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include "PeakFitParamsDialog.h" #include <QObject>
#include <QRubberBand> // 新增
#include <QWidget>
#include <QwtPickerDragRectMachine>
#include <QwtPlotPicker>
#include <qwt_plot_shapeitem.h> // 新增
#include "DataCalcProcess/FindPeaksBySvd.h"
#include "DataCalcProcess/NolinearLeastSquaresCurveFit.h"
#include "DataCalcProcess/AdaptiveSimpsonIntegrate.h" #include "DataCalcProcess/AdaptiveSimpsonIntegrate.h"
#include "DataCalcProcess/FindPeaksBySvd.h"
#include "DataCalcProcess/MathModelDefine.h" #include "DataCalcProcess/MathModelDefine.h"
#include "DataCalcProcess/NolinearLeastSquaresCurveFit.h"
struct PeakFitResult struct PeakFitResult {
{ double center; // 峰中心能量 (keV)
double center; // 峰中心能量 (keV) double amplitude; // 高斯振幅
double amplitude; // 高斯振幅 double sigma; // 高斯宽度
double sigma; // 高斯宽度 double fwhm; // 半高宽 = sigma * 2.355
double fwhm; // 半高宽 = sigma * 2.355 double area; // 峰面积(积分)
double area; // 峰面积(积分) double baseline; // 常数项 C
double baseline; // 常数项 C double sigmoidH; // Sigmoid 项高度 H
double sigmoidH; // Sigmoid 项高度 H double sigmoidW; // Sigmoid 项宽度 W
double sigmoidW; // Sigmoid 项宽度 W
}; };
struct FitCurveData { struct FitCurveData {
double amplitude; // 峰幅度 double amplitude; // 峰幅度
double sigma; // 高斯sigma double sigma; // 高斯sigma
double sigmoidH; // Sigmoid高度 double sigmoidH; // Sigmoid高度
double sigmoidW; // Sigmoid宽度 double sigmoidW; // Sigmoid宽度
double baseline; // 本底 double baseline; // 本底
double center; // 峰中心 double center; // 峰中心
double xMin; // 曲线X范围最小值 double xMin; // 曲线X范围最小值
double xMax; // 曲线X范围最大值 double xMax; // 曲线X范围最大值
QRectF selectionRect; //关联的框选区域 QRectF selectionRect; // 关联的框选区域
double fwhm; // 半高宽 double fwhm; // 半高宽
double area; // 峰面积 double area; // 峰面积
}; };
struct PeakFitHistoryItem { struct PeakFitHistoryItem {
QDateTime timestamp; QDateTime timestamp;
QList<FitCurveData> curveList; //存储多条曲线 QList<FitCurveData> curveList; // 存储多条曲线
QJsonObject toJson() const; QJsonObject toJson() const;
static PeakFitHistoryItem fromJson(const QJsonObject& obj); static PeakFitHistoryItem fromJson(const QJsonObject& obj);
}; };
struct CurrentFitRecord { struct CurrentFitRecord {
PeakFitResult result; // 拟合结果 PeakFitResult result; // 拟合结果
arma::vec params; // 完整拟合参数 arma::vec params; // 完整拟合参数
double xMin; // X范围最小值 double xMin; // X范围最小值
double xMax; // X范围最大值 double xMax; // X范围最大值
int historyIndex; // 对应_fitHistoryList中的索引-1表示未保存 int historyIndex; // 对应_fitHistoryList中的索引-1表示未保存
CurrentFitRecord() : historyIndex(-1) {} // 构造函数初始化 CurrentFitRecord()
: historyIndex(-1)
{
} // 构造函数初始化
}; };
struct DisplayedCurveRef { struct DisplayedCurveRef {
int historyIndex; // 对应 _fitHistoryList 的索引 int historyIndex; // 对应 _fitHistoryList 的索引
int curveIndex; // 对应 curveList 的索引 int curveIndex; // 对应 curveList 的索引
int curveStartIndex; //对应 _fitCurves 中的起始索引一条历史曲线对应2条Qwt曲线拟合+本底) int curveStartIndex; // 对应 _fitCurves 中的起始索引一条历史曲线对应2条Qwt曲线拟合+本底)
int rectIndex; //对应 _selectionRectItems 中的索引 int rectIndex; // 对应 _selectionRectItems 中的索引
}; };
class PlotRectItem; // 前向声明 class PlotRectItem; // 前向声明
class QMenu; class QMenu;
class CustomQwtPlot; class CustomQwtPlot;
class CustomQwtPlotXaxisSelector; class CustomQwtPlotXaxisSelector;
@ -76,11 +78,10 @@ class QwtPlotPicker;
class QwtPlotCurve; class QwtPlotCurve;
class BusyIndicator; class BusyIndicator;
class EnergyCountPeakFitView : public MeasureAnalysisView class EnergyCountPeakFitView : public MeasureAnalysisView {
{
Q_OBJECT Q_OBJECT
public: public:
EnergyCountPeakFitView(QWidget *parent = nullptr); EnergyCountPeakFitView(QWidget* parent = nullptr);
virtual ~EnergyCountPeakFitView(); virtual ~EnergyCountPeakFitView();
virtual void InitViewWorkspace(const QString& project_name) override final; virtual void InitViewWorkspace(const QString& project_name) override final;
@ -89,30 +90,30 @@ public:
private: private:
void setupPlot(); void setupPlot();
void setupMenu(); void setupMenu();
void loadDataFromFile(const QString &data_name, const QString& filename); void loadDataFromFile(const QString& data_name, const QString& filename);
void loadHistoryFromFile(); void loadHistoryFromFile();
void saveHistoryToFile(); void saveHistoryToFile();
void saveCurrentFitToHistory(); // 保存当前拟合结果 void saveCurrentFitToHistory(); // 保存当前拟合结果
void displayFitFromHistory(const PeakFitHistoryItem& item); // 显示历史拟合曲线 void displayFitFromHistory(const PeakFitHistoryItem& item); // 显示历史拟合曲线
//处理鼠标悬停检测 // 处理鼠标悬停检测
void updateHoverState(const QPoint& mousePos); void updateHoverState(const QPoint& mousePos);
//显示选中的曲线 // 显示选中的曲线
void displaySelectedCurves(const QList<FitCurveData>& curves, const QList<DisplayedCurveRef>& refs); void displaySelectedCurves(const QList<FitCurveData>& curves, const QList<DisplayedCurveRef>& refs);
private slots: private slots:
void onActionCurveShowSetting(); void onActionCurveShowSetting();
void onActionPlotConfigure(); void onActionPlotConfigure();
void clearAllSelectionRects(); void clearAllSelectionRects();
void clearFitCurves(); // 清除所有拟合曲线 void clearFitCurves(); // 清除所有拟合曲线
void onActionSaveCurrentFit(); void onActionSaveCurrentFit();
void onActionShowFitHistory(); void onActionShowFitHistory();
void onActionDeleteHoveredRect(); void onActionDeleteHoveredRect();
//重新拟合当前悬停的框选区域 // 重新拟合当前悬停的框选区域
void onActionRefitCurrentRect(); void onActionRefitCurrentRect();
protected: protected:
bool eventFilter(QObject* watched, QEvent* event) override; // 事件过滤器 bool eventFilter(QObject* watched, QEvent* event) override; // 事件过滤器
private: private:
void startSelection(const QPoint& pos); void startSelection(const QPoint& pos);
@ -121,7 +122,7 @@ private:
void addSelectionRect(const QRectF& plotRect); void addSelectionRect(const QRectF& plotRect);
void fadeSelectionRectBorders(); void fadeSelectionRectBorders();
QList<PeakFitResult> performPeakFitting(const QVector<double>& x, const QVector<double>& y/*, const arma::vec* userP0 = nullptr*/); QList<PeakFitResult> performPeakFitting(const QVector<double>& x, const QVector<double>& y /*, const arma::vec* userP0 = nullptr*/);
// 根据拟合参数生成曲线 // 根据拟合参数生成曲线
QwtPlotCurve* createFitCurve(const PeakFitResult& result, double xMin, double xMax, const QString& name); QwtPlotCurve* createFitCurve(const PeakFitResult& result, double xMin, double xMax, const QString& name);
@ -135,33 +136,30 @@ private:
// 手动框选状态 // 手动框选状态
bool _isSelecting = false; bool _isSelecting = false;
QPoint _selectionStart; QPoint _selectionStart;
QRubberBand* _rubberBand = nullptr; // 原生橡皮筋 QRubberBand* _rubberBand = nullptr; // 原生橡皮筋
QList<PlotRectItem*> _selectionRectItems; QList<PlotRectItem*> _selectionRectItems;
QwtPlotCurve* _fittedCurve = nullptr; // 显示拟合结果的曲线 QwtPlotCurve* _fittedCurve = nullptr; // 显示拟合结果的曲线
// 存储当前显示的拟合曲线,用于清除 // 存储当前显示的拟合曲线,用于清除
QList<QwtPlotCurve*> _fitCurves; QList<QwtPlotCurve*> _fitCurves;
QString _historyFilePath; QString _historyFilePath;
// [NEW] 最近一次拟合结果(用于手动保存) // [NEW] 最近一次拟合结果(用于手动保存)
PeakFitResult _lastFitResult; PeakFitResult _lastFitResult;
arma::vec _lastFitParams; // 6个拟合参数 (A, delt, H, W, C, P) arma::vec _lastFitParams; // 6个拟合参数 (A, delt, H, W, C, P)
double _lastXMin = 0.0, _lastXMax = 0.0; double _lastXMin = 0.0, _lastXMax = 0.0;
bool _hasLastFit = false; bool _hasLastFit = false;
QString _workspace; QString _workspace;
//存储当前所有拟合结果(用于多曲线管理) // 存储当前所有拟合结果(用于多曲线管理)
QList<CurrentFitRecord> _currentFitRecords; QList<CurrentFitRecord> _currentFitRecords;
//历史记录列表 // 历史记录列表
QList<PeakFitHistoryItem> _fitHistoryList; QList<PeakFitHistoryItem> _fitHistoryList;
//当前悬停的框选区域 // 当前悬停的框选区域
PlotRectItem* _hoveredRectItem = nullptr; PlotRectItem* _hoveredRectItem = nullptr;
QList<DisplayedCurveRef> _displayedHistoryCurves; QList<DisplayedCurveRef> _displayedHistoryCurves;
}; };
#endif // ENERGYCOUNTPEAKFITVIEW_H #endif // ENERGYCOUNTPEAKFITVIEW_H

View File

@ -106,9 +106,9 @@ void ParticleInjectTimeAnalysisView::setupMenu()
void ParticleInjectTimeAnalysisView::updateData(bool b_init_update) void ParticleInjectTimeAnalysisView::updateData(bool b_init_update)
{ {
_busy_indicator->Start();
auto functionToRun = [this, b_init_update](){ auto functionToRun = [this, b_init_update](){
if(!_data_filename.isEmpty()) { if(!_data_filename.isEmpty()) {
_busy_indicator->Start();
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString(); std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString(); std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
std::string energy_str = QString(QStringLiteral(u"能量(KeV)")).toStdString(); std::string energy_str = QString(QStringLiteral(u"能量(KeV)")).toStdString();
@ -137,12 +137,14 @@ void ParticleInjectTimeAnalysisView::updateData(bool b_init_update)
if ( y_max < time_count ) if ( y_max < time_count )
y_max = time_count; y_max = time_count;
} }
_plot->SetAxisInitRange(QwtPlot::xBottom, 0.0f, x_max); QMetaObject::invokeMethod(this, [this, x_max, y_max, x, y]() {
_plot->SetAxisInitRange(QwtPlot::yLeft, 0.0f, y_max); _plot->SetAxisInitRange(QwtPlot::xBottom, 0.0f, x_max);
_curve->setSamples(x, y); _plot->SetAxisInitRange(QwtPlot::yLeft, 0.0f, y_max);
_plot->replot(); _curve->setSamples(x, y);
LOG_INFO(QStringLiteral(u"%1数据更新完毕.").arg(this->GetViewName())); _plot->replot();
_busy_indicator->Stop(); LOG_INFO(QStringLiteral(u"%1数据更新完毕.").arg(this->GetViewName()));
_busy_indicator->Stop();
}, Qt::QueuedConnection);
} }
}; };
QThread* load_data_thread = QThread::create(functionToRun); QThread* load_data_thread = QThread::create(functionToRun);

View File

@ -156,17 +156,21 @@ void ParticleTimeDifferenceView::loadDataFromFile()
if ( y_max < count ) if ( y_max < count )
y_max = count; y_max = count;
} }
_histogram->setData(new QwtIntervalSeriesData(samples)); QMetaObject::invokeMethod(this, [this, samples, x_max, y_max]() {
_plot->SetAxisInitRange(QwtPlot::xBottom, 0.0f, x_max); _histogram->setData(new QwtIntervalSeriesData(samples));
_plot->SetAxisInitRange(QwtPlot::yLeft, 0.0f, y_max); _plot->SetAxisInitRange(QwtPlot::xBottom, 0.0f, x_max);
_plot->replot(); _plot->SetAxisInitRange(QwtPlot::yLeft, 0.0f, y_max);
const QString& info = QStringLiteral(u"粒子时间差分析处理完成."); _plot->replot();
LOG_INFO(info); const QString& info = QStringLiteral(u"粒子时间差分析处理完成.");
LOG_INFO(info);
}, Qt::QueuedConnection);
} catch (const std::exception& e) { } catch (const std::exception& e) {
const QString& e_what = QString::fromStdString(e.what()); const QString& e_what = QString::fromStdString(e.what());
LOG_WARN(QStringLiteral(u"粒子时间差分析处理异常:%1").arg(e_what)); LOG_WARN(QStringLiteral(u"粒子时间差分析处理异常:%1").arg(e_what));
} }
_busy_indicator->Stop(); QMetaObject::invokeMethod(this, [this]() {
_busy_indicator->Stop();
}, Qt::QueuedConnection);
} }
void ParticleTimeDifferenceView::onActionPlotConfigure() void ParticleTimeDifferenceView::onActionPlotConfigure()