医工互联

 找回密码
 注册[Register]

手机动态码快速登录

手机号快速登录

微信登录

微信扫一扫,快速登录

QQ登录

只需一步,快速开始

查看: 387|回复: 0
收起左侧

【Dcmtk】Pacs-小型dicom数据库(sqlite)

[复制链接]

  离线 

发表于 2022-10-10 17:19:59 来自手机 | 显示全部楼层 |阅读模式 <
前言:
  要做一个简单的开源dcm浏览器 KISS Dicom Viewer ,小型pacs服务肯定必不可少。开发中到处找现成代码,基本上找到的资源都是一个发布版,很少有可以用来研究的源码。 KISS Dicom Viewer 目前处于开发阶段,最近几篇博客就用来记录下开发一个小型pacs数据库(Qt+Dcmtk)的过程。提供服务包括:通讯测试echo、远程查询findscu、远程下载get/move、本机存储storescp。
  Dicom协议、通讯原理等等,网上有很多优秀的中文博客来说明,这里就不介绍了。

  如果你需要定义自己的 dicom数据库,可以看下我整理的:



182005hq3v0jmm0o0n0c0u.gif

1. 数据库结构

  正常的完善的pacs系统的话一般是搞四张表,分别存储 PATIENT、STUDY、SERIES、IMAGE。因为我仅仅是开发一个小型的dcm浏览器,数据就建两张表 STUDY 和 IMAGE
182006ugwmowmazg5zfa5s.png

创建语句
  1. str = "CREATE TABLE IF NOT EXISTS StudyTable("
  2.           "StudyUid VARCHAR(128) PRIMARY KEY NOT NULL,"
  3.           "AccNumber VARCHAR(64) NOT NULL, PatientId VARCHAR(64) NOT NULL,"
  4.           "PatientName VARCHAR(64), "
  5.           "PatientSex VARCHAR(2) NOT NULL,"
  6.           "PatientBirth DATE NOT NULL,"
  7.           "PatientAge VARCHAR(6),"
  8.           "StudyTime DATETIME NOT NULL,"
  9.           "Modality VARCHAR(2) NOT NULL, "
  10.           "StudyDesc TEXT)";
  11. str = "CREATE TABLE IF NOT EXISTS ImageTable("
  12.           "ImageUid VARCHAR(128) PRIMARY KEY NOT NULL,"
  13.           "SopClassUid VARCHAR(128) NOT NULL,"
  14.           "SeriesUid VARCHAR(128) NOT NULL, "
  15.           "StudyUid VARCHAR(128) NOT NULL,"
  16.           "RefImageUid VARCHAR(128),"
  17.           "ImageNo VARCHAR(16), "
  18.           "ImageTime DATETIME NOT NULL,"
  19.           "ImageDesc TEXT,"
  20.           "ImageFile TEXT,"
  21.           "FOREIGN KEY(StudyUid) REFERENCES StudyTable(StudyUid))";
复制代码
2. 数据库可视化 QSqlTableModel+QTableView

  qt对于数据库的可视化封装基本上很完善了,sqlmode+tableview 可以快速实现数据库的可视化。只有两张表 STUDY 和 IMAGE,就是上边选中STUDY后下边的IMAGE会对应弹出该STUDY的IMAGE。
182006pyx66ygce8htjqh8.png

  1. class SqlImageModel : public QSqlTableModel {
  2.     Q_OBJECT
  3.   public:
  4.     enum ColumnType {
  5.         ImageUid,
  6.         SopClassUid,
  7.         SeriesUid,
  8.         StudyUid,
  9.         RefImageUid,
  10.         ImageNo,
  11.         ImageTime,
  12.         ImageDesc,
  13.         ImageFile,
  14.         ColumnCount,
  15.     };
  16.     explicit SqlImageModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase());
  17.     QVariant headerData(int section, Qt::Orientation orientation = Qt::Horizontal,
  18.                         int role = Qt::DisplayRole) const;
  19.     QStringList getAllImageFiles() const;
  20.   Q_SIGNALS:
  21.     void viewImages(const QStringList &imageFiles);
  22.     void Signal_RemoveFinished();
  23.   public Q_SLOTS:
  24.     bool select();
  25.   public Q_SLOTS:
  26.     void SLot_ViewImages(const QModelIndexList &indexes);
  27.     void SLot_ViewAllImages();
  28.     void Slot_RemoveImages(const QModelIndexList &indexes);
  29.     void Slot_RemoveAllImages();
  30.     void Slot_StudySelected(const QStringList &studyUids);
  31. };
复制代码
  1. class SqlStudyModel : public QSqlTableModel {
  2.     Q_OBJECT
  3.   public:
  4.     enum ColumnType {
  5.         StudyUid,
  6.         AccNumber,
  7.         PatientId,
  8.         PatientName,
  9.         PatientSex,
  10.         PatientBirth,
  11.         PatientAge,
  12.         StudyTime,
  13.         Modality,
  14.         StudyDesc,
  15. //        ColumnCount,
  16.     };
  17.     explicit SqlStudyModel(QObject *parent = nullptr,
  18.                            QSqlDatabase db = QSqlDatabase());
  19.     QVariant headerData(int section,
  20.                         Qt::Orientation orientation, int role = Qt::DisplayRole) const;
  21.     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
  22.     QString getFirstSelectedStudyUid() const;
  23.   public Q_SLOTS:
  24.     bool select();
  25.   Q_SIGNALS:
  26.     void Signal_studySelectionChanged(const QStringList &studyUids);
  27.     void Signal_NewStudy(const QSqlRecord &studyRec);
  28.     void Signal_NewImage(const QSqlRecord &studyRec);
  29.     void Signal_RemoveFinished();
  30.   public Q_SLOTS:
  31.     void Slot_SelectionChanged(const QModelIndexList &indexes);
  32.     void Slot_RemoveStudies();
  33.     void Slot_NewStudy(const QModelIndex &index);
  34.     void Slot_NewImage(const QModelIndex &index);
  35.   private:
  36.     QStringList selected_study_uids_;
  37.     StudyRecord *mod_study_;
  38.     int modify_row_;
  39. };
复制代码
  1. class SqlStudyTabView : public KissTabView {
  2.     Q_OBJECT
  3.   public:
  4.     explicit SqlStudyTabView(QAbstractTableModel *model, QWidget *parent = nullptr);
  5.     ~SqlStudyTabView() {}
  6.   Q_SIGNALS:
  7.     void Signal_ViewImages();
  8.     void Signal_RemoveStudies();
  9.     void Singal_StudySelectionChanged(const QModelIndexList &indexes);
  10.   protected slots:
  11.     void selectionChanged(const QItemSelection &selected,
  12.                           const QItemSelection &deselected);
  13.     void contextMenuEvent(QContextMenuEvent *e);
  14.   private:
  15.     void SetupContextMenu();
  16.     void HideColumns();
  17.   private:
  18.     QStringList study_uids_;
  19.     QAction *view_image_;
  20.     QAction *remove_study_;
  21. };
复制代码
  1. class SqlImageTabView : public KissTabView {
  2.     Q_OBJECT
  3.   public:
  4.     explicit SqlImageTabView(QAbstractTableModel *model, QWidget *parent = nullptr);
  5.     ~SqlImageTabView() {}
  6.   Q_SIGNALS:
  7.     void Signal_ViewImages(const QModelIndexList &indexes);
  8.     void Signal_RemoveImages(const QModelIndexList &indexes);
  9.   private:
  10.     void SetupContextMenu();
  11.     void HideColumns();
  12.   private:
  13.     QAction *view_image_action_;
  14.     QAction *remove_image_action_;
  15. };
复制代码
3. 数据库查询

  QSqlTableModel可以很快速便捷的实现模型的检索
182007gquicc5bjmfyybdb.png

  1. void StudyExplorerWidget::SetStudyFilter() {
  2.     QString filter, temp;
  3.     if (ui->fromCheckBox->isChecked()) {
  4.         filter = QString("StudyTime>\'%1\'").arg(
  5.                      ui->fromDateTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss"));
  6.     }
  7.     if (ui->toCheckBox->isChecked()) {
  8.         if (!filter.isEmpty()) {
  9.             filter.append(" and ");
  10.         }
  11.         filter.append(QString("StudyTime<\'%1\'").arg(
  12.                           ui->toDateTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss")));
  13.     }
  14.     if (!ui->modalityCombo->currentText().isEmpty()) {
  15.         if (!filter.isEmpty()) {
  16.             filter.append(" and ");
  17.         }
  18.         filter.append(QString("Modality=\'%1\'").arg(ui->modalityCombo->currentText()));
  19.     }
  20.     if (!ui->patientIDEdit->text().isEmpty()) {
  21.         temp = ui->patientIDEdit->text();
  22.         temp.replace(QChar('*'), QChar('%'));
  23.         temp.replace(QChar('?'), QChar('_'));
  24.         if (!filter.isEmpty()) {
  25.             filter.append(" and ");
  26.         }
  27.         filter.append(QString("PatientId LIKE \'%%1%\'").arg(temp));
  28.     }
  29.     if (!ui->patientNameEdit->text().isEmpty()) {
  30.         temp = ui->patientNameEdit->text();
  31.         temp.replace(QChar('*'), QChar('%'));
  32.         temp.replace(QChar('?'), QChar('_'));
  33.         if (!filter.isEmpty()) {
  34.             filter.append(" and ");
  35.         }
  36.         filter.append(QString("PatientName LIKE \'%%1%\'").arg(temp));
  37.     }
  38.     if (!ui->accNumberEdit->text().isEmpty()) {
  39.         temp = ui->accNumberEdit->text();
  40.         temp.replace(QChar('*'), QChar('%'));
  41.         temp.replace(QChar('?'), QChar('_'));
  42.         if (!filter.isEmpty()) {
  43.             filter.append(" and ");
  44.         }
  45.         filter.append(QString("AccNumber LIKE \'%%1%\'").arg(temp));
  46.     }
  47.     this->RefreshReadStudyModel(filter);
  48. }
  49. void StudyExplorerWidget::RefreshReadStudyModel(const QString &filter) {
  50.     bool close = false;
  51.     if(DbManager::IsOpenedDb()) {
  52.     } else {
  53.         if (DbManager::OpenDb()) {
  54.             close = true;
  55.         }
  56.     }
  57.     study_model_->setFilter(filter);
  58.     study_model_->select();
  59.     if(close) {
  60.         DbManager::CloseDb();
  61.     }
  62. }
复制代码
4. 数据库增删改查

  需求很简单,我这里使用sql语句实现。
  1. #ifndef STUDYDAO_H
  2. #define STUDYDAO_H
  3. #include "Db/dbmanager.h"
  4. class StudyRecord;
  5. class ImageRecord;
  6. class StudyDao : public QObject {
  7.     Q_OBJECT
  8.   public:
  9.     static const QString study_table_name_;
  10.     static const QString image_table_name_;
  11.   public:
  12.     explicit StudyDao(QObject *parent = nullptr);
  13.     virtual ~StudyDao() override;
  14.     bool InsertStudyToDb(const StudyRecord &study, bool imported = false);
  15.     bool RemoveStudyFromDb(const QString &study_uid);
  16.     bool VerifyStudyByStuid(const QString &study_uid);
  17.     //
  18.     bool InsertImageToDb(const ImageRecord &image, bool imported = false);
  19.     bool RemoveImageFromDb(const QString &image_uid, bool updateStudy = true);
  20.     bool RemoveAllImagesOfStudyFromDb(const QString &study_uid, bool updateStudy = true);
  21.     bool UpdateImageFile(const QString &image_uid, const QString &image_file);
  22.     bool VerifyImageByIMmuid(const QString &image_uid);
  23.   public:
  24.     static bool Initial();
  25.   private:
  26.     static bool CreateTable();
  27.     static bool CheckTable();
  28.   private:
  29. };
  30. #endif // STUDYDAO_H
复制代码
  1. #include "studydao.h"
  2. #include <Global/KissGlobal>
  3. const QString StudyDao::study_table_name_ = "StudyTable";
  4. const QString StudyDao::image_table_name_ = "ImageTable";
  5. StudyDao::StudyDao(QObject *parent):
  6.     QObject(parent) {
  7. }
  8. StudyDao::~StudyDao() {
  9. }
  10. bool StudyDao::InsertStudyToDb(const StudyRecord &study, bool imported) {
  11.     Q_UNUSED(imported)
  12.     bool success = false;
  13.     if(DbManager::OpenDb()) {
  14.         QMap<QString, QVariant> data;
  15.         data.insert("StudyUid", study.study_uid_);
  16.         data.insert("AccNumber", study.acc_number_);
  17.         data.insert("PatientId", study.patient_id_);
  18.         data.insert("PatientName", study.patient_name_);
  19.         data.insert("PatientSex", study.patient_sex_);
  20.         if(study.patient_birth_.toString("yyyy-MM-dd").isEmpty()) {
  21.             data.insert("PatientBirth", "");
  22.         } else {
  23.             data.insert("PatientBirth", study.patient_birth_.toString("yyyy-MM-dd"));
  24.         }
  25.         data.insert("PatientAge", study.patient_age_);
  26.         data.insert("StudyTime", study.study_time_.toString(NORMAL_DATETIME_FORMAT));
  27.         data.insert("Modality", study.modality_);
  28.         data.insert("StudyDesc", study.study_desc_);
  29.         if (DbManager::insert(study_table_name_, data)) {
  30.             success = true;
  31.         }
  32.     }
  33.     DbManager::CloseDb();
  34.     return success;
  35. }
  36. bool StudyDao::RemoveStudyFromDb(const QString &study_uid) {
  37.     bool success = false;
  38.     if (study_uid.isEmpty()) {
  39.         return false;
  40.     }
  41.     if(DbManager::OpenDb()) {
  42.         QString where = QString("StudyUid = '%1'").arg(study_uid);
  43.         if (DbManager::remove(study_table_name_, where)) {
  44.             success = true;
  45.         }
  46.     }
  47.     DbManager::CloseDb();
  48.     this->RemoveAllImagesOfStudyFromDb(study_uid, false);
  49.     return success;
  50. }
  51. /**
  52. * @brief StudyDao::VerifyStudyByStuid
  53. * @param study_uid
  54. * @return
  55. */
  56. bool StudyDao::VerifyStudyByStuid(const QString &study_uid) {
  57.     bool success = false;
  58.     if (study_uid.isEmpty()) {
  59.         return false;
  60.     }
  61.     if(DbManager::OpenDb()) {
  62.         QStringList key_list;
  63.         key_list.append("StudyUid");
  64.         QString where = QString("StudyUid = '%1'").arg(study_uid);
  65.         QList<QMap<QString, QVariant>> res;
  66.         if (DbManager::select(study_table_name_, key_list, res, where)) {
  67.             if (res.size() == 1) {
  68.                 success = true;
  69.             }
  70.         }
  71.     }
  72.     DbManager::CloseDb();
  73.     return success;
  74. }
  75. bool StudyDao::InsertImageToDb(const ImageRecord &image, bool imported) {
  76.     Q_UNUSED(imported)
  77.     bool success = false;
  78.     if(DbManager::OpenDb()) {
  79.         QMap<QString, QVariant> data;
  80.         data.insert("ImageUid", image.image_uid_);
  81.         data.insert("SopClassUid", image.sop_class_uid_);
  82.         data.insert("SeriesUid", image.series_uid_);
  83.         data.insert("StudyUid", image.study_uid_);
  84.         data.insert("RefImageUid", image.ref_image_uid_);
  85.         data.insert("ImageNo", image.image_number_);
  86.         data.insert("ImageTime", image.image_yime_.toString(NORMAL_DATETIME_FORMAT));
  87.         data.insert("ImageDesc", image.image_desc_);
  88.         data.insert("ImageFile", image.image_file_);
  89.         if (DbManager::insert(image_table_name_, data)) {
  90.             success = true;
  91.         }
  92.     }
  93.     DbManager::CloseDb();
  94.     return success;
  95. }
  96. bool StudyDao::RemoveImageFromDb(const QString &image_uid, bool updateStudy) {
  97.     Q_UNUSED(updateStudy)
  98.     bool success = false;
  99.     // select data && Remove file
  100.     if (image_uid.isEmpty()) {
  101.         return false;
  102.     }
  103.     if (DbManager::OpenDb()) {
  104.         QStringList key_list;
  105.         key_list.append("ImageFile");
  106.         QString where = QString("ImageUid = '%1'").arg(image_uid);
  107.         QList<QMap<QString, QVariant>> res;
  108.         if (DbManager::select(image_table_name_, key_list, res, where)) {
  109.             if (res.size() == 1) {
  110.                 const QMap<QString, QVariant> &res0 = res.at(0);
  111.                 if (res0.size() == 1) {
  112.                     QString image_file = res0.value("ImageFile").toString();
  113.                     QString file = QString("./DcmFile/%2").arg(image_file);
  114.                     // QString dir_name = file.left(file.lastIndexOf('/'));
  115.                     FileUtil::DeleteFileOrFolder(file);
  116.                     success = true;
  117.                 } else {
  118.                 }
  119.             } else {
  120.             }
  121.         }
  122.     }
  123.     DbManager::CloseDb();
  124.     // remove data
  125.     if(DbManager::OpenDb()) {
  126.         QString where = QString("ImageUid = '%1'").arg(image_uid);
  127.         if (DbManager::remove(image_table_name_, where)) {
  128.             success = true;
  129.         }
  130.     }
  131.     DbManager::CloseDb();
  132.     return success;
  133. }
  134. bool StudyDao::RemoveAllImagesOfStudyFromDb(
  135.     const QString &study_uid, bool updateStudy) {
  136.     Q_UNUSED(updateStudy)
  137.     if (study_uid.isEmpty()) {
  138.         return false;
  139.     }
  140.     bool result = false;
  141.     //
  142.     QStringList image_uids;
  143.     // select data
  144.     if (DbManager::OpenDb()) {
  145.         QStringList key_list;
  146.         key_list.append("ImageUid");
  147.         QString where = QString("StudyUid = '%1'").arg(study_uid);
  148.         QList<QMap<QString, QVariant>> res;
  149.         if (DbManager::select(image_table_name_, key_list, res, where)) {
  150.             if (res.size() >= 1) {
  151.                 for (int i = 0; i < res.size(); i++) {
  152.                     const QMap<QString, QVariant> &res0 = res.at(i);
  153.                     if (res0.size() == 1) {
  154.                         image_uids << res0.value("ImageUid").toString();
  155.                     }
  156.                 }
  157.             }
  158.         }
  159.     }
  160.     DbManager::CloseDb();
  161.     // remove data
  162.     foreach (auto var, image_uids) {
  163.         RemoveImageFromDb(var);
  164.     }
  165.     return result;
  166. }
  167. bool StudyDao::UpdateImageFile(const QString &image_uid, const QString &image_file) {
  168.     if (image_uid.isEmpty()) {
  169.         return false;
  170.     }
  171.     if (image_file.isEmpty()) {
  172.         return false;
  173.     }
  174.     bool result = false;
  175.     // Create StudyTable
  176.     QString str ;
  177.     str = "UPDATE ImageTable SET ImageFile=%1 WHERE ImageUid=%2";
  178.     str = str.arg(image_uid, image_file);
  179.     result = DbManager::ExecSqlStr(str);
  180.     return result;
  181. }
  182. bool StudyDao::VerifyImageByIMmuid(const QString &image_uid) {
  183.     bool success = false;
  184.     if (image_uid.isEmpty()) {
  185.         return false;
  186.     }
  187.     if(DbManager::OpenDb()) {
  188.         QStringList key_list;
  189.         key_list.append("ImageUid");
  190.         QString where = QString("ImageUid = '%1'").arg(image_uid);
  191.         QList<QMap<QString, QVariant>> res;
  192.         if (DbManager::select(image_table_name_, key_list, res, where)) {
  193.             if (res.size() == 1) {
  194.                 success = true;
  195.             }
  196.         }
  197.     }
  198.     DbManager::CloseDb();
  199.     return success;
  200. }
  201. bool StudyDao::Initial() {
  202.     bool result = false;
  203.     if (DbManager::OpenDb()) {
  204.         bool exist;
  205.         if (DbManager::IsExistTable(study_table_name_, exist)) {
  206.             if (!exist) {
  207.                 result = CreateTable();
  208.             } else {
  209.                 if (CheckTable()) {
  210.                     result = true;
  211.                 } else {
  212.                     if (DbManager::RemoveTable(study_table_name_)) {
  213.                         result = CreateTable();
  214.                     }
  215.                 }
  216.             }
  217.         }
  218.     }
  219.     DbManager::CloseDb();
  220.     return result;
  221. }
  222. bool StudyDao::CreateTable() {
  223.     bool result = false;
  224.     // Create StudyTable
  225.     QString str ;
  226.     str = "CREATE TABLE IF NOT EXISTS StudyTable("
  227.           "StudyUid VARCHAR(128) PRIMARY KEY NOT NULL,"
  228.           "AccNumber VARCHAR(64) NOT NULL, PatientId VARCHAR(64) NOT NULL,"
  229.           "PatientName VARCHAR(64), "
  230.           "PatientSex VARCHAR(2) NOT NULL,"
  231.           "PatientBirth DATE NOT NULL,"
  232.           "PatientAge VARCHAR(6),"
  233.           "StudyTime DATETIME NOT NULL,"
  234.           "Modality VARCHAR(2) NOT NULL, "
  235.           "StudyDesc TEXT)";
  236.     result = DbManager::ExecSqlStr(str);
  237.     str = "CREATE INDEX IF NOT EXISTS IX_StudyTable_StudyDate ON StudyTable(StudyTime)";
  238.     result = DbManager::ExecSqlStr(str);
  239.     // Create ImageTable
  240.     str = "CREATE TABLE IF NOT EXISTS ImageTable("
  241.           "ImageUid VARCHAR(128) PRIMARY KEY NOT NULL,"
  242.           "SopClassUid VARCHAR(128) NOT NULL,"
  243.           "SeriesUid VARCHAR(128) NOT NULL, "
  244.           "StudyUid VARCHAR(128) NOT NULL,"
  245.           "RefImageUid VARCHAR(128),"
  246.           "ImageNo VARCHAR(16), "
  247.           "ImageTime DATETIME NOT NULL,"
  248.           "ImageDesc TEXT,"
  249.           "ImageFile TEXT,"
  250.           "FOREIGN KEY(StudyUid) REFERENCES StudyTable(StudyUid))";
  251.     result = DbManager::ExecSqlStr(str);
  252.     str = "CREATE INDEX IF NOT EXISTS IX_ImageTable_ImageTime ON ImageTable(ImageTime)";
  253.     result = DbManager::ExecSqlStr(str);
  254.     return result;
  255. }
  256. bool StudyDao::CheckTable() {
  257.     bool ok1 = false;
  258.     bool ok2 = false;
  259.     if (DbManager::IsExistTable(study_table_name_, ok1) &&
  260.             DbManager::IsExistTable(image_table_name_, ok2) ) {
  261.         if (ok1 && ok2) {
  262.             return true;
  263.         }
  264.     }
  265.     return false;
  266. }
复制代码
  1. #ifndef DBMANAGER_H
  2. #define DBMANAGER_H
  3. #include <QObject>
  4. #include <QMutex>
  5. #include <QSqlDatabase>
  6. namespace Kiss {
  7.     class DbManager : public QObject {
  8.         Q_OBJECT
  9.       public :
  10.         enum SQLiteType {
  11.             dtNull = 0,//空值类型
  12.             dtInteger = 1,//有符号整数
  13.             dtReal = 2,//有符号浮点数,8字节
  14.             dtText = 3,//文本字符串
  15.             dtBlob = 4,//根据输入类型
  16.             dtVarchar_64 = 5,
  17.             dtTimeStamp = 6,
  18.             dtTimeStamp_NotNull = 7,
  19.         };
  20.       public:
  21.         static bool DbInitial();
  22.         static bool Deallocate();
  23.         static bool CreateDbFile();
  24.         static bool IsOpenedDb();
  25.         static bool OpenDb();
  26.         static bool CloseDb();
  27.         static bool IsExistTable(const QString &table_name, bool &result);
  28.         static bool CreateTable(const QString &table_name,
  29.                                 const QStringList &key_list,
  30.                                 const QList<SQLiteType> &type_list);
  31.         static bool RemoveTable(const QString &table_name);
  32.         static bool IsExistColumn(const QString &table_name,
  33.                                   const QString &column_name,
  34.                                   bool &result);
  35.         static bool update(const QString &table_name,
  36.                            const QMap<QString, QVariant> &values,
  37.                            const QString &where);
  38.         static bool remove(const QString &table_name,
  39.                            const QString &where = "");
  40.         static bool insert(const QString &table_name,
  41.                            const QMap<QString, QVariant> &values);
  42.         static bool select(const QString &table_name,
  43.                            const QStringList &colunms,
  44.                            QList<QMap<QString, QVariant>> &values,
  45.                            const QString &where = "");
  46.         static bool ExecSqlStr(const QString &sql_str);
  47.       signals:
  48.       public slots:
  49.       private:
  50.         explicit DbManager(QObject *parent = nullptr);
  51.         virtual ~DbManager() override;
  52.       public:
  53.         static QSqlDatabase data_base;
  54.       private:
  55.         static QMutex file_mutex_;
  56.         static QMutex data_mutex_;
  57.         static QString db_name_;
  58.         static QString file_name_;
  59.         static QStringList sqlite_type_string_;
  60.         static bool init_;
  61.     };
  62. }
  63. using namespace Kiss;
  64. #endif // DBMANAGER_H
复制代码
  1. #include "dbmanager.h"
  2. #include <Global/KissGlobal>
  3. QSqlDatabase DbManager::data_base;
  4. QMutex DbManager::file_mutex_;
  5. QMutex DbManager::data_mutex_;
  6. QString DbManager::db_name_ = DB_CONNECTION_NAME;
  7. QString DbManager::file_name_ = DB_NAME;
  8. QStringList DbManager::sqlite_type_string_;
  9. bool DbManager::init_ = false;
  10. bool DbManager::DbInitial() {
  11.     QMutexLocker locker(&data_mutex_);
  12.     if (!init_) {
  13.         init_ = true;
  14.         if (QSqlDatabase::contains(db_name_)) {
  15.             data_base = QSqlDatabase::database(db_name_);
  16.         } else {
  17.             data_base = QSqlDatabase::addDatabase("QSQLITE", db_name_);
  18.         }
  19.         sqlite_type_string_.append("NULL");
  20.         sqlite_type_string_.append("INTEGER");
  21.         sqlite_type_string_.append("REAL");
  22.         sqlite_type_string_.append("TEXT");
  23.         sqlite_type_string_.append("BLOB");
  24.         sqlite_type_string_.append("VARCHAR ( 64 )");
  25.         sqlite_type_string_.append("TimeStamp");
  26.         sqlite_type_string_.append("TimeStamp NOT NULL");
  27.         return true;
  28.     }
  29.     return true;
  30. }
  31. bool DbManager::Deallocate() {
  32.     QMutexLocker locker(&data_mutex_);
  33.     data_base = QSqlDatabase();
  34.     if (QSqlDatabase::contains(db_name_)) {
  35.         QSqlDatabase::removeDatabase(db_name_);
  36.     }
  37.     return true;
  38. }
  39. bool DbManager::CreateDbFile() {
  40.     if (!QFile::exists(file_name_)) {
  41.         QFile db_file(file_name_);
  42.         if (!db_file.open(QIODevice::WriteOnly)) {
  43.             db_file.close();
  44.             qWarning() << "dbFile open failed";
  45.             return false;
  46.         }
  47.         db_file.close();
  48.     }
  49.     return true;
  50. }
  51. bool DbManager::IsOpenedDb() {
  52.     QMutexLocker locker(&data_mutex_);
  53.     return data_base.isOpen();
  54. }
  55. bool DbManager::OpenDb() {
  56.     file_mutex_.lock();
  57.     if (!IsOpenedDb()) {
  58.         QMutexLocker locker(&data_mutex_);
  59.         data_base.setDatabaseName(file_name_);
  60.         if (!data_base.open()) {
  61.             qWarning() << "database open error:" << data_base.lastError().text();
  62.             return false;
  63.         }
  64.     }
  65.     return true;
  66. }
  67. bool DbManager::CloseDb() {
  68.     file_mutex_.unlock();
  69.     if (IsOpenedDb()) {
  70.         QMutexLocker locker(&data_mutex_);
  71.         data_base.close();
  72.     }
  73.     return true;
  74. }
  75. bool DbManager::IsExistTable(const QString &table_name, bool &result) {
  76.     if (!IsOpenedDb()) {
  77.         qWarning() << "database not open error!";
  78.         return false;
  79.     }
  80.     QString sql_str = QString("SELECT 1 FROM sqlite_master "
  81.                               "WHERE type = 'table' AND  "
  82.                               "name = '%1'").arg(table_name);
  83.     QMutexLocker locker(&data_mutex_);
  84.     QSqlQuery query(data_base);
  85.     if (query.exec(sql_str)) {
  86.         if (query.next()) {
  87.             qint32 sql_result = query.value(0).toInt(); //有表时返回1,无表时返回null
  88.             if (sql_result) {
  89.                 result = true;
  90.                 return true;
  91.             } else {
  92.                 result = false;
  93.                 return true;
  94.             }
  95.         } else {
  96.             result = false;
  97.             return true;
  98.         }
  99.     } else {
  100.         qWarning() << "sqlstr exec error:" << data_base.lastError().text();
  101.         return false;
  102.     }
  103. }
  104. bool DbManager::CreateTable(const QString &table_name,
  105.                             const QStringList &key_list,
  106.                             const QList<DbManager::SQLiteType> &type_list) {
  107.     if (key_list.size() != type_list.size()) {
  108.         qWarning() << "keylist != typelist error";
  109.         return false;
  110.     }
  111.     if (!IsOpenedDb()) {
  112.         qWarning() << "database not open error!";
  113.         return false;
  114.     }
  115.     QString sql_str_1 = QString("CREATE TABLE %1 (").arg(table_name);
  116.     QString sql_str_2 = "%1 %2 PRIMARY KEY ,";
  117.     QString sql_str_temp = "%1 %2 ,";
  118.     sql_str_2 = sql_str_2
  119.                 .arg(key_list.at(0))
  120.                 .arg(sqlite_type_string_.at(type_list.at(0)));
  121.     for (qint32 i = 1; i < type_list.size(); ++i) {
  122.         sql_str_2 += sql_str_temp.arg(key_list.at(i))
  123.                      .arg(sqlite_type_string_.at(type_list.at(i)));
  124.     }
  125.     sql_str_2 = sql_str_2.left(sql_str_2.size() - 1);
  126.     QString sql_str_3 = ");";
  127.     QString sql_str = sql_str_1 + sql_str_2 + sql_str_3;
  128.     QMutexLocker locker(&data_mutex_);
  129.     QSqlQuery query(data_base);
  130.     if (query.exec(sql_str)) {
  131.         return true;
  132.     } else {
  133.         qWarning() << "sqlstr exec error:" << data_base.lastError().text();
  134.         return false;
  135.     }
  136. }
  137. bool DbManager::RemoveTable(const QString &table_name) {
  138.     if (!IsOpenedDb()) {
  139.         qWarning() << "database not open error!";
  140.         return false;
  141.     }
  142.     QString sql_str = QString("DROP TABLE '%1'").arg(table_name);
  143.     QMutexLocker locker(&data_mutex_);
  144.     QSqlQuery query(data_base);
  145.     if (query.exec(sql_str)) {
  146.         return true;
  147.     } else {
  148.         qWarning() << "sqlstr exec error:" << data_base.lastError().text();
  149.         return false;
  150.     }
  151. }
  152. bool DbManager::IsExistColumn(const QString &table_name,
  153.                               const QString &column_name,
  154.                               bool &result) {
  155.     if (!IsOpenedDb()) {
  156.         qWarning() << "database not open error!";
  157.         return false;
  158.     }
  159.     QString sql_str = QString("SELECT 1 FROM sqlite_master "
  160.                               "WHERE type = 'table' and "
  161.                               "name = '%1' and sql like '%%2%' "
  162.                              ).arg(table_name).arg(column_name);
  163.     QMutexLocker locker(&data_mutex_);
  164.     QSqlQuery query(data_base);
  165.     if (query.exec(sql_str)) {
  166.         if (query.next()) {
  167.             qint32 sql_result = query.value(0).toInt(); //有此字段时返回1,无字段时返回null
  168.             if (sql_result) {
  169.                 result = true;
  170.                 return true;
  171.             } else {
  172.                 result = false;
  173.                 return true;
  174.             }
  175.         } else {
  176.             result = false;
  177.             return true;
  178.         }
  179.     } else {
  180.         qWarning() << "sqlstr exec error:" << data_base.lastError().text();
  181.         return false;
  182.     }
  183. }
  184. bool DbManager::update(const QString &table_name,
  185.                        const QMap<QString, QVariant> &values,
  186.                        const QString &where) {
  187.     if (!IsOpenedDb()) {
  188.         qWarning() << "database not open error!";
  189.         return false;
  190.     }
  191.     QString sql_str_data;
  192.     QList<QString> key_list = values.keys();
  193.     foreach (QString key, key_list) {
  194.         if (!sql_str_data.isEmpty()) {
  195.             sql_str_data += ",";
  196.         }
  197.         sql_str_data += QString("%1=?").arg(key);
  198.     }
  199.     QString sql_str;
  200.     if (where.isEmpty()) {
  201.         sql_str = QString("UPDATE %1 SET %2"
  202.                          ).arg(table_name).arg(sql_str_data);
  203.     } else {
  204.         sql_str = QString("UPDATE %1 SET %2 WHERE %3"
  205.                          ).arg(table_name).arg(sql_str_data).arg(where);
  206.     }
  207.     QMutexLocker locker(&data_mutex_);
  208.     QSqlQuery query(data_base);
  209.     query.prepare(sql_str);
  210.     for (qint32 i = 0; i < key_list.count(); ++i) {
  211.         query.bindValue(i, values.value(key_list.at(i)));
  212.     }
  213.     if (query.exec()) {
  214.         return true;
  215.     } else {
  216.         qWarning() << "sqlstr exec error:" << data_base.lastError().text();
  217.         return false;
  218.     }
  219. }
  220. bool DbManager::remove(const QString &table_name,
  221.                        const QString &where) {
  222.     if (!IsOpenedDb()) {
  223.         qWarning() << "database not open error!";
  224.         return false;
  225.     }
  226.     QMutexLocker locker(&data_mutex_);
  227.     QString sql_str = QString("DELETE FROM %1 WHERE %2"
  228.                              ).arg(table_name).arg(where);
  229.     QSqlQuery query(data_base);
  230.     if (query.exec(sql_str)) {
  231.         return true;
  232.     } else {
  233.         qWarning() << "sqlstr exec error:" << data_base.lastError().text();
  234.         return false;
  235.     }
  236. }
  237. bool DbManager::insert(const QString &table_name,
  238.                        const QMap<QString, QVariant> &values) {
  239.     if (!IsOpenedDb()) {
  240.         qWarning() << "database not open error!";
  241.         return false;
  242.     }
  243.     QString sql_str_column, sql_str_data;
  244.     QList<QString> key_list = values.keys();
  245.     foreach (QString key, key_list) {
  246.         if (!sql_str_column.isEmpty()) {
  247.             sql_str_column += ",";
  248.         }
  249.         sql_str_column += key;
  250.         if (!sql_str_data.isEmpty()) {
  251.             sql_str_data += ",";
  252.         }
  253.         sql_str_data += "?";
  254.     }
  255.     QString sql_str = QString("INSERT INTO %1(%2) VALUES(%3)")
  256.                       .arg(table_name).arg(sql_str_column).arg(sql_str_data);
  257.     QMutexLocker locker(&data_mutex_);
  258.     QSqlQuery query(data_base);
  259.     query.prepare(sql_str);
  260.     for (qint32 i = 0; i < key_list.count(); ++i) {
  261.         query.bindValue(i, values.value(key_list.at(i)));
  262.     }
  263.     if (query.exec()) {
  264.         return true;
  265.     } else {
  266.         qWarning() << "sqlstr exec error:" << data_base.lastError().text();
  267.         return false;
  268.     }
  269. }
  270. bool DbManager::select(const QString &table_name,
  271.                        const QStringList &colunms,
  272.                        QList<QMap<QString, QVariant>> &values,
  273.                        const QString &where) {
  274.     if (!IsOpenedDb()) {
  275.         qWarning() << "database not open error!";
  276.         return false;
  277.     }
  278.     QString sql_str_columns;
  279.     if (colunms.size()) {
  280.         sql_str_columns = colunms.join(",");
  281.     } else {
  282. //        sql_str_columns = "*";
  283.         qWarning() << "colunms is null";
  284.         return false;
  285.     }
  286.     QString sql_str;
  287.     if (where.isEmpty()) {
  288.         sql_str = QString("SELECT %1 FROM %2")
  289.                   .arg(sql_str_columns)
  290.                   .arg(table_name);
  291.     } else {
  292.         sql_str = QString("SELECT %1 FROM %2 WHERE %3")
  293.                   .arg(sql_str_columns)
  294.                   .arg(table_name).arg(where);
  295.     }
  296.     QMutexLocker locker(&data_mutex_);
  297.     QSqlQuery query(data_base);
  298.     if (query.exec(sql_str)) {
  299.         qint32 columns_sum = query.record().count();
  300.         while (query.next()) {
  301.             QMap<QString, QVariant> row;
  302.             for (qint32 i = 0; i < columns_sum; ++i) {
  303.                 row.insert(colunms.at(i), query.value(i));
  304.             }
  305.             values.append(row);
  306.         }
  307.         return true;
  308.     } else {
  309.         qWarning() << "sqlstr exec error:" << data_base.lastError();
  310.         return false;
  311.     }
  312. }
  313. bool DbManager::ExecSqlStr(const QString &sql_str) {
  314.     if (!IsOpenedDb()) {
  315.         qWarning() << "database not open error!";
  316.         return false;
  317.     }
  318.     QMutexLocker locker(&data_mutex_);
  319.     QSqlQuery query(data_base);
  320.     if (query.exec(sql_str)) {
  321.         return true;
  322.     } else {
  323.         qWarning() << "sqlstr exec error:" << data_base.lastError().text();
  324.         return false;
  325.     }
  326. }
  327. DbManager::DbManager(QObject *parent) : QObject(parent) {
  328. }
  329. DbManager::~DbManager() {
  330. }
复制代码
5. 添加数据 StoreScp

  Dcmtk Pacs 开发:StoreScp 实现
6. 添加数据 本地加载

182007q1poz6pfgco66bx6.png

  两个线程,一个负责打开本地dcm文件,一个往数据库添加
  1. #include "importdcmfilethread.h"
  2. #include <Db/KissDb>
  3. #include <Global/KissGlobal>
  4. #include "dcmtk/dcmdata/dcuid.h"
  5. ImportDcmFileThread::ImportDcmFileThread(ImportStudyModel *model, QObject *parent) :
  6.     QThread(parent) {
  7.     this->abort_ = false;
  8.     this->import_model_ = model;
  9. }
  10. void ImportDcmFileThread::run() {
  11.     StudyDao dao;
  12.     foreach (StudyRecord *study, import_model_->getStudyList()) {
  13.         if (abort_) {
  14.             break;
  15.         }
  16.         int images = 0;
  17.         QString study_dir_name =
  18.             QString("%1/%2_%3").arg(study->study_time_.date().toString("yyyyMM"),
  19.                                     study->study_time_.toString(DICOM_DATETIME_FORMAT),
  20.                                     study->acc_number_);
  21.         if(!dao.VerifyStudyByStuid(study->study_uid_)) {
  22.             dao.InsertStudyToDb(*study, true);
  23.         }
  24.         FileUtil::DirMake(QString("%1/%2").arg(DICOM_SAVE_PATH, study_dir_name));
  25.         foreach (ImageRecord *image, study->image_list_) {
  26.             bool raw = image->sop_class_uid_ == QString(UID_XRayAngiographicImageStorage);
  27.             QString src_file = image->image_file_;
  28.             image->image_file_ = QString("%1/%2_%3.dcm").arg(study_dir_name,
  29.                                  raw ? "angio" : "", image->image_uid_);
  30.             QFileInfo info(QString("%1/%2").arg(DICOM_SAVE_PATH, image->image_file_));
  31.             if (FileUtil::FileCopy(src_file, QString("%1/%2").arg(DICOM_SAVE_PATH, image->image_file_))) {
  32.                 if (!dao.VerifyImageByIMmuid(image->image_uid_)) {
  33.                     if (dao.InsertImageToDb(*image, true)) {
  34.                         images++;
  35.                     } else {
  36.                     }
  37.                 } else {
  38.                     if (dao.UpdateImageFile(image->image_uid_, image->image_file_)) {
  39.                         images++;
  40.                     } else {
  41.                         FileUtil::DeleteFileOrFolder(
  42.                             QString("%1/%2").arg(DICOM_SAVE_PATH, image->image_file_));
  43.                     }
  44.                 }
  45.             }
  46.             image->image_file_ = src_file;
  47.             emit Signal_ResultReady();
  48.         }
  49.         study->status_ = tr("Imported: Images %1.").arg(images);
  50.         import_model_->resetStudyStatus(study);
  51.     }
  52. }
  53. void ImportDcmFileThread::SetAbort(bool yes) {
  54.     abort_ = yes;
  55. }
复制代码
  1. #include "scandcmfilethread.h"
  2. #include <Global/KissGlobal>
  3. #include "dcmtk/config/osconfig.h"
  4. #include "dcmtk/dcmdata/dcfilefo.h"
  5. #include "dcmtk/dcmdata/dcdeftag.h"
  6. #include "dcmtk/dcmsr/dsrdoc.h"
  7. #include "dcmtk/dcmimgle/dcmimage.h"
  8. #include "dcmtk/dcmdata/dcuid.h"
  9. ScanDcmFileThread::ScanDcmFileThread(QObject *parent) :
  10.     QThread(parent) {
  11.     this->abort_ = false;
  12. }
  13. void ScanDcmFileThread::run() {
  14.     using namespace Kiss;
  15.     foreach (QString file, file_list_) {
  16.         if (abort_) {
  17.             break;
  18.         }
  19.         StudyRecord *study = nullptr;
  20.         DcmFileFormat dcmFile;
  21.         OFCondition cond = dcmFile.loadFile(file.toLocal8Bit().data());
  22.         DcmDataset *dset = dcmFile.getDataset();
  23.         if (cond.good() && dset) {
  24.             const char *value = nullptr;
  25.             QString studyUid, seriesUid, instUid, sopClassUid;
  26.             dset->findAndGetString(DCM_StudyInstanceUID, value);
  27.             studyUid = QString::fromLatin1(value);
  28.             dset->findAndGetString(DCM_SeriesInstanceUID, value);
  29.             seriesUid = QString::fromLatin1(value);
  30.             dset->findAndGetString(DCM_SOPInstanceUID, value);
  31.             instUid = QString::fromLatin1(value);
  32.             dset->findAndGetString(DCM_SOPClassUID, value);
  33.             sopClassUid = QString::fromLatin1(value);
  34.             if (!(studyUid.isEmpty() || seriesUid.isEmpty() ||
  35.                     instUid.isEmpty() || sopClassUid.isEmpty())) {
  36.                 study = new StudyRecord(studyUid);
  37.                 dset->findAndGetString(DCM_AccessionNumber, value);
  38.                 study->acc_number_ = QString::fromLocal8Bit(value).remove(QChar(' '));
  39.                 dset->findAndGetString(DCM_PatientID, value);
  40.                 study->patient_id_ = QString::fromLocal8Bit(value);
  41.                 dset->findAndGetString(DCM_PatientName, value);
  42.                 study->patient_name_ = QString::fromLocal8Bit(value);
  43.                 dset->findAndGetString(DCM_PatientSex, value);
  44.                 study->patient_sex_ = QString::fromLocal8Bit(value).remove(QChar(' '));
  45.                 dset->findAndGetString(DCM_PatientBirthDate, value);
  46.                 study->patient_birth_ = QDate::fromString(QString::fromLatin1(value), "yyyyMMdd");
  47.                 dset->findAndGetString(DCM_PatientAge, value);
  48.                 study->patient_age_ = QString::fromLocal8Bit(value).remove(QChar(' '));
  49.                 dset->findAndGetString(DCM_StudyDate, value);
  50.                 study->study_time_.setDate(QDate::fromString(QString::fromLatin1(value), "yyyyMMdd"));
  51.                 dset->findAndGetString(DCM_StudyTime, value);
  52.                 study->study_time_.setTime(formatDicomTime(QString::fromLatin1(value)));
  53.                 dset->findAndGetString(DCM_StudyDescription, value);
  54.                 study->study_desc_ = QString::fromLocal8Bit(value);
  55.                 dset->findAndGetString(DCM_InstitutionName, value);
  56.                 study->institution_ = QString::fromLocal8Bit(value);
  57.                 dset->findAndGetString(DCM_Modality, value);
  58.                 study->modality_ = QString::fromLatin1(value);
  59.                 if (sopClassUid == UID_XRayAngiographicImageStorage ||// 造影血管
  60.                         true) {
  61.                     ImageRecord *image = new ImageRecord(instUid);
  62.                     image->sop_class_uid_ = sopClassUid;
  63.                     image->series_uid_ = seriesUid;
  64.                     image->study_uid_ = studyUid;
  65.                     image->image_file_ = file;
  66.                     study->image_list_.append(image);
  67.                     dset->findAndGetString(DCM_ReferencedSOPInstanceUID, value, true);
  68.                     image->ref_image_uid_ = QString::fromLatin1(value);
  69.                     dset->findAndGetString(DCM_InstanceNumber, value);
  70.                     image->image_number_ = QString::fromLatin1(value);
  71.                     dset->findAndGetString(DCM_SeriesDescription, value);
  72.                     image->image_desc_ = QString::fromLocal8Bit(value);
  73.                     dset->findAndGetString(DCM_ContentDate, value);
  74.                     image->image_yime_.setDate(
  75.                         QDate::fromString(QString::fromLatin1(value), "yyyyMMdd"));
  76.                     dset->findAndGetString(DCM_ContentTime, value);
  77.                     image->image_yime_.setTime(formatDicomTime(QString::fromLatin1(value)));
  78.                 }
  79.             }
  80.         }
  81.         if (study && (study->image_list_.isEmpty())) {
  82.             delete study;
  83.             study = nullptr;
  84.         }
  85.         emit Signal_ResultRecord(study);
  86.         emit Signal_ResultReady();
  87.     }
  88. }
  89. void ScanDcmFileThread::SetFiles(const QStringList &files) {
  90.     file_list_ = files;
  91. }
  92. void ScanDcmFileThread::SetAbort(bool yes) {
  93.     abort_ = yes;
  94. }
复制代码
来源:https://blog.csdn.net/a150********/article/details/109058579
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

提醒:禁止复制他人回复等『恶意灌水』行为,违者重罚!
您需要登录后才可以回帖 登录 | 注册[Register] 手机动态码快速登录 微信登录

本版积分规则

发布主题 快速回复 收藏帖子 返回列表 客服中心 搜索
简体中文 繁體中文 English 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french

QQ|RSS订阅|小黑屋|处罚记录|手机版|联系我们|Archiver|医工互联 |粤ICP备2021178090号 |网站地图

GMT+8, 2024-11-22 11:37 , Processed in 0.303985 second(s), 66 queries .

Powered by Discuz!

Copyright © 2001-2023, Discuz! Team.

快速回复 返回顶部 返回列表