From 5d808b6264b682a26b06680fb4dc6db84b6b3a22 Mon Sep 17 00:00:00 2001 From: anxinglong <2910824064@qq.com> Date: Thu, 26 Mar 2026 17:35:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=89=E7=BB=B4=E8=A7=86=E5=9B=BE=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MeasureAnalysisTreeView.cpp | 7 +- .../CoincidenceSpectrumProcess.h | 75 +++ .../ConformityAnalysis.cpp | 301 ++++++++++ .../ConformityAnalysis.h | 86 +++ .../ConformityAnalysis.ui | 213 +++++++ .../DetectorStatusSummary.cpp | 209 +++++++ .../DetectorStatusSummary.h | 74 +++ .../DetectorStatusSummary.ui | 204 +++++++ .../ParticleDataStatistics.cpp | 137 +++++ .../ParticleDataStatistics.h | 55 ++ .../ParticleDataStatistics.ui | 253 +++++++++ .../ThreeDDisplay.cpp | 521 ++++++++++++++++++ .../ThreeDDisplay.h | 112 ++++ .../ThreeDDisplay.ui | 333 +++++++++++ 14 files changed, 2576 insertions(+), 4 deletions(-) create mode 100644 src/ThreeDimensionalConformityAnalysisView/CoincidenceSpectrumProcess.h create mode 100644 src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.cpp create mode 100644 src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.h create mode 100644 src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.ui create mode 100644 src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.cpp create mode 100644 src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.h create mode 100644 src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.ui create mode 100644 src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.cpp create mode 100644 src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.h create mode 100644 src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.ui create mode 100644 src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp create mode 100644 src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.h create mode 100644 src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.ui diff --git a/src/MeasureAnalysisTreeView.cpp b/src/MeasureAnalysisTreeView.cpp index 47bd826..f7ca37d 100644 --- a/src/MeasureAnalysisTreeView.cpp +++ b/src/MeasureAnalysisTreeView.cpp @@ -56,7 +56,6 @@ void MeasureAnalysisTreeView::onCustomContextMenuRequested(const QPoint& pos) QVariant user_data = _model->GetNodeUserData(item); } - void MeasureAnalysisTreeView::onNodeDoubleClicked(const QModelIndex& index) { if (!index.isValid()) @@ -218,9 +217,9 @@ void MeasureAnalysisTreeView::onNodeDoubleClicked(const QModelIndex& index) case AnalysisType::CoincidenceParticleEnergySpectrum3DView: { MeasureAnalysisProjectModel* project_model = _model->GetProjectModel(project_name); if (project_model) { - auto file_name = project_model->GetAllChannelParticleDataFilename(); - if ( !file_name.isEmpty() ) { - data_files_set[QStringLiteral(u"符合粒子能量3D视图")] = file_name; + auto file_name_list = project_model->GetTimeWinConformEnergyDataFilenameList(project_model->GetConformTimeWin()); + for (auto it = file_name_list.constBegin(); it!=file_name_list.constEnd(); ++it) { + data_files_set[QString::number(it.key())] = it.value(); } } } break; diff --git a/src/ThreeDimensionalConformityAnalysisView/CoincidenceSpectrumProcess.h b/src/ThreeDimensionalConformityAnalysisView/CoincidenceSpectrumProcess.h new file mode 100644 index 0000000..be85b64 --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/CoincidenceSpectrumProcess.h @@ -0,0 +1,75 @@ +#ifndef COINCIDENCESPECTRUMPROCESS_H +#define COINCIDENCESPECTRUMPROCESS_H + +#include +// 定义板卡和通道的最大数量 +static constexpr int MAX_BOARD = 8; +static constexpr int MAX_CHANNEL = 4; + +// 用于存储三维数据 +typedef struct SurfacePoint { + float primaryEnergy; // 初级粒子能量 + float secondaryEnergySum; // 次级粒子能量和 + int count; // 符合事件计数 +}SURFACEPOINT; + + + + +namespace CoincidenceSpectrum { + +// 二次符合事件筛选 +arma::mat EventFilter2Order(const arma::mat& raw_spec_data, double conformity_event_time_win = 50.0f); + +// 计算二维直方图 +void Hist3(const arma::mat& data, int nx, int ny, arma::mat& counts, arma::vec& x_edges, arma::vec& y_edges); + +// 二次符合事件处理 +namespace F2t9Order { + + // 能谱数据结构 + struct SpectrumData { + int board_id; // 板卡号 + int channel_id; // 通道号 + double energy; // 能量 + unsigned long long timestamp; // 时间戳(纳秒) + }; + + // 符合事件结果结构 + struct CoincidenceEvent { + int coincidence_order; // 符合次数(2-9) + std::vector events; // 符合的事件集合 + unsigned int time_window; // 使用的时间窗口(秒) + }; + + // 读取CSV数据文件 + std::vector ReadCsv(const std::string& filename); + + // 按时间戳排序数据 + void SortDataByTimestamp(std::vector& data); + + // 2-9次能谱符合处理 + std::vector ProcessCoincidence( + const std::vector& data, + unsigned int time_window, // 时间窗口(纳秒) + int min_order = 2, // 最小符合次数 + int max_order = 9 // 最大符合次数 + ); + + // 统计符合事件结果 + arma::mat AnalyzeCoincidenceResults( + const std::vector& results, + int max_order = 9 + ); + + // 保存结果到文件 + void SaveResults( + const std::vector& results, + const arma::mat& stats, + const std::string& base_filename = "coincidence_results" + ); +}; + +} + +#endif // COINCIDENCESPECTRUMPROCESS_H diff --git a/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.cpp b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.cpp new file mode 100644 index 0000000..5f01d48 --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.cpp @@ -0,0 +1,301 @@ +#include "ConformityAnalysis.h" +#include "ui_ConformityAnalysis.h" +#include +ConformityAnalysis::ConformityAnalysis(QWidget *parent) : + MeasureAnalysisView(parent), + ui(new Ui::ConformityAnalysis) +{ + ui->setupUi(this); + memset(m_boardChannel, 0, sizeof(m_boardChannel)); // 新增 +//// // 获取当前可执行文件的路径 +//// QString executablePath = QCoreApplication::applicationDirPath() + "/test.csv"; +// connect(ui->widget,SIGNAL(Signal_ClickedBoard(int,int)),this,SLOT(slot_ClickedBoard(int,int))); +// connect(ui->widget,SIGNAL(Signal_InitialState()),this,SLOT(slot_InitialState())); +//// qDebug()<<"可执行文件:"<< executablePath; +// setCsvFile(m_fileName); +// readCsv(); +// handleBoard(); +// m_FirstParticle = statisticsFirstParticleQuantity(); +// setAllBoardData(); +// calculateFirstSecondRange(); +// setThreeUiData(); +// generateSurfaceData(m_CoincidenceEventVector); +} + +ConformityAnalysis::~ConformityAnalysis() +{ + delete ui; +} + +void ConformityAnalysis::InitViewWorkspace(const QString &project_name) +{ + +} + +void ConformityAnalysis::SetAnalyzeDataFilename(const QMap &data_files_set) +{ + QStringList ch_count_data_name = data_files_set.keys(); + int conformCount = ui->comboBox->currentIndex() + 2; + for (const QString& ch_count_data_name : ch_count_data_name) { + qDebug()< reader(m_fileName.toStdString()); + qDebug()<<"板卡号:"; + // 符合事件变量 + int id = 0; + std::vector SpectrumDataList; + // 逐行读取数据 + while (true) + { + int comply = 0; + F2t9Order::SpectrumData SpectrumData; + bool flag = reader.read_row(/*EventData.coincidence_order*/comply,SpectrumData.board_id, SpectrumData.channel_id, SpectrumData.energy,SpectrumData.timestamp); + qDebug()<<"符合事件:"< data = handleBasicSubordinate(m_CoincidenceEventVector,board,channel); + if(data.size() <= 0) return; + generateSurfaceData(data); + m_subordinate = handleSubordinate(data, board, channel); + for (int i = 0; i < m_subordinate.keys().size();i++) + { + QString objectName =m_subordinate.keys().at(i); + QStringList parts = objectName.split('_'); + int bd = parts[1].toInt(); + int ch = parts[2].toInt(); + ui->widget->setWidgetData(bd,ch,m_boardChannel[bd - 1][ch - 1],m_subordinate[objectName]); + } + ui->widget->setWidgetData(board,channel,m_boardChannel[board - 1][channel - 1],data.size()); +} + +void ConformityAnalysis::handleBoard() +{ + for (int i = 0; i < m_CoincidenceEventVector.size(); i++) + { + for(auto spetruData:m_CoincidenceEventVector.at(i).events) + { + m_boardChannel[spetruData.channel_id][spetruData.board_id]++; + } + } +} + +QMap ConformityAnalysis::statisticsFirstParticleQuantity() +{ + QMap firstParticleCount; + // 初始化所有板卡通道的计数为0 + for (int board = 1; board <= 8; ++board) + { + for (int channel = 1; channel <= 4; ++channel) + { + QString key = QString("widget_%1_%2").arg(board).arg(channel); + firstParticleCount[key] = 0; + } + } + // 遍历所有符合事件 + for (const auto& event : m_CoincidenceEventVector) + { + if (event.events.empty()) continue; + + // 第一个事件是初级粒子 + const auto& firstParticle = event.events[0]; + int boardId = firstParticle.board_id + 1; // 转换为1-based + int channelId = firstParticle.channel_id + 1; // 转换为1-based + + if (boardId >= 1 && boardId <= 8 && channelId >= 1 && channelId <= 4) { + QString key = QString("widget_%1_%2").arg(boardId).arg(channelId); + firstParticleCount[key]++; + } + } + return firstParticleCount; +} + +std::vector ConformityAnalysis::handleBasicSubordinate(std::vector &eventData, int Board, int Channel) +{ + std::vector EventVector;//所有的初级粒子符合事件 + for (int i = 0; i < eventData.size(); i++) + { + F2t9Order::CoincidenceEvent EventData = eventData.at(i); + F2t9Order::SpectrumData data = EventData.events.at(0); + if (data.board_id == Board - 1&& data.channel_id == Channel - 1) + { + EventVector.push_back(EventData); + m_beginVector.push_back(data); + } + } + return EventVector; +} + +QMap ConformityAnalysis::handleSubordinate(std::vector &eventData,int Board, int Channel) +{ + m_secondVector.clear(); + QMap data; + for (int board = 1; board <= 8; ++board) + { + for (int channel = 1; channel <= 4; ++channel) + { + QString key = QString("widget_%1_%2").arg(board).arg(channel); + data[key] = 0; + for (int j = 0; j < eventData.size(); j++) + { + std::vector dataVector = eventData.at(j).events; + for (int k = 0; k < dataVector.size(); k++) + { + F2t9Order::SpectrumData dataSpectrum = dataVector.at(k); + if (dataSpectrum.board_id == Board - 1 && dataSpectrum.channel_id == Channel - 1) + continue; + if (dataSpectrum.board_id == board - 1 && dataSpectrum.channel_id == channel - 1) + { + data[key]++; + m_secondVector.push_back(dataSpectrum); + } + } + } + } + } + return data; +} + +int ConformityAnalysis::getMaxValue() +{ + auto begin = &m_boardChannel[0][0]; + auto end = begin + MAX_BOARD * MAX_CHANNEL; + return *std::max_element(begin, end); +} + +void ConformityAnalysis::calculateFirstSecondRange() +{ + //符合事件总计数 + m_iComply = 0; + // 找出所有数值中的最小值和最大值 + double minFirstVal = 11111111.1111; + double maxFirstVal = 0.0; + + double minSecondVal = 11111111.1111; + double maxSecondVal = 0.0; + for (int i = 0; i < m_CoincidenceEventVector.size(); i++) + { + F2t9Order::CoincidenceEvent data = m_CoincidenceEventVector.at(i); + m_iComply += data.events.size(); + for (int j = 0; j < data.events.size(); j++) + { + F2t9Order::SpectrumData spectrum = data.events.at(j); + if (j == 0) + { + if (spectrum.energy < minFirstVal) minFirstVal = spectrum.energy; + if (spectrum.energy > maxFirstVal) maxFirstVal = spectrum.energy; + continue; + } + if (spectrum.energy < minSecondVal) minSecondVal = spectrum.energy; + if (spectrum.energy > maxSecondVal) maxSecondVal = spectrum.energy; + } + } + m_dFirstStart = minFirstVal; + m_dFirstEnd = maxFirstVal; + m_dSecondStart = minSecondVal; + m_dSecondEnd = maxSecondVal; +} + +void ConformityAnalysis::setAllBoardData() +{ + for (int board = 1; board <= 8; ++board) + { + for (int channel = 1; channel <= 4; ++channel) + { + QString key = QString("widget_%1_%2").arg(board).arg(channel); + qDebug()<widget->setInitWidgetData(board,channel,m_boardChannel[board - 1][channel-1],m_FirstParticle[key]); + } + } + } +} + + +void ConformityAnalysis::setThreeUiData() +{ + qDebug()<< m_dSecondStart << m_dSecondEnd; + ui->widget_3D->setBasicParticle(m_dFirstStart,m_dFirstEnd); + ui->widget_3D->setSecondParticle(m_dSecondStart,m_dSecondEnd); + ui->widget_3D->setComplyWithEvent(m_iComply); +} + +void ConformityAnalysis::generateSurfaceData(const std::vector& events) +{ + m_surfaceData.clear(); + for (const auto& event : events) + { + if (event.events.empty()) continue; + + float primaryEnergy = event.events[0].energy; + + float secondaryEnergySum = 0.0f; + for (size_t i = 1; i < event.events.size(); i++) { + secondaryEnergySum += event.events[i].energy; + } + SurfacePoint point; + point.primaryEnergy = primaryEnergy; + point.secondaryEnergySum = secondaryEnergySum; + point.count = 1; + + m_surfaceData.append(point); + } + ui->widget_3D->setSurfaceData(m_surfaceData); +} + + + diff --git a/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.h b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.h new file mode 100644 index 0000000..21f87be --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.h @@ -0,0 +1,86 @@ +#ifndef CONFORMITYANALYSIS_H +#define CONFORMITYANALYSIS_H + +#include +#include +#include "ParticleDataStatistics.h" +#include "ThreeDDisplay.h" +#include "csv.h" +#include "CoincidenceSpectrumProcess.h" +#include "MeasureAnalysisView.h" + +using namespace CoincidenceSpectrum; + +namespace Ui { +class ConformityAnalysis; +} + +class ConformityAnalysis : public MeasureAnalysisView +{ + Q_OBJECT + +private: + QMap m_FirstParticle; + QMap m_subordinate; + std::vector m_beginVector; + std::vector m_secondVector; + QVector m_surfaceData; + +public: + explicit ConformityAnalysis(QWidget *parent = nullptr); + ~ConformityAnalysis(); + + virtual void InitViewWorkspace(const QString& project_name) override final; + virtual void SetAnalyzeDataFilename(const QMap& data_files_set); + //设置csv文件路径及文件名称 + void setCsvFile(QString fileName); + //读取csv文件 + void readCsv(); + + +private slots: + void slot_InitialState(); + void slot_ClickedBoard(int board,int channel); + +private: + //处理板卡信息 + void handleBoard(); + //统计每个板卡的初级粒子计数 + QMap statisticsFirstParticleQuantity(); + //处理当前板卡初级粒子信息 + std::vector handleBasicSubordinate(std::vector &eventData,int Board, int Channel); + //处理次级粒子信息 + QMap handleSubordinate(std::vector &eventData, int Board, int Channel); + // 获取最大值 + int getMaxValue(); + //计算全部的初级粒子范围 和 次级粒子范围 + void calculateFirstSecondRange(); + //设置板卡数据信息 + void setAllBoardData(); + //设置符合事件相关信息 + void setThreeUiData(); + //全部谱图数据处理 + void generateSurfaceData(const std::vector& events); + +private: + Ui::ConformityAnalysis *ui; + + QString m_fileName; + + std::vector m_CoincidenceEventVector;//所有的能谱符合处理 + + int m_boardChannel[MAX_BOARD][MAX_CHANNEL]; + + + + double m_dFirstStart = 0.0;//初级粒子起始能量 + double m_dFirstEnd = 0.0;//初级粒子终止能量 + double m_dSecondStart = 0.0;//初级粒子起始能量 + double m_dSecondEnd = 0.0;//初级粒子终止能量 + int m_iComply = 0;//符合事件总计数 +}; + + + + +#endif // CONFORMITYANALYSIS_H diff --git a/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.ui b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.ui new file mode 100644 index 0000000..c053dbe --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.ui @@ -0,0 +1,213 @@ + + + ConformityAnalysis + + + + 0 + 0 + 1920 + 1080 + + + + ConformityAnalysis + + + + + + 6 + + + 6 + + + + + 通过在下图中框选区域,自动识别出符合时间进入下方列表,选择符合时间右侧界面内进行统计 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 符合时间窗: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + 50 + + + + + + + ns + + + + + + + + 0 + 26 + + + + + + + 修改 + + + + + + + 选择符合事件: + + + + + + + + 100 + 22 + + + + + 二次符合 + + + + + 三次符合 + + + + + 四次符合 + + + + + 五次符合 + + + + + 六次符合 + + + + + 七次符合 + + + + + 八次符合 + + + + + 九次符合 + + + + + + + + + + + + 3 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + ThreeDDisplay + QWidget +
ThreeDDisplay.h
+ 1 +
+ + ParticleDataStatistics + QWidget +
ParticleDataStatistics.h
+ 1 +
+
+ + +
diff --git a/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.cpp b/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.cpp new file mode 100644 index 0000000..2776264 --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.cpp @@ -0,0 +1,209 @@ +#include "DetectorStatusSummary.h" +#include "ui_DetectorStatusSummary.h" +#include +#include +DetectorStatusSummary::DetectorStatusSummary(QWidget *parent) : + QWidget(parent), + ui(new Ui::DetectorStatusSummary) +{ + ui->setupUi(this); +} + +DetectorStatusSummary::~DetectorStatusSummary() +{ + delete ui; +} + +void DetectorStatusSummary::setName(QString name) +{ + ui->label_name->setText(name); +} + +void DetectorStatusSummary::setCountName(QString name) +{ + ui->label->setText(name); +} + +void DetectorStatusSummary::setNum(int num) +{ + m_nNum = num; + ui->label_num->setText(QString::number(num)); +} + +QString DetectorStatusSummary::getCountNum() +{ + return ui->label_num->text(); +} + +void DetectorStatusSummary::setBeginSecond(QString str) +{ + ui->label_begin_second->setText(str); +} + +void DetectorStatusSummary::setBeginSecondNum(int num) +{ + ui->label_begin_second_num->setText(QString::number(num)); + +} + +QString DetectorStatusSummary::getBeginSecondNum() +{ + return ui->label_begin_second_num->text(); +} + +void DetectorStatusSummary::setBeginSecondWidget(bool isHide) +{ + if (isHide) + { + ui->label_begin_second->hide(); + ui->label_begin_second_num->hide(); + } + else + { + ui->label_begin_second->show(); + ui->label_begin_second_num->show(); + } +} + +void DetectorStatusSummary::setColorMaxValue(int maxValue) +{ + m_nMaxValue = maxValue; +} + +void DetectorStatusSummary::setInitWidgetColor() +{ + QPainter p(this); + QString strqs = "min-height:35px;max-height:35px;"; + p.fillRect(rect(), QColor("#0E508A")); // 仅自己区域 + ui->label_name->setStyleSheet("background-color: #1E79C2;color:#FFFFFF;"+strqs); + ui->label->setStyleSheet("color:#4CA9F9;"); + ui->label_num->setStyleSheet("background-color: #125997;color:#FFFFFF;font-size: 30px; font-family: Microsoft YaHei; "); + ui->widget_dw->setStyleSheet("background-color: #1B66A7;color:#FFFFFF;" + strqs); + m_useCustomColor = false; + update(); +} + +void DetectorStatusSummary::setHideBorder() +{ + ui->widget->setStyleSheet(""); + flag = false; +} + +void DetectorStatusSummary::setBackgroundColor(const QColor &color) +{ + m_backgroundColor = color; + //QPainter painter(this); + //painter.setRenderHint(QPainter::Antialiasing); + + ////// 创建渐变背景 + ////QLinearGradient gradient(0, 0, width(), 0); + ////gradient.setColorAt(0.0, m_backgroundColor.lighter(110)); + ////gradient.setColorAt(0.5, m_backgroundColor); + ////gradient.setColorAt(1.0, m_backgroundColor.darker(110)); + + //// 绘制背景 + //painter.fillRect(rect(), m_backgroundColor); + //QString strColor = QString("rgb(%1,%2,%3,1)").arg(m_backgroundColor.red()).arg(m_backgroundColor.green()).arg(m_backgroundColor.blue()); + //ui.widget_dw->setStyleSheet(QString("background-color: %1;color:#FFFFFF;").arg(strColor)); + + //// 绘制边框 + //painter.setPen(QPen(QColor(23, 99, 162), 1)); + //painter.drawRect(rect().adjusted(0, 0, -1, -1)); + update(); // 触发重绘 +} + +void DetectorStatusSummary::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + +// QPainter painter(this); +// painter.setRenderHint(QPainter::Antialiasing); + +// //// 创建渐变背景 +// //QLinearGradient gradient(0, 0, width(), 0); +// //gradient.setColorAt(0.0, m_backgroundColor.lighter(110)); +// //gradient.setColorAt(0.5, m_backgroundColor); +// //gradient.setColorAt(1.0, m_backgroundColor.darker(110)); + +// // 绘制背景 +// painter.fillRect(rect(), m_backgroundColor); +// QString strColor = QString("rgb(%1,%2,%3,1)").arg(m_backgroundColor.red()).arg(m_backgroundColor.green()).arg(m_backgroundColor.blue()); +// ui->widget_dw->setStyleSheet(QString("background-color: %1;color:#FFFFFF;").arg(strColor)); + +// ui->label->setStyleSheet(QString("background-color: %1;color:#FFFFFF;").arg(strColor)); +// ui->label_num->setStyleSheet(QString("background-color: %1;color:#FFFFFF;font-size: 30px; font-family: Microsoft YaHei; ").arg(strColor)); + +// // 绘制边框 +// painter.setPen(QPen(QColor(23, 99, 162), 1)); +// painter.drawRect(rect().adjusted(0, 0, -1, -1)); +} + +QColor DetectorStatusSummary::calculateGradientColor(double ratio) +{ + // 四段式渐变:蓝->蓝绿->黄->橙->红 + QColor color; + + if (ratio < 0.25) { + // 蓝到蓝绿 + double subRatio = ratio / 0.25; + color = QColor( + 14 + (51 - 14) * subRatio, // R: 14->51 + 80 + (102 - 80) * subRatio, // G: 80->102 + 138 + (153 - 138) * subRatio // B: 138->153 + ); + } + else if (ratio < 0.5) { + // 蓝绿到黄 + double subRatio = (ratio - 0.25) / 0.25; + color = QColor( + 51 + (244 - 51) * subRatio, // R: 51->244 + 102 + (196 - 102) * subRatio, // G: 102->196 + 153 + (34 - 153) * subRatio // B: 153->34 + ); + } + else if (ratio < 0.75) { + // 黄到橙 + double subRatio = (ratio - 0.5) / 0.25; + color = QColor( + 244 + (239 - 244) * subRatio, // R: 244->239 + 196 + (155 - 196) * subRatio, // G: 196->155 + 34 + (57 - 34) * subRatio // B: 34->57 + ); + } + else { + // 橙到红 + double subRatio = (ratio - 0.75) / 0.25; + color = QColor( + 239 + (255 - 239) * subRatio, // R: 239->255 + 155 + (51 - 155) * subRatio, // G: 155->51 + 57 + (76 - 57) * subRatio // B: 57->76 + ); + } + + return color; +} + +void DetectorStatusSummary::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton && flag == false) + { + if ((ui->label_num->text().toInt() == 0 )&& (ui->label_begin_second_num->text().toInt() == 0)) + return; + + ui->widget->setStyleSheet("QWidget#widget{border: 2px solid #ffffff;}"); + // 可以发出信号 + emit oneclicked(objectName()); + flag = true; + } + else + { + if ((ui->label_num->text().toInt() == 0 )&& (ui->label_begin_second_num->text().toInt() == 0)) + return; + ui->widget->setStyleSheet(""); + // 可以发出信号 + emit twoClicked(objectName()); + flag = false; + } + // 调用基类实现 + QWidget::mousePressEvent(event); +} diff --git a/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.h b/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.h new file mode 100644 index 0000000..c1615c8 --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.h @@ -0,0 +1,74 @@ +#ifndef DETECTORSTATUSSUMMARY_H +#define DETECTORSTATUSSUMMARY_H + +#include + +namespace Ui { +class DetectorStatusSummary; +} + +class DetectorStatusSummary : public QWidget +{ + Q_OBJECT + +public: + explicit DetectorStatusSummary(QWidget *parent = nullptr); + ~DetectorStatusSummary(); + //设置名称 + void setName(QString name); + //设置总计数名称 + void setCountName(QString name); + //设置数量 + void setNum(int num); + //获取总计数 + QString getCountNum(); + + //设置初 次级粒子计数 + void setBeginSecond(QString str); + //设置初 次级粒子计数 数量 + void setBeginSecondNum(int num); + //获取 + QString getBeginSecondNum(); + //设置初级粒子计数隐藏 + void setBeginSecondWidget(bool isHide); + //设置最大值 + void setColorMaxValue(int maxValue); + //设置初始底色 + void setInitWidgetColor(); + //设置隐藏边框 + void setHideBorder(); + /*--------------------设置颜色---------------------------*/ + + // 直接设置颜色(由ConformanceAnalysis调用) + void setBackgroundColor(const QColor& color); +protected: + // 添加paintEvent声明 + void paintEvent(QPaintEvent *event) override; +private: + // 计算渐变颜色 + QColor calculateGradientColor(double ratio); + // 添加私有成员 + QColor m_backgroundColor; + bool m_useCustomColor = false; + /*-------------------------------------------------------*/ + +protected: + /*void paintEvent(QPaintEvent *) override;*/ + void mousePressEvent(QMouseEvent *event) override; + +signals: + void oneclicked(QString value); + void twoClicked(QString value); +private: + Ui::DetectorStatusSummary *ui; + // 计数器数值 + int m_nNum; + + // 点击标志 + bool flag = false; + + // 自适应色阶相关 + int m_nMaxValue = 200; // 当前最大值 +}; + +#endif // DETECTORSTATUSSUMMARY_H diff --git a/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.ui b/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.ui new file mode 100644 index 0000000..35cb7ff --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.ui @@ -0,0 +1,204 @@ + + + DetectorStatusSummary + + + + 0 + 0 + 134 + 156 + + + + Form + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 33 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + 总计数: + + + Qt::AlignCenter + + + + + + + + + + 0 + + + Qt::AlignCenter + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + Qt::Vertical + + + + 17 + 45 + + + + + + + + + 0 + + + 1 + + + 0 + + + 0 + + + 0 + + + + + 次级粒子计数: + + + Qt::AlignCenter + + + + + + + 0 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + + + + + diff --git a/src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.cpp b/src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.cpp new file mode 100644 index 0000000..333480b --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.cpp @@ -0,0 +1,137 @@ +#include "ParticleDataStatistics.h" +#include "ui_ParticleDataStatistics.h" +#include +ParticleDataStatistics::ParticleDataStatistics(QWidget *parent) : + QWidget(parent), + ui(new Ui::ParticleDataStatistics) +{ + ui->setupUi(this); + InitUi(); +} + +ParticleDataStatistics::~ParticleDataStatistics() +{ + delete ui; +} + +void ParticleDataStatistics::InitUi() +{ + m_widgetMap[0][0] = ui->widget_1_1; + m_widgetMap[0][1] = ui->widget_1_2; + m_widgetMap[0][2] = ui->widget_1_3; + m_widgetMap[0][3] = ui->widget_1_4; + m_widgetMap[1][0] = ui->widget_2_1; + m_widgetMap[1][1] = ui->widget_2_2; + m_widgetMap[1][2] = ui->widget_2_3; + m_widgetMap[1][3] = ui->widget_2_4; + m_widgetMap[2][0] = ui->widget_3_1; + m_widgetMap[2][1] = ui->widget_3_2; + m_widgetMap[2][2] = ui->widget_3_3; + m_widgetMap[2][3] = ui->widget_3_4; + m_widgetMap[3][0] = ui->widget_4_1; + m_widgetMap[3][1] = ui->widget_4_2; + m_widgetMap[3][2] = ui->widget_4_3; + m_widgetMap[3][3] = ui->widget_4_4; + m_widgetMap[4][0] = ui->widget_5_1; + m_widgetMap[4][1] = ui->widget_5_2; + m_widgetMap[4][2] = ui->widget_5_3; + m_widgetMap[4][3] = ui->widget_5_4; + m_widgetMap[5][0] = ui->widget_6_1; + m_widgetMap[5][1] = ui->widget_6_2; + m_widgetMap[5][2] = ui->widget_6_3; + m_widgetMap[5][3] = ui->widget_6_4; + m_widgetMap[6][0] = ui->widget_7_1; + m_widgetMap[6][1] = ui->widget_7_2; + m_widgetMap[6][2] = ui->widget_7_3; + m_widgetMap[6][3] = ui->widget_7_4; + m_widgetMap[7][0] = ui->widget_8_1; + m_widgetMap[7][1] = ui->widget_8_2; + m_widgetMap[7][2] = ui->widget_8_3; + m_widgetMap[7][3] = ui->widget_8_4; + for(int bd = 0; bd < MAX_BOARD;bd++ ) + { + for (int ch = 0;ch < MAX_CHANNEL;ch++) + { + QString name = QString("板卡%1(通道%2)").arg(bd + 1).arg(ch + 1); + m_widgetMap[bd][ch]->setName(name); + // 连接探测器点击信号到处理槽函数 + connect(m_widgetMap[bd][ch],SIGNAL(oneclicked(QString)),this,SLOT(_slotClickedBoard(QString))); + connect(m_widgetMap[bd][ch], SIGNAL(twoClicked(QString)), this, SLOT(_slotClickedTwoBoard(QString))); + } + } +} + + +void ParticleDataStatistics::_slotClickedBoard(QString board) +{ + QStringList parts = board.split('_'); + m_bd = parts[1].toInt();//获取初级粒子板卡号 + m_ch = parts[2].toInt();//获取初级粒子通道号 + emit Signal_ClickedBoard(m_bd,m_ch); + QList pdetorList = this->findChildren(); + for (int i = 0; i < pdetorList.size(); i++) + { + if (pdetorList.at(i)->objectName() != board) + pdetorList.at(i)->setHideBorder(); +// QColor color = getColorForValue(0); +// pdetorList.at(i)->setBackgroundColor(color); + } +} + +void ParticleDataStatistics::_slotClickedTwoBoard(QString board) +{ + emit Signal_InitialState();//发送信号更新数据至初始值 +} + + +void ParticleDataStatistics::setWidgetData(int board, int channel, int countNum, int secondNum) +{ + // 参数合法性检查(防止越界) + if (board < 1 || board > MAX_BOARD || channel < 1 || channel > MAX_CHANNEL) { + qWarning() << "setWidgetData: invalid board or channel:" << board << channel; + return; + } + DetectorStatusSummary* widget = m_widgetMap[board - 1][channel - 1]; + if (widget) + { + //当板卡号和通道号都为-1时 设置为为点击状态 + if(board == -1 && channel == -1) + { + widget->setCountName("总计数"); + widget->setNum(countNum); + widget->setBeginSecond("初级粒子计数"); + widget->setBeginSecondNum(secondNum); + return ; + } + //当板卡号和点击的板卡号相同时 设置为初级粒子 + if(board == m_bd && channel == m_ch) + { + widget->setCountName("初级粒子计数"); + widget->setNum(secondNum); + widget->setBeginSecond("总计数"); + widget->setBeginSecondNum(countNum); + } + else//否则为次级粒子 + { + widget->setCountName("次级粒子计数"); + widget->setNum(secondNum); + widget->setBeginSecond("总计数"); + widget->setBeginSecondNum(countNum); + } + } +} + +void ParticleDataStatistics::setInitWidgetData(int board, int channel, int countNum, int secondNum) +{ + DetectorStatusSummary* widget = m_widgetMap[board - 1][channel - 1]; + if(widget==nullptr) + { + return; + } + widget->setCountName("总计数"); + widget->setNum(countNum); + widget->setBeginSecond("初级粒子计数"); + widget->setBeginSecondNum(secondNum); + +} + diff --git a/src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.h b/src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.h new file mode 100644 index 0000000..53eff6e --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.h @@ -0,0 +1,55 @@ +#ifndef PARTICLEDATASTATISTICS_H +#define PARTICLEDATASTATISTICS_H + +#include +#include "DetectorStatusSummary.h" +#include "CoincidenceSpectrumProcess.h" +#include +using namespace CoincidenceSpectrum; + +namespace Ui { +class ParticleDataStatistics; +} + +class ParticleDataStatistics : public QWidget +{ + Q_OBJECT + +public: + explicit ParticleDataStatistics(QWidget *parent = nullptr); + ~ParticleDataStatistics(); + void setWidgetData(int board, int channel, int countNum, int secondNum); + + void setInitWidgetData(int board, int channel, int countNum, int secondNum); + + //处理板卡信息 + void handleBoard(std::vector eventData); + //设置数据 + void setCoincidenceEvent(const F2t9Order::CoincidenceEvent &CoincidenceEvent); + +private: + // 初始化映射表 + void InitUi(); + //统计每个板卡的初级粒子计数 + QMap statisticsFirstParticleQuantity(); + //处理初级粒子信息 + std::vector handleBasicSubordinate(std::vector dataList,int Board, int Channel); + +private slots: + //点击板卡探测器时触发的函数 区分初级粒子和次级粒子计数 + void _slotClickedBoard(QString board); + void _slotClickedTwoBoard(QString board); +signals: + void Signal_InitialState(); + void Signal_ClickedBoard(int board,int channel); +private: + Ui::ParticleDataStatistics *ui; + // 存储所有 DetectorStatusSummary 指针的二维数组(索引从0开始) + DetectorStatusSummary* m_widgetMap[MAX_BOARD][MAX_CHANNEL]; + //初级粒子板卡号 + int m_bd; + //初级粒子通道号 + int m_ch; +}; + +#endif // PARTICLEDATASTATISTICS_H diff --git a/src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.ui b/src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.ui new file mode 100644 index 0000000..717e84a --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.ui @@ -0,0 +1,253 @@ + + + ParticleDataStatistics + + + + 0 + 0 + 902 + 623 + + + + Form + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + background-color: rgb(244, 244, 244); + + + + + + + + DetectorStatusSummary + QWidget +
DetectorStatusSummary.h
+ 1 +
+
+ + +
diff --git a/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp b/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp new file mode 100644 index 0000000..e4fe691 --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp @@ -0,0 +1,521 @@ +#include "ThreeDDisplay.h" +#include "ui_ThreeDDisplay.h" + +ThreeDDisplay::ThreeDDisplay(QWidget *parent) : + QWidget(parent), + ui(new Ui::ThreeDDisplay) +{ + ui->setupUi(this); + _init3DSurface(); + // 初始化颜色渐变 + initColorGradient(); +} + +ThreeDDisplay::~ThreeDDisplay() +{ + delete ui; +} + +void ThreeDDisplay::_init3DSurface() +{ + // 连接按钮的信号槽 + connect(ui->pBtn_magnify, &QPushButton::clicked, this, &ThreeDDisplay::_zoomOut); + connect(ui->pBtn_reduce, &QPushButton::clicked, this, &ThreeDDisplay::_zoomIn); + connect(ui->pBtn_restore, &QPushButton::clicked, this, &ThreeDDisplay::_resetView); + // 创建3D曲面图 + m_surface = new Q3DSurface(); + m_surface->setAxisX(new QValue3DAxis); + m_surface->setAxisY(new QValue3DAxis); + m_surface->setAxisZ(new QValue3DAxis); + + // 设置场景和样式 + m_surface->activeTheme()->setType(Q3DTheme::ThemeQt); + m_surface->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); + m_surface->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricRight); + + // 创建数据代理和系列 + m_dataProxy = new QSurfaceDataProxy(); + m_series = new QSurface3DSeries(m_dataProxy); + //m_series->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe); + m_series->setDrawMode(QSurface3DSeries::DrawSurface);//无网格线 + m_series->setFlatShadingEnabled(true); + + // 设置颜色渐变 + QLinearGradient gradient; + gradient.setColorAt(0.0, Qt::blue); + gradient.setColorAt(0.25, Qt::cyan); + gradient.setColorAt(0.5, Qt::green); + gradient.setColorAt(0.75, Qt::yellow); + gradient.setColorAt(1.0, Qt::red); + m_series->setBaseGradient(gradient); + m_series->setColorStyle(Q3DTheme::ColorStyleRangeGradient); + + // 添加到图表 + m_surface->addSeries(m_series); + + // 创建容器widget + m_surfaceContainer = QWidget::createWindowContainer(m_surface, this); + //m_surfaceContainer->setMinimumSize(800, 600); + + // 添加到UI布局中(假设有一个QFrame或QVBoxLayout) + // 这里需要根据你的实际UI布局进行调整 + + ui->hLayout3D->addWidget(m_surfaceContainer); + ui->hLayout3D->setContentsMargins(0, 0, 0, 0); + + // 设置轴标签和标题 + m_surface->axisX()->setTitle("初级粒子能量 (MeV)"); + m_surface->axisY()->setTitle("符合事件计数"); + m_surface->axisZ()->setTitle("次级粒子能量和 (MeV)"); + + // 创建自定义主题(或者修改当前主题) + Q3DTheme *customTheme = m_surface->activeTheme(); + customTheme->setType(Q3DTheme::ThemeQt); // 使用Qt主题为基础 + + // 禁用标签边框和背景 + customTheme->setLabelBorderEnabled(false); // 禁用标签边框 + customTheme->setLabelBackgroundEnabled(false); // 禁用标签背景 + + // 设置坐标轴样式 + // 保留网格线(如果需要的话),如果不想要网格线可以设为透明 + // 如果希望保留淡化的网格线参考 + customTheme->setGridLineColor(QColor(200, 200, 200, 50)); // 半透明灰色网格线 + + // 设置坐标轴标签颜色(确保标签可见) + customTheme->setLabelTextColor(Qt::black); + customTheme->setLabelBackgroundColor(Qt::transparent); // 透明背景 + + // 应用自定义主题 + m_surface->setActiveTheme(customTheme); + + // 也可以直接对坐标轴进行设置 + // 设置坐标轴标签格式(可选) + m_surface->axisX()->setLabelFormat("%.1f"); + m_surface->axisY()->setLabelFormat("%.0f"); + m_surface->axisZ()->setLabelFormat("%.1f"); + + // 调整标签位置和显示 + m_surface->axisX()->setLabelAutoRotation(30); // 标签适当旋转 + m_surface->axisY()->setLabelAutoRotation(30); + m_surface->axisZ()->setLabelAutoRotation(30); + + + // 设置标题 + m_surface->setTitle("符合事件能量分布曲面图"); + //m_surface->setTitleFont(QFont("Arial", 20, QFont::Bold)); + // 启用鼠标交互(默认已启用,这里确保设置) + + m_surface->setSelectionMode(QAbstract3DGraph::SelectionNone); // 禁用选择模式,避免干扰 + m_surface->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricRight);// 设置初始相机位置 + _saveInitialCameraState(); + // 添加注释 + _addAnnotations(); +} + +void ThreeDDisplay::_addAnnotations() +{ + // 添加注释到曲面图 + QCustom3DLabel* titleLabel = new QCustom3DLabel(); + titleLabel->setText("符合事件分析曲面图\nX: 初级粒子能量\nY: 符合事件计数\nZ: 次级粒子能量和"); + titleLabel->setFont(QFont("Arial", 12)); + titleLabel->setPosition(QVector3D(0.5f, 1.0f, 0.0f)); + titleLabel->setScaling(QVector3D(0.1f, 0.1f, 0.1f)); + m_surface->addCustomItem(titleLabel); +} + +void ThreeDDisplay::_updateSurfaceData() +{ + if (m_surfaceData.isEmpty()) return; + + // 准备数据数组 + const int ROWS = 600 / 2; // Y轴分段数 + const int COLS = 360 / 2; // X轴分段数 + + // 找出能量范围 + float minPrimary = std::numeric_limits::max(); + float maxPrimary = std::numeric_limits::min(); + float minSecondary = std::numeric_limits::max(); + float maxSecondary = std::numeric_limits::min(); + + for (const auto& point : m_surfaceData) + { + minPrimary = std::min(minPrimary, point.primaryEnergy); + maxPrimary = std::max(maxPrimary, point.primaryEnergy); + minSecondary = std::min(minSecondary, point.secondaryEnergySum); + maxSecondary = std::max(maxSecondary, point.secondaryEnergySum); + } + + // 创建数据矩阵 + QVector> dataMatrix(ROWS, QVector(COLS, 0)); + + // 填充数据矩阵(简单的二维直方图) + float primaryStep = (maxPrimary - minPrimary) / COLS; + float secondaryStep = (maxSecondary - minSecondary) / ROWS; + + for (const auto& point : m_surfaceData) + { + int col = std::min(COLS - 1, static_cast((point.primaryEnergy - minPrimary) / primaryStep)); + int row = std::min(ROWS - 1, static_cast((point.secondaryEnergySum - minSecondary) / secondaryStep)); + + if (row >= 0 && row < ROWS && col >= 0 && col < COLS) + { + dataMatrix[row][col] += point.count; + } + } + + // 创建曲面数据 + QSurfaceDataArray* dataArray = new QSurfaceDataArray; + dataArray->reserve(ROWS); + + for (int row = 0; row < ROWS; row++) + { + QSurfaceDataRow* newRow = new QSurfaceDataRow(COLS); + + float yPos = minSecondary + row * secondaryStep; + + for (int col = 0; col < COLS; col++) + { + float xPos = minPrimary + col * primaryStep; + float zVal = dataMatrix[row][col]; // 计数作为高度 + + (*newRow)[col].setPosition(QVector3D(xPos, zVal, yPos)); + } + + dataArray->append(newRow); + } + + // 更新代理数据 + m_dataProxy->resetArray(dataArray); + + // 设置轴范围 + //m_surface->axisX()->setRange(minPrimary, maxPrimary); + //m_surface->axisY()->setRange(0, getMaxCount(dataMatrix)); + //m_surface->axisZ()->setRange(minSecondary, maxSecondary); + if (flag) + { + qDebug()<< maxPrimary << maxSecondary; + m_surface->axisX()->setRange(0, maxPrimary); + m_surface->axisZ()->setRange(0, maxSecondary); + flag = false; + } + qDebug()<axisY()->setRange(0, getMaxCount(dataMatrix)); +} + +float ThreeDDisplay::getMaxCount(const QVector > &dataMatrix) +{ + return 0.0; +} + + + +void ThreeDDisplay::_saveInitialCameraState() +{ + Q3DCamera *camera = m_surface->scene()->activeCamera(); + m_initialCameraTarget = camera->target(); + m_initialCameraXRotation = camera->xRotation(); + m_initialCameraYRotation = camera->yRotation(); + m_initialCameraZoomLevel = camera->zoomLevel(); +} + +// 缩小 +void ThreeDDisplay::_zoomIn() +{ + Q3DCamera *camera = m_surface->scene()->activeCamera(); + float currentZoom = camera->zoomLevel(); + + // 减小zoomLevel可以放大视图 + // zoomLevel越小,视图越大 + float newZoom = currentZoom - 10.0f; + + // 限制最小缩放级别,避免过度放大 + if (newZoom >= 10.0f) + { + camera->setZoomLevel(newZoom); + } + else + { + camera->setZoomLevel(10.0f); + } +} +// 放大 +void ThreeDDisplay::_zoomOut() +{ + Q3DCamera *camera = m_surface->scene()->activeCamera(); + float currentZoom = camera->zoomLevel(); + + // 增加zoomLevel可以缩小视图 + // zoomLevel越大,视图越小 + float newZoom = currentZoom + 10.0f; + + // 限制最大缩放级别,避免过度缩小 + if (newZoom <= 500.0f) + { + camera->setZoomLevel(newZoom); + } + else + { + camera->setZoomLevel(500.0f); + } + +} +// 还原视图 +void ThreeDDisplay::_resetView() +{ + Q3DCamera *camera = m_surface->scene()->activeCamera(); + + // 恢复到初始的相机预设 + //camera->setCameraPreset(Q3DCamera::CameraPresetIsometricRight); + + //或者恢复到保存的初始状态 + camera->setTarget(m_initialCameraTarget); + camera->setXRotation(m_initialCameraXRotation); + camera->setYRotation(m_initialCameraYRotation); + camera->setZoomLevel(m_initialCameraZoomLevel); +} + +void ThreeDDisplay::setSurfaceData(const QVector &surfaceData) +{ + m_surfaceData = surfaceData; + _updateSurfaceData(); +} + +void ThreeDDisplay::setBasicParticle(double startValue, double endValue) +{ + ui->lineEdit_begin_start->setText(QString::number(startValue)); + ui->lineEdit_begin_end->setText(QString::number(endValue)); +} + +void ThreeDDisplay::setSecondParticle(double startValue, double endValue) +{ + ui->lineEdit_second_start->setText(QString::number(startValue)); + ui->lineEdit_second_end->setText(QString::number(endValue)); + +} + +void ThreeDDisplay::setComplyWithEvent(double value) +{ + ui->lineEdit_count->setText(QString::number(value)); +} + +/*--------------------设置颜色---------------------------*/ +// 初始化颜色渐变 +void ThreeDDisplay::initColorGradient() +{ + // 设置默认渐变类型 + m_gradientType = Gradient_Custom; + + // 初始化颜色范围 + m_colorRange.minValue = 0; + m_colorRange.maxValue = 100; + m_colorRange.range = 100.0; + + //// 初始化颜色 + //m_gradientColors.clear(); + //m_gradientColors << QColor("#0E508A") // 蓝色 + // << QColor("#D5A914") // 亮蓝 + // << QColor("#D57C14") // 青色 + // << QColor("#D52A3D"); // 青绿; + //// << QColor(0, 255, 0) // 绿色 + //// << QColor(128, 255, 0) // 黄绿 + //// << QColor(255, 255, 0) // 黄色 + //// << QColor(255, 192, 0) // 橙色 + //// << QColor(255, 128, 0) // 深橙 + //// << QColor(255, 0, 0); // 红色 +} + + +// 根据数值获取颜色 +QColor ThreeDDisplay::getColorForValue(int value) +{ + if (m_colorRange.range < 1e-6) { + return QColor("#0E508A"); // 默认蓝色 + } + + // 计算归一化比例 (0.0 - 1.0) + double ratio = static_cast(value - m_colorRange.minValue) / m_colorRange.range; + ratio = qBound(0.0, ratio, 1.0); + + // 根据渐变类型返回颜色 + switch (m_gradientType) { + case Gradient_HeatMap: + return getHeatMapColor(ratio); + case Gradient_BlueToRed: + return getBlueToRedColor(ratio); + case Gradient_GreenToRed: + return getGreenToRedColor(ratio); + case Gradient_TrafficLight: + return getTrafficLightColor(ratio); + case Gradient_Custom: + return getCustomColor(ratio); + default: + return getHeatMapColor(ratio); + } +} + +// 热力图颜色 +QColor ThreeDDisplay::getHeatMapColor(double ratio) +{ + if (m_gradientColors.isEmpty()) { + return QColor("#0E508A"); + } + + int colorCount = m_gradientColors.size(); + double colorStep = 1.0 / (colorCount - 1); + int colorIndex = static_cast(ratio / colorStep); + colorIndex = qMin(colorIndex, colorCount - 2); + + double colorRatio = (ratio - colorIndex * colorStep) / colorStep; + + const QColor& color1 = m_gradientColors[colorIndex]; + const QColor& color2 = m_gradientColors[colorIndex + 1]; + + return QColor( + color1.red() + (color2.red() - color1.red()) * colorRatio, + color1.green() + (color2.green() - color1.green()) * colorRatio, + color1.blue() + (color2.blue() - color1.blue()) * colorRatio + ); +} + +// 蓝到红渐变 +QColor ThreeDDisplay::getBlueToRedColor(double ratio) +{ + // 蓝(0,0,255) -> 紫(128,0,128) -> 红(255,0,0) + if (ratio < 0.5) { + double r = ratio / 0.5; + return QColor( + static_cast(128 * r), // R: 0->128 + 0, // G: 0 + 255 - static_cast(127 * r) // B: 255->128 + ); + } + else { + double r = (ratio - 0.5) / 0.5; + return QColor( + 128 + static_cast(127 * r), // R: 128->255 + 0, // G: 0 + 128 - static_cast(128 * r) // B: 128->0 + ); + } +} + +// 绿到红渐变 +QColor ThreeDDisplay::getGreenToRedColor(double ratio) +{ + // 绿(0,255,0) -> 黄(255,255,0) -> 红(255,0,0) + if (ratio < 0.5) { + double r = ratio / 0.5; + return QColor( + static_cast(255 * r), // R: 0->255 + 255, // G: 255 + 0 // B: 0 + ); + } + else { + double r = (ratio - 0.5) / 0.5; + return QColor( + 255, // R: 255 + 255 - static_cast(255 * r), // G: 255->0 + 0 // B: 0 + ); + } +} + +// 红黄绿三色 +QColor ThreeDDisplay::getTrafficLightColor(double ratio) +{ + if (ratio < 0.33) { + return QColor(0, 255, 0); // 绿色 + } + else if (ratio < 0.66) { + return QColor(255, 255, 0); // 黄色 + } + else { + return QColor(255, 0, 0); // 红色 + } +} +// 自定义 +QColor ThreeDDisplay::getCustomColor(double ratio) +{ + if (ratio < 0.05) + { + return QColor("#0E508A"); + } + else if (ratio < 0.10) + { + return QColor("#1A7ED6"); + } + else if (ratio < 0.15) + { + return QColor("#125997"); + } + else if (ratio < 0.20) + { + return QColor("#1B66A7"); + } + else if (ratio < 0.25) + { + return QColor("#4CA9F9"); + } + + else if (ratio < 0.30) + { + return QColor("#D5A914"); + } + else if (ratio < 0.35) + { + return QColor("#F4C422"); + } + else if (ratio < 0.40) + { + return QColor("#EBC236"); + } + else if (ratio < 0.45) + { + return QColor("#EFC32F"); + } + else if (ratio < 0.50) + { + return QColor("#FFF8D2"); + } + else if (ratio < 0.55) + { + return QColor("#D57C14"); + } + else if (ratio < 0.60) + { + return QColor("#EF9B39"); + } + else if (ratio < 0.65) + { + return QColor("#F09B38"); + } + else if (ratio < 0.70) + { + return QColor("#EF9831"); + } + else if (ratio < 0.75) + { + return QColor("#FFECD1"); + } + else if (ratio < 0.80) + { + return QColor("#D52A3D"); + } + else if (ratio < 0.85) + { + return QColor("#FF334C"); + } + else if (ratio < 0.90) + { + return QColor("#F55B5B");// + } + else if (ratio < 0.95) + { + return QColor("#E93E51"); + } + else + { + return QColor("#FFD4D7"); + } +} diff --git a/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.h b/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.h new file mode 100644 index 0000000..c0e0170 --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.h @@ -0,0 +1,112 @@ +#ifndef THREEDDISPLAY_H +#define THREEDDISPLAY_H + +#include +#include +#include + +#include "CoincidenceSpectrumProcess.h" +using namespace QtDataVisualization; + +//// 用于存储三维数据 +//typedef struct SurfacePoint { +// float primaryEnergy; // 初级粒子能量 +// float secondaryEnergySum; // 次级粒子能量和 +// int count; // 符合事件计数 +//}SURFACEPOINT; + +namespace Ui { +class ThreeDDisplay; +} + +class ThreeDDisplay : public QWidget +{ + Q_OBJECT + +public: + explicit ThreeDDisplay(QWidget *parent = nullptr); + ~ThreeDDisplay(); + void setSurfaceData(const QVector &surfaceData); + + //设置初级粒子范围 + void setBasicParticle(double startValue,double endValue); + //设置次级粒子范围 + void setSecondParticle(double startValue,double endValue); + //设置符合事件计数 + void setComplyWithEvent(double value); + //设置曲面图数据 +private: + void _init3DSurface(); + void _addAnnotations(); + void _updateSurfaceData(); + float getMaxCount(const QVector>& dataMatrix); + // 保存初始相机状态 + void _saveInitialCameraState(); + // 缩小 + void _zoomIn(); + // 放大 + void _zoomOut(); + // 还原视图 + void _resetView(); + + void initColorGradient(); + // 颜色渐变管理 + void updateWidgetColors(QString board); // 更新所有widget的颜色 + QColor getColorForValue(int value); // 根据数值获取颜色 + void updateColorRange(); // 更新颜色范围 + + // 颜色获取函数(不同渐变方案) + QColor getHeatMapColor(double ratio); // 热力图颜色 + QColor getBlueToRedColor(double ratio); // 蓝到红渐变 + QColor getGreenToRedColor(double ratio); // 绿到红渐变 + QColor getTrafficLightColor(double ratio); // 红黄绿三色 + QColor getCustomColor(double ratio); // 自定义 + + // 高亮管理 + void highlightSelectedDetector(const QString& selectedName = ""); + + +private: + Ui::ThreeDDisplay *ui; + + Q3DSurface* m_surface = nullptr; + QSurfaceDataProxy* m_dataProxy = nullptr; + QSurface3DSeries* m_series = nullptr; + QWidget* m_surfaceContainer = nullptr; + // 用于保存初始相机状态 + QVector3D m_initialCameraTarget; + float m_initialCameraXRotation; + float m_initialCameraYRotation; + float m_initialCameraZoomLevel; + bool flag = true; + + QVector m_surfaceData; + + + // 颜色渐变相关 + struct ColorRange { + int minValue; + int maxValue; + double range; + }; + + ColorRange m_colorRange; + bool m_useGradient = true; + + // 渐变颜色配置 + enum GradientType { + Gradient_HeatMap, // 热力图(蓝->青->绿->黄->红) + Gradient_BlueToRed, // 蓝到红 + Gradient_GreenToRed, // 绿到红 + Gradient_TrafficLight, // 红黄绿 + Gradient_Custom //自定义 + }; + + GradientType m_gradientType = Gradient_Custom; + QVector m_gradientColors; + + // 颜色图例 + QLabel* m_colorLegendLabel = nullptr; +}; + +#endif // THREEDDISPLAY_H diff --git a/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.ui b/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.ui new file mode 100644 index 0000000..430e483 --- /dev/null +++ b/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.ui @@ -0,0 +1,333 @@ + + + ThreeDDisplay + + + + 0 + 0 + 443 + 729 + + + + Form + + + + + + 10 + + + + + + + + + + 还原 + + + + + + + + + + 放大 + + + + + + + + + + 缩小 + + + + + + + + + + 16777215 + 498 + + + + + + + + 0 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + + + + + + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 40 + + + + + QWidget#widget_6{ + border-bottom: 1px solid #1763a2; +} + + + + 20 + + + 20 + + + + + + + + 选择的符合事件相关信息展示: + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 20 + + + + + + + + 初级粒子能量范围: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + ~ + + + + + + + + + + keV + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 20 + + + + + + + + 次级粒子能量范围: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + ~ + + + + + + + + + + keV + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 20 + + + + + + 0 + 0 + + + + + + + 符合事件计数: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Horizontal + + + + 120 + 20 + + + + + + + + + + + + + + + +