diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 14e4128..06ebef4 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -27,6 +27,7 @@ #include "MeasureAnalysisTreeView.h" #include "GlobalDefine.h" #include "DeviceParameterConfig.h" +#include "NuclideLib.h" using namespace ads; MainWindow* MainWindow::_s_main_win = nullptr; @@ -372,3 +373,9 @@ void MainWindow::closeEvent(QCloseEvent* event) } QMainWindow::closeEvent(event); } + +void MainWindow::on_action_nuclideLib_triggered() +{ + NuclideLibManage *nuclidelib = new NuclideLibManage(); + nuclidelib->show(); +} diff --git a/src/MainWindow.h b/src/MainWindow.h index 043a76b..e7cbfd4 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -15,7 +15,6 @@ class CDockWidget; } class QPlainTextEdit; class MeasureAnalysisTreeView; - QT_END_NAMESPACE class MainWindow : public QMainWindow @@ -56,6 +55,9 @@ protected: virtual void showEvent(QShowEvent* event) override; virtual void closeEvent(QCloseEvent* event) override; +private slots: + void on_action_nuclideLib_triggered(); + private: QMutex _mutex_info_output; QPlainTextEdit* _plain_edit_info_output; diff --git a/src/NuclideLib/NuclideLib.cpp b/src/NuclideLib/NuclideLib.cpp new file mode 100644 index 0000000..8318a2d --- /dev/null +++ b/src/NuclideLib/NuclideLib.cpp @@ -0,0 +1,181 @@ +#include "NuclideLib.h" +#include "ui_NuclideLib.h" +#include "sqlitemanager.h" +#include +#include +#include +#include +#include +#include + +ButtonDelegate::ButtonDelegate(QObject *parent) : QStyledItemDelegate(parent) {} + +void ButtonDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (index.column() == 7) { + QStyleOptionButton button; + button.rect = option.rect; + button.text = "核素发射射线信息"; + button.state = QStyle::State_Enabled; + if (option.state & QStyle::State_MouseOver) + button.state |= QStyle::State_MouseOver; + QApplication::style()->drawControl(QStyle::CE_PushButton, &button, painter); + } else { + QStyledItemDelegate::paint(painter, option, index); + } +} + +bool ButtonDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) +{ + if (index.column() == 7 && event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast(event); + QRect buttonRect = option.rect; + if (buttonRect.contains(mouseEvent->pos())) { + emit buttonClicked(index); + return true; + } + } + return QStyledItemDelegate::editorEvent(event, model, option, index); +} + +NuclideLibManage::NuclideLibManage(QWidget *parent) : + QWidget(parent), + ui(new Ui::NuclideLibManage) +{ + ui->setupUi(this); + + m_model = new QStandardItemModel(0, 8, this); + m_model->setHorizontalHeaderLabels({"ID", "序号", "核素名称", "半衰期", "半衰期不确定度", "母体核素名称", "子体核素名称", "操作"}); + ui->tableView->setModel(m_model); + + ui->tableView->setColumnHidden(0, true); + + ui->tableView->setColumnWidth(1, 90); // 序号 + ui->tableView->setColumnWidth(7, 280); // 操作列 + ui->tableView->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); + ui->tableView->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch); + ui->tableView->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Stretch); + ui->tableView->horizontalHeader()->setSectionResizeMode(5, QHeaderView::Stretch); + ui->tableView->horizontalHeader()->setSectionResizeMode(6, QHeaderView::Stretch); + + ui->tableView->setAlternatingRowColors(true); + ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tableView->setFocusPolicy(Qt::NoFocus); + + // 设置委托 + m_delegate = new ButtonDelegate(this); + ui->tableView->setItemDelegateForColumn(7, m_delegate); + connect(m_delegate, &ButtonDelegate::buttonClicked, this, &NuclideLibManage::onButtonClicked); +} + +NuclideLibManage::~NuclideLibManage() +{ + delete ui; +} + +void NuclideLibManage::loadNuclideData() +{ + QString sql = "SELECT ID, NAME, HALF_LIFE, HALF_LIFE_UNC, PARENT_NUCLIDE, CHILD_NUCLIDE FROM tbl_nuclide_library;"; + auto rows = SqliteManager::instance().selectRows(sql); + m_listNuclide.clear(); + m_model->setRowCount(0); + + int row = 0; + for (const auto& rec : rows) { + QStringList item; + item << rec["ID"].toString() + << rec["NAME"].toString() + << rec["HALF_LIFE"].toString() + << rec["HALF_LIFE_UNC"].toString() + << rec["PARENT_NUCLIDE"].toString() + << rec["CHILD_NUCLIDE"].toString(); + m_listNuclide.append(item); + + // 插入模型行 + QList rowItems; + // 第0列:ID(隐藏) + QStandardItem *idItem = new QStandardItem(item[0]); + idItem->setTextAlignment(Qt::AlignCenter); + idItem->setFlags(idItem->flags() & ~Qt::ItemIsEditable); + rowItems << idItem; + + // 第1列:序号(行号+1) + QStandardItem *seqItem = new QStandardItem(QString::number(row + 1)); + seqItem->setTextAlignment(Qt::AlignCenter); + seqItem->setFlags(seqItem->flags() & ~Qt::ItemIsEditable); + rowItems << seqItem; + + // 第2~6列:核素名称、半衰期等 + for (int col = 1; col <= 5; ++col) { // item的下标1~5对应NAME到CHILD_NUCLIDE + QStandardItem *dataItem = new QStandardItem(item[col]); + dataItem->setTextAlignment(Qt::AlignCenter); + dataItem->setFlags(dataItem->flags() & ~Qt::ItemIsEditable); + rowItems << dataItem; + } + + // 第7列:操作列(存储空字符串,委托负责绘制按钮) + QStandardItem *opItem = new QStandardItem(""); + opItem->setFlags(opItem->flags() & ~Qt::ItemIsEditable); + rowItems << opItem; + + m_model->appendRow(rowItems); + ++row; + } +} + +QStringList NuclideLibManage::getNuclideData(const QString &id) +{ + for (const auto& item : m_listNuclide) { + if (item.at(0) == id) + return item; + } + return QStringList(); +} + +void NuclideLibManage::on_pushButton_add_clicked() +{ + loadNuclideData(); // 刷新数据 +} + +void NuclideLibManage::on_pushButton_edit_clicked() +{ + QModelIndexList selected = ui->tableView->selectionModel()->selectedRows(); + if (selected.isEmpty()) { + QMessageBox::information(this, "提示", "请先选择要修改的核素行!"); + return; + } + int row = selected.first().row(); + QString id = m_model->index(row, 0).data().toString(); // 隐藏的ID列 + QStringList data = getNuclideData(id); + if (data.isEmpty()) return; +} + +void NuclideLibManage::on_pushButton_del_clicked() +{ + QModelIndexList selected = ui->tableView->selectionModel()->selectedRows(); + if (selected.isEmpty()) { + QMessageBox::information(this, "提示", "请先选择要删除的核素行!"); + return; + } + int row = selected.first().row(); + QString id = m_model->index(row, 0).data().toString(); + + QMessageBox box(QMessageBox::Question, "提示", "确定删除该核素吗?", + QMessageBox::Yes | QMessageBox::No, this); + box.button(QMessageBox::Yes)->setText("确认"); + box.button(QMessageBox::No)->setText("取消"); + if (box.exec() != QMessageBox::Yes) return; + + bool ok = SqliteManager::instance().deleteRow("tbl_nuclide_library", "ID = ?", {id}); + if (ok) { + loadNuclideData(); + } else { + QMessageBox::warning(this, "错误", "删除失败:" + SqliteManager::instance().lastError()); + } +} + +void NuclideLibManage::onButtonClicked(const QModelIndex &index) +{ + int row = index.row(); + QString nuclideId = m_model->index(row, 0).data().toString(); +} diff --git a/src/NuclideLib/NuclideLib.h b/src/NuclideLib/NuclideLib.h new file mode 100644 index 0000000..7df3fb8 --- /dev/null +++ b/src/NuclideLib/NuclideLib.h @@ -0,0 +1,50 @@ +#ifndef NUCLIDELIBMANAGE_H +#define NUCLIDELIBMANAGE_H + +#include +#include +#include +#include +#include + +namespace Ui { +class NuclideLibManage; +} + +class ButtonDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit ButtonDelegate(QObject *parent = nullptr); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; + +signals: + void buttonClicked(const QModelIndex &index) const; +}; + +class NuclideLibManage : public QWidget +{ + Q_OBJECT + +public: + explicit NuclideLibManage(QWidget *parent = nullptr); + ~NuclideLibManage(); + +private slots: + void on_pushButton_add_clicked(); + void on_pushButton_edit_clicked(); + void on_pushButton_del_clicked(); + void onButtonClicked(const QModelIndex &index); // 响应委托按钮点击 + +private: + void loadNuclideData(); + QStringList getNuclideData(const QString &id); + + Ui::NuclideLibManage *ui; + QStandardItemModel *m_model; + ButtonDelegate *m_delegate; + QVector m_listNuclide; // 缓存原始核素数据(ID + 各字段) +}; + +#endif // NUCLIDELIBMANAGE_H diff --git a/src/NuclideLib/NuclideLib.ui b/src/NuclideLib/NuclideLib.ui new file mode 100644 index 0000000..dcae1e1 --- /dev/null +++ b/src/NuclideLib/NuclideLib.ui @@ -0,0 +1,63 @@ + + + NuclideLibManage + + + + 0 + 0 + 944 + 593 + + + + NuclideLibManage + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 新增 + + + + + + + 修改 + + + + + + + 删除 + + + + + + + + + + + + + + diff --git a/src/NuclideLib/sqlitemanager.cpp b/src/NuclideLib/sqlitemanager.cpp new file mode 100644 index 0000000..6890506 --- /dev/null +++ b/src/NuclideLib/sqlitemanager.cpp @@ -0,0 +1,258 @@ +#include "sqlitemanager.h" +#include +#include +#include + +SqliteManager::~SqliteManager() +{ + closeDatabase(); +} + +SqliteManager& SqliteManager::instance() +{ + static SqliteManager instance; + return instance; +} + +bool SqliteManager::openDatabase(const QString& dbPath, const QString& connectionName) +{ + // 如果已存在同名连接,先移除(避免重复创建) + if (QSqlDatabase::contains(connectionName)) { + m_db = QSqlDatabase::database(connectionName); + if (m_db.isOpen()) { + return true; // 已打开且可用 + } + QSqlDatabase::removeDatabase(connectionName); + } + + m_db = QSqlDatabase::addDatabase("QSQLITE", connectionName); + m_db.setDatabaseName(dbPath); + + if (!m_db.open()) { + m_lastError = m_db.lastError().text(); + qWarning() << "Failed to open database:" << m_lastError; + return false; + } + + // 启用外键约束(SQLite 默认关闭) + executeNonQuery("PRAGMA foreign_keys = ON;"); + return true; +} + +void SqliteManager::closeDatabase() +{ + if (m_db.isOpen()) { + m_db.close(); + } +} + +bool SqliteManager::executeNonQuery(const QString& sql, const QVector& bindValues) +{ + if (!m_db.isOpen()) { + m_lastError = "Database not open"; + return false; + } + + QSqlQuery query(m_db); + if (!query.prepare(sql)) { + m_lastError = query.lastError().text(); + qWarning() << "Prepare failed:" << m_lastError << "\nSQL:" << sql; + return false; + } + + for (int i = 0; i < bindValues.size(); ++i) { + query.bindValue(i, bindValues[i]); + } + + if (!query.exec()) { + m_lastError = query.lastError().text(); + qWarning() << "Execution failed:" << m_lastError << "\nSQL:" << sql; + return false; + } + + return true; +} + +QSqlQuery SqliteManager::executeQuery(const QString& sql, const QVector& bindValues) +{ + QSqlQuery query(m_db); + if (!m_db.isOpen()) { + m_lastError = "Database not open"; + return query; // 无效的 query + } + + if (!query.prepare(sql)) { + m_lastError = query.lastError().text(); + qWarning() << "Prepare failed:" << m_lastError << "\nSQL:" << sql; + return query; + } + + for (int i = 0; i < bindValues.size(); ++i) { + query.bindValue(i, bindValues[i]); + } + + if (!query.exec()) { + m_lastError = query.lastError().text(); + qWarning() << "Query execution failed:" << m_lastError << "\nSQL:" << sql; + } + + return query; // 即使失败也返回,调用者需检查 query.isActive() +} + +QVector> SqliteManager::selectRows(const QString& sql, const QVector& bindValues) +{ + QVector> results; + QSqlQuery query = executeQuery(sql, bindValues); + if (!query.isActive()) { + return results; + } + + while (query.next()) { + QMap row; + QSqlRecord record = query.record(); + for (int i = 0; i < record.count(); ++i) { + row[record.fieldName(i)] = query.value(i); + } + results.append(row); + } + return results; +} + +qint64 SqliteManager::insertRow(const QString& tableName, const QMap& fieldValues) +{ + if (fieldValues.isEmpty()) { + m_lastError = "No fields to insert"; + return -1; + } + + QStringList fields = fieldValues.keys(); + QStringList placeholders; + QVector bindValues; + + for (const QString& field : fields) { + placeholders.append("?"); + bindValues.append(fieldValues[field]); + } + + QString sql = QString("INSERT INTO %1 (%2) VALUES (%3);") + .arg(tableName) + .arg(fields.join(", ")) + .arg(placeholders.join(", ")); + + if (!executeNonQuery(sql, bindValues)) { + return -1; + } + + // 获取最后插入的行ID(SQLite 自增主键) + QSqlQuery query(m_db); + if (query.exec("SELECT last_insert_rowid();") && query.next()) { + return query.value(0).toLongLong(); + } + return -1; +} + +int SqliteManager::updateRow(const QString& tableName, const QMap& fieldValues, + const QString& whereClause, const QVector& whereValues) +{ + if (fieldValues.isEmpty()) { + m_lastError = "No fields to update"; + return -1; + } + + QStringList setClauses; + QVector bindValues; + + for (auto it = fieldValues.begin(); it != fieldValues.end(); ++it) { + setClauses.append(it.key() + " = ?"); + bindValues.append(it.value()); + } + + bindValues.append(whereValues); + + QString sql = QString("UPDATE %1 SET %2").arg(tableName).arg(setClauses.join(", ")); + if (!whereClause.isEmpty()) { + sql += " WHERE " + whereClause; + } + sql += ";"; + + if (!executeNonQuery(sql, bindValues)) { + return -1; + } + + QSqlQuery query(m_db); + if (query.exec("SELECT changes();") && query.next()) { + return query.value(0).toInt(); + } + return 0; +} + +int SqliteManager::deleteRow(const QString& tableName, const QString& whereClause, const QVector& whereValues) +{ + QString sql = QString("DELETE FROM %1").arg(tableName); + if (!whereClause.isEmpty()) { + sql += " WHERE " + whereClause; + } + sql += ";"; + + if (!executeNonQuery(sql, whereValues)) { + return -1; + } + + QSqlQuery query(m_db); + if (query.exec("SELECT changes();") && query.next()) { + return query.value(0).toInt(); + } + return 0; +} + +bool SqliteManager::beginTransaction() +{ + if (!m_db.isOpen()) { + m_lastError = "Database not open"; + return false; + } + if (!m_db.transaction()) { + m_lastError = m_db.lastError().text(); + qWarning() << "Begin transaction failed:" << m_lastError; + return false; + } + return true; +} + +bool SqliteManager::commitTransaction() +{ + if (!m_db.isOpen()) { + m_lastError = "Database not open"; + return false; + } + if (!m_db.commit()) { + m_lastError = m_db.lastError().text(); + qWarning() << "Commit transaction failed:" << m_lastError; + return false; + } + return true; +} + +bool SqliteManager::rollbackTransaction() +{ + if (!m_db.isOpen()) { + m_lastError = "Database not open"; + return false; + } + if (!m_db.rollback()) { + m_lastError = m_db.lastError().text(); + qWarning() << "Rollback transaction failed:" << m_lastError; + return false; + } + return true; +} + +QString SqliteManager::lastError() const +{ + return m_lastError; +} + +bool SqliteManager::isOpen() const +{ + return m_db.isOpen(); +} diff --git a/src/NuclideLib/sqlitemanager.h b/src/NuclideLib/sqlitemanager.h new file mode 100644 index 0000000..12e296c --- /dev/null +++ b/src/NuclideLib/sqlitemanager.h @@ -0,0 +1,61 @@ +#ifndef SQLITEMANAGER_H +#define SQLITEMANAGER_H + +#include +#include +#include +#include +#include + +class SqliteManager +{ +public: + // 获取单例实例 + static SqliteManager& instance(); + + // 禁止拷贝和赋值 + SqliteManager(const SqliteManager&) = delete; + SqliteManager& operator=(const SqliteManager&) = delete; + + // 打开数据库,默认创建连接名 "sqlite_conn" + bool openDatabase(const QString& dbPath, const QString& connectionName = "sqlite_conn"); + void closeDatabase(); + + // 执行无结果的 SQL(INSERT, UPDATE, DELETE, CREATE TABLE 等) + bool executeNonQuery(const QString& sql, const QVector& bindValues = {}); + + // 执行查询,返回 QSqlQuery(需自行遍历) + QSqlQuery executeQuery(const QString& sql, const QVector& bindValues = {}); + + // 便捷查询:返回所有行(每行为字段名到值的映射) + QVector> selectRows(const QString& sql, const QVector& bindValues = {}); + + // 插入一行并返回最后插入的行ID(适用于自增主键) + qint64 insertRow(const QString& tableName, const QMap& fieldValues); + + // 更新行,返回影响的行数 + int updateRow(const QString& tableName, const QMap& fieldValues, const QString& whereClause, const QVector& whereValues = {}); + + // 删除行,返回影响的行数 + int deleteRow(const QString& tableName, const QString& whereClause, const QVector& whereValues = {}); + + // 事务控制 + bool beginTransaction(); + bool commitTransaction(); + bool rollbackTransaction(); + + // 获取最后一次错误信息 + QString lastError() const; + + // 检查数据库是否已打开 + bool isOpen() const; + +private: + SqliteManager() = default; + ~SqliteManager(); + + QSqlDatabase m_db; + QString m_lastError; +}; + +#endif // SQLITEMANAGER_H diff --git a/src/src.pro b/src/src.pro index 1d04ea3..184b076 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,6 +1,6 @@ TARGET = EnergySpectrumAnalyer -QT += core gui widgets concurrent datavisualization network +QT += core gui widgets concurrent datavisualization network sql CONFIG += c++17 release msvc { @@ -37,7 +37,8 @@ INCLUDEPATH += \ $${PWD}/MeasureDeviceParamsConfigView \ $${PWD}/DeviceParameterConfig \ $${PWD}/2DSpectralCompliance \ - $${PWD}/ConformToTheEnergySpectrum + $${PWD}/ConformToTheEnergySpectrum \ + $${PWD}/NuclideLib DEPENDPATH += \ @@ -55,7 +56,9 @@ DEPENDPATH += \ $${PWD}/MeasureDeviceParamsConfigView \ $${PWD}/DeviceParameterConfig \ $${PWD}/2DSpectralCompliance \ - $${PWD}/ConformToTheEnergySpectrum + $${PWD}/ConformToTheEnergySpectrum \ + $${PWD}/NuclideLib + @@ -79,6 +82,7 @@ SOURCES += \ MeasureAnalysisHistoryForm/MeasureAnalysisHistoryForm.cpp \ MeasureDeviceParamsConfigView/DeviceParamsTableForm.cpp \ MeasureDeviceParamsConfigView/MeasureDeviceParamsConfigView.cpp \ + NuclideLib/sqlitemanager.cpp \ ParticleCountPlotView/BatchEnergyScaleDialog.cpp \ ParticleCountPlotView/FindPeaksResultDialog.cpp \ ParticleCountPlotView/ParticleCountPlotView.cpp \ @@ -100,6 +104,7 @@ SOURCES += \ EnergyCountPeakFitView/EnergyCountPeakFitView.cpp \ DeviceParameterConfig/DeviceParameterProxy.cpp \ ConformToTheEnergySpectrum/ConformToTheEnergySpectrum.cpp \ + NuclideLib/NuclideLib.cpp \ main.cpp HEADERS += \ @@ -123,6 +128,7 @@ HEADERS += \ MeasureAnalysisHistoryForm/MeasureAnalysisHistoryForm.h \ MeasureDeviceParamsConfigView/DeviceParamsTableForm.h \ MeasureDeviceParamsConfigView/MeasureDeviceParamsConfigView.h \ + NuclideLib/sqlitemanager.h \ ParticleCountPlotView/BatchEnergyScaleDialog.h \ ParticleCountPlotView/FindPeaksResultDialog.h \ ParticleCountPlotView/ParticleCountPlotView.h \ @@ -145,6 +151,7 @@ HEADERS += \ EnergyCountPeakFitView/EnergyCountPeakFitView.h \ DeviceParameterConfig/DeviceParameterProxy.h \ ConformToTheEnergySpectrum/ConformToTheEnergySpectrum.h \ + NuclideLib/NuclideLib.h @@ -163,7 +170,8 @@ FORMS += \ ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.ui \ ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.ui \ ThreeDimensionalConformityAnalysisView/ThreeDDisplay.ui \ - ThreeDimensionalConformityAnalysisView/ConformityAnalysis.ui + ThreeDimensionalConformityAnalysisView/ConformityAnalysis.ui \ + NuclideLib/NuclideLib.ui DEFINES += ENABLE_DEBUG