新增核素库管理界面,新增数据库操作类

This commit is contained in:
anxinglong 2026-04-14 18:05:38 +08:00
parent 7b6b967397
commit ceb970319e
8 changed files with 635 additions and 5 deletions

View File

@ -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();
}

View File

@ -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;

View File

@ -0,0 +1,181 @@
#include "NuclideLib.h"
#include "ui_NuclideLib.h"
#include "sqlitemanager.h"
#include <QMessageBox>
#include <QHeaderView>
#include <QPainter>
#include <QStyleOptionButton>
#include <QMouseEvent>
#include <QApplication>
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<QMouseEvent*>(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<QStandardItem*> 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();
}

View File

@ -0,0 +1,50 @@
#ifndef NUCLIDELIBMANAGE_H
#define NUCLIDELIBMANAGE_H
#include <QWidget>
#include <QMap>
#include <QVector>
#include <QStyledItemDelegate>
#include <QStandardItemModel>
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<QStringList> m_listNuclide; // 缓存原始核素数据ID + 各字段)
};
#endif // NUCLIDELIBMANAGE_H

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NuclideLibManage</class>
<widget class="QWidget" name="NuclideLibManage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>944</width>
<height>593</height>
</rect>
</property>
<property name="windowTitle">
<string>NuclideLibManage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_add">
<property name="text">
<string>新增</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_edit">
<property name="text">
<string>修改</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_del">
<property name="text">
<string>删除</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="tableView"/>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,258 @@
#include "sqlitemanager.h"
#include <QSqlError>
#include <QDebug>
#include <QSqlRecord>
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<QVariant>& 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<QVariant>& 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<QMap<QString, QVariant>> SqliteManager::selectRows(const QString& sql, const QVector<QVariant>& bindValues)
{
QVector<QMap<QString, QVariant>> results;
QSqlQuery query = executeQuery(sql, bindValues);
if (!query.isActive()) {
return results;
}
while (query.next()) {
QMap<QString, QVariant> 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<QString, QVariant>& fieldValues)
{
if (fieldValues.isEmpty()) {
m_lastError = "No fields to insert";
return -1;
}
QStringList fields = fieldValues.keys();
QStringList placeholders;
QVector<QVariant> 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;
}
// 获取最后插入的行IDSQLite 自增主键)
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<QString, QVariant>& fieldValues,
const QString& whereClause, const QVector<QVariant>& whereValues)
{
if (fieldValues.isEmpty()) {
m_lastError = "No fields to update";
return -1;
}
QStringList setClauses;
QVector<QVariant> 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<QVariant>& 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();
}

View File

@ -0,0 +1,61 @@
#ifndef SQLITEMANAGER_H
#define SQLITEMANAGER_H
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QVariant>
#include <QVector>
#include <QMap>
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();
// 执行无结果的 SQLINSERT, UPDATE, DELETE, CREATE TABLE 等)
bool executeNonQuery(const QString& sql, const QVector<QVariant>& bindValues = {});
// 执行查询,返回 QSqlQuery需自行遍历
QSqlQuery executeQuery(const QString& sql, const QVector<QVariant>& bindValues = {});
// 便捷查询:返回所有行(每行为字段名到值的映射)
QVector<QMap<QString, QVariant>> selectRows(const QString& sql, const QVector<QVariant>& bindValues = {});
// 插入一行并返回最后插入的行ID适用于自增主键
qint64 insertRow(const QString& tableName, const QMap<QString, QVariant>& fieldValues);
// 更新行,返回影响的行数
int updateRow(const QString& tableName, const QMap<QString, QVariant>& fieldValues, const QString& whereClause, const QVector<QVariant>& whereValues = {});
// 删除行,返回影响的行数
int deleteRow(const QString& tableName, const QString& whereClause, const QVector<QVariant>& 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

View File

@ -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