ctkDICOMAppWidget.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. // std includes
  2. #include <iostream>
  3. #include <dcmimage.h>
  4. // Qt includes
  5. #include <QDebug>
  6. #include <QTreeView>
  7. #include <QTabBar>
  8. #include <QSettings>
  9. #include <QAction>
  10. #include <QModelIndex>
  11. #include <QCheckBox>
  12. // ctkDICOMCore includes
  13. #include "ctkDICOMDatabase.h"
  14. #include "ctkDICOMIndexer.h"
  15. // ctkDICOMWidgets includes
  16. #include "ctkDICOMModel.h"
  17. #include "ctkDICOMAppWidget.h"
  18. #include "ctkDICOMQueryResultsTabWidget.h"
  19. #include "ui_ctkDICOMAppWidget.h"
  20. #include "ctkDirectoryButton.h"
  21. #include "ctkFileDialog.h"
  22. #include "ctkDICOMQueryRetrieveWidget.h"
  23. #include "ctkDICOMImportWidget.h"
  24. #include "ctkDICOMThumbnailWidget.h"
  25. #include "ctkDICOMThumbnailGenerator.h"
  26. //logger
  27. #include <ctkLogger.h>
  28. static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMAppWidget");
  29. //----------------------------------------------------------------------------
  30. class ctkDICOMAppWidgetPrivate: public Ui_ctkDICOMAppWidget
  31. {
  32. public:
  33. ctkDICOMAppWidgetPrivate();
  34. ctkFileDialog* ImportDialog;
  35. ctkDICOMQueryRetrieveWidget* QueryRetrieveWidget;
  36. QSharedPointer<ctkDICOMDatabase> DICOMDatabase;
  37. QSharedPointer<ctkDICOMThumbnailGenerator> thumbnailGenerator;
  38. ctkDICOMModel DICOMModel;
  39. QSharedPointer<ctkDICOMIndexer> DICOMIndexer;
  40. };
  41. //----------------------------------------------------------------------------
  42. // ctkDICOMAppWidgetPrivate methods
  43. ctkDICOMAppWidgetPrivate::ctkDICOMAppWidgetPrivate(){
  44. DICOMDatabase = QSharedPointer<ctkDICOMDatabase> (new ctkDICOMDatabase);
  45. thumbnailGenerator = QSharedPointer <ctkDICOMThumbnailGenerator> (new ctkDICOMThumbnailGenerator);
  46. DICOMDatabase->setThumbnailGenerator(thumbnailGenerator.data());
  47. DICOMIndexer = QSharedPointer<ctkDICOMIndexer> (new ctkDICOMIndexer);
  48. DICOMIndexer->setThumbnailGenerator(thumbnailGenerator.data());
  49. }
  50. //----------------------------------------------------------------------------
  51. // ctkDICOMAppWidget methods
  52. //----------------------------------------------------------------------------
  53. ctkDICOMAppWidget::ctkDICOMAppWidget(QWidget* _parent):Superclass(_parent),
  54. d_ptr(new ctkDICOMAppWidgetPrivate)
  55. {
  56. Q_D(ctkDICOMAppWidget);
  57. d->setupUi(this);
  58. //Hide image previewer buttons
  59. d->nextImageButton->hide();
  60. d->prevImageButton->hide();
  61. d->nextSeriesButton->hide();
  62. d->prevSeriesButton->hide();
  63. d->nextStudyButton->hide();
  64. d->prevStudyButton->hide();
  65. //Enable sorting in tree view
  66. d->treeView->setSortingEnabled(true);
  67. d->treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
  68. connect(d->treeView, SIGNAL(collapsed(QModelIndex)), this, SLOT(onTreeCollapsed(QModelIndex)));
  69. connect(d->treeView, SIGNAL(expanded(QModelIndex)), this, SLOT(onTreeExpanded(QModelIndex)));
  70. //Set toolbar button style
  71. d->toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
  72. //Initialize Q/R widget
  73. d->QueryRetrieveWidget = new ctkDICOMQueryRetrieveWidget();
  74. d->QueryRetrieveWidget->setWindowModality ( Qt::ApplicationModal );
  75. //initialize directory from settings, then listen for changes
  76. QSettings settings;
  77. if ( settings.value("DatabaseDirectory", "") == "" )
  78. {
  79. QString directory = QString("./ctkDICOM-Database");
  80. settings.setValue("DatabaseDirectory", directory);
  81. settings.sync();
  82. }
  83. QString databaseDirectory = settings.value("DatabaseDirectory").toString();
  84. this->setDatabaseDirectory(databaseDirectory);
  85. d->directoryButton->setDirectory(databaseDirectory);
  86. connect(d->directoryButton, SIGNAL(directoryChanged(const QString&)), this, SLOT(setDatabaseDirectory(const QString&)));
  87. //Initialize import widget
  88. d->ImportDialog = new ctkFileDialog();
  89. QCheckBox* importCheckbox = new QCheckBox("Copy on import", d->ImportDialog);
  90. d->ImportDialog->setBottomWidget(importCheckbox);
  91. d->ImportDialog->setFileMode(QFileDialog::Directory);
  92. d->ImportDialog->setLabelText(QFileDialog::Accept,"Import");
  93. d->ImportDialog->setWindowTitle("Import DICOM files from directory ...");
  94. d->ImportDialog->setWindowModality(Qt::ApplicationModal);
  95. //connect signal and slots
  96. connect(d->treeView, SIGNAL(clicked(const QModelIndex&)), d->thumbnailsWidget, SLOT(onModelSelected(const QModelIndex &)));
  97. connect(d->treeView, SIGNAL(clicked(const QModelIndex&)), d->imagePreview, SLOT(onModelSelected(const QModelIndex &)));
  98. connect(d->treeView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(onModelSelected(const QModelIndex &)));
  99. connect(d->thumbnailsWidget, SIGNAL(selected(const ctkDICOMThumbnailWidget&)), this, SLOT(onThumbnailSelected(const ctkDICOMThumbnailWidget&)));
  100. connect(d->thumbnailsWidget, SIGNAL(doubleClicked(const ctkDICOMThumbnailWidget&)), this, SLOT(onThumbnailDoubleClicked(const ctkDICOMThumbnailWidget&)));
  101. connect(d->ImportDialog, SIGNAL(fileSelected(QString)),this,SLOT(onImportDirectory(QString)));
  102. connect(d->DICOMDatabase.data(), SIGNAL( databaseChanged() ), &(d->DICOMModel), SLOT( reset() ) );
  103. connect(d->QueryRetrieveWidget, SIGNAL( canceled() ), d->QueryRetrieveWidget, SLOT( hide() ) );
  104. connect(d->imagePreview, SIGNAL(requestNextImage()), this, SLOT(onNextImage()));
  105. connect(d->imagePreview, SIGNAL(requestPreviousImage()), this, SLOT(onPreviousImage()));
  106. }
  107. //----------------------------------------------------------------------------
  108. ctkDICOMAppWidget::~ctkDICOMAppWidget()
  109. {
  110. Q_D(ctkDICOMAppWidget);
  111. d->QueryRetrieveWidget->deleteLater();
  112. d->ImportDialog->deleteLater();
  113. }
  114. //----------------------------------------------------------------------------
  115. void ctkDICOMAppWidget::setDatabaseDirectory(const QString& directory)
  116. {
  117. Q_D(ctkDICOMAppWidget);
  118. QSettings settings;
  119. settings.setValue("DatabaseDirectory", directory);
  120. settings.sync();
  121. //close the active DICOM database
  122. d->DICOMDatabase->closeDatabase();
  123. //open DICOM database on the directory
  124. QString databaseFileName = directory + QString("/ctkDICOM.sql");
  125. try { d->DICOMDatabase->openDatabase( databaseFileName ); }
  126. catch (std::exception e)
  127. {
  128. std::cerr << "Database error: " << qPrintable(d->DICOMDatabase->lastError()) << "\n";
  129. d->DICOMDatabase->closeDatabase();
  130. return;
  131. }
  132. d->DICOMModel.setDatabase(d->DICOMDatabase->database());
  133. d->DICOMModel.setDisplayLevel(ctkDICOMModel::SeriesType);
  134. d->treeView->setModel(&d->DICOMModel);
  135. d->treeView->resizeColumnToContents(0);
  136. //pass DICOM database instance to Import widget
  137. // d->ImportDialog->setDICOMDatabase(d->DICOMDatabase);
  138. d->QueryRetrieveWidget->setRetrieveDatabase(d->DICOMDatabase);
  139. // update the button and let any connected slots know about the change
  140. d->directoryButton->setDirectory(directory);
  141. d->thumbnailsWidget->setDatabaseDirectory(directory);
  142. d->imagePreview->setDatabaseDirectory(directory);
  143. emit databaseDirectoryChanged(directory);
  144. }
  145. //----------------------------------------------------------------------------
  146. QString ctkDICOMAppWidget::databaseDirectory() const
  147. {
  148. QSettings settings;
  149. return settings.value("DatabaseDirectory").toString();
  150. }
  151. void ctkDICOMAppWidget::onAddToDatabase()
  152. {
  153. //Q_D(ctkDICOMAppWidget);
  154. //d->
  155. }
  156. //----------------------------------------------------------------------------
  157. void ctkDICOMAppWidget::openImportDialog()
  158. {
  159. Q_D(ctkDICOMAppWidget);
  160. d->ImportDialog->show();
  161. d->ImportDialog->raise();
  162. }
  163. //----------------------------------------------------------------------------
  164. void ctkDICOMAppWidget::openExportDialog()
  165. {
  166. }
  167. //----------------------------------------------------------------------------
  168. void ctkDICOMAppWidget::openQueryDialog()
  169. {
  170. Q_D(ctkDICOMAppWidget);
  171. d->QueryRetrieveWidget->show();
  172. d->QueryRetrieveWidget->raise();
  173. }
  174. //----------------------------------------------------------------------------
  175. void ctkDICOMAppWidget::onThumbnailSelected(const ctkDICOMThumbnailWidget& widget)
  176. {
  177. Q_D(ctkDICOMAppWidget);
  178. d->imagePreview->onModelSelected(widget.sourceIndex());
  179. }
  180. //----------------------------------------------------------------------------
  181. void ctkDICOMAppWidget::onThumbnailDoubleClicked(const ctkDICOMThumbnailWidget& widget)
  182. {
  183. Q_D(ctkDICOMAppWidget);
  184. logger.debug("double clicked");
  185. QModelIndex index = widget.sourceIndex();
  186. ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
  187. QModelIndex index0 = index.sibling(index.row(), 0);
  188. if(model && (model->data(index0,ctkDICOMModel::TypeRole) != ctkDICOMModel::ImageType)){
  189. this->onModelSelected(index0);
  190. d->treeView->setCurrentIndex(index0);
  191. d->thumbnailsWidget->onModelSelected(index0);
  192. d->imagePreview->onModelSelected(index0);
  193. }
  194. }
  195. //----------------------------------------------------------------------------
  196. void ctkDICOMAppWidget::onImportDirectory(QString directory)
  197. {
  198. Q_D(ctkDICOMAppWidget);
  199. if (QDir(directory).exists())
  200. {
  201. QCheckBox* copyOnImport = qobject_cast<QCheckBox*>(d->ImportDialog->bottomWidget());
  202. QString targetDirectory;
  203. if (copyOnImport->isEnabled())
  204. {
  205. targetDirectory = d->DICOMDatabase->databaseDirectory();
  206. }
  207. d->DICOMIndexer->addDirectory(*d->DICOMDatabase,directory,targetDirectory);
  208. d->DICOMModel.reset();
  209. }
  210. }
  211. //----------------------------------------------------------------------------
  212. void ctkDICOMAppWidget::onModelSelected(const QModelIndex &index){
  213. Q_D(ctkDICOMAppWidget);
  214. ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
  215. if(model){
  216. QModelIndex index0 = index.sibling(index.row(), 0);
  217. if ( model->data(index0,ctkDICOMModel::TypeRole) == ctkDICOMModel::PatientType ){
  218. d->nextImageButton->show();
  219. d->prevImageButton->show();
  220. d->nextSeriesButton->show();
  221. d->prevSeriesButton->show();
  222. d->nextStudyButton->show();
  223. d->prevStudyButton->show();
  224. }else if ( model->data(index0,ctkDICOMModel::TypeRole) == ctkDICOMModel::StudyType ){
  225. d->nextImageButton->show();
  226. d->prevImageButton->show();
  227. d->nextSeriesButton->show();
  228. d->prevSeriesButton->show();
  229. d->nextStudyButton->hide();
  230. d->prevStudyButton->hide();
  231. }else if ( model->data(index0,ctkDICOMModel::TypeRole) == ctkDICOMModel::SeriesType ){
  232. d->nextImageButton->show();
  233. d->prevImageButton->show();
  234. d->nextSeriesButton->hide();
  235. d->prevSeriesButton->hide();
  236. d->nextStudyButton->hide();
  237. d->prevStudyButton->hide();
  238. }else{
  239. d->nextImageButton->hide();
  240. d->prevImageButton->hide();
  241. d->nextSeriesButton->hide();
  242. d->prevSeriesButton->hide();
  243. d->nextStudyButton->hide();
  244. d->prevStudyButton->hide();
  245. }
  246. }else{
  247. d->nextImageButton->hide();
  248. d->prevImageButton->hide();
  249. d->nextSeriesButton->hide();
  250. d->prevSeriesButton->hide();
  251. d->nextStudyButton->hide();
  252. d->prevStudyButton->hide();
  253. }
  254. }
  255. //----------------------------------------------------------------------------
  256. void ctkDICOMAppWidget::onNextImage(){
  257. Q_D(ctkDICOMAppWidget);
  258. QModelIndex currentIndex = d->imagePreview->currentImageIndex();
  259. if(currentIndex.isValid()){
  260. ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
  261. if(model){
  262. QModelIndex seriesIndex = currentIndex.parent();
  263. int imageCount = model->rowCount(seriesIndex);
  264. int imageID = currentIndex.row();
  265. imageID = (imageID+1)%imageCount;
  266. QModelIndex nextIndex = currentIndex.sibling(imageID, 0);
  267. d->imagePreview->onModelSelected(nextIndex);
  268. d->thumbnailsWidget->selectThumbnail(nextIndex);
  269. }
  270. }
  271. }
  272. //----------------------------------------------------------------------------
  273. void ctkDICOMAppWidget::onPreviousImage(){
  274. Q_D(ctkDICOMAppWidget);
  275. QModelIndex currentIndex = d->imagePreview->currentImageIndex();
  276. if(currentIndex.isValid()){
  277. ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
  278. if(model){
  279. QModelIndex seriesIndex = currentIndex.parent();
  280. int imageCount = model->rowCount(seriesIndex);
  281. int imageID = currentIndex.row();
  282. imageID--;
  283. if(imageID < 0) imageID += imageCount;
  284. QModelIndex prevIndex = currentIndex.sibling(imageID, 0);
  285. d->imagePreview->onModelSelected(prevIndex);
  286. d->thumbnailsWidget->selectThumbnail(prevIndex);
  287. }
  288. }
  289. }
  290. //----------------------------------------------------------------------------
  291. void ctkDICOMAppWidget::onNextSeries(){
  292. Q_D(ctkDICOMAppWidget);
  293. QModelIndex currentIndex = d->imagePreview->currentImageIndex();
  294. if(currentIndex.isValid()){
  295. ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
  296. if(model){
  297. QModelIndex seriesIndex = currentIndex.parent();
  298. QModelIndex studyIndex = seriesIndex.parent();
  299. int seriesCount = model->rowCount(studyIndex);
  300. int seriesID = seriesIndex.row();
  301. seriesID = (seriesID + 1)%seriesCount;
  302. QModelIndex nextIndex = seriesIndex.sibling(seriesID, 0);
  303. d->imagePreview->onModelSelected(nextIndex);
  304. d->thumbnailsWidget->selectThumbnail(nextIndex);
  305. }
  306. }
  307. }
  308. //----------------------------------------------------------------------------
  309. void ctkDICOMAppWidget::onPreviousSeries(){
  310. Q_D(ctkDICOMAppWidget);
  311. QModelIndex currentIndex = d->imagePreview->currentImageIndex();
  312. if(currentIndex.isValid()){
  313. ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
  314. if(model){
  315. QModelIndex seriesIndex = currentIndex.parent();
  316. QModelIndex studyIndex = seriesIndex.parent();
  317. int seriesCount = model->rowCount(studyIndex);
  318. int seriesID = seriesIndex.row();
  319. seriesID--;
  320. if(seriesID < 0) seriesID += seriesCount;
  321. QModelIndex prevIndex = seriesIndex.sibling(seriesID, 0);
  322. d->imagePreview->onModelSelected(prevIndex);
  323. d->thumbnailsWidget->selectThumbnail(prevIndex);
  324. }
  325. }
  326. }
  327. //----------------------------------------------------------------------------
  328. void ctkDICOMAppWidget::onNextStudy(){
  329. Q_D(ctkDICOMAppWidget);
  330. QModelIndex currentIndex = d->imagePreview->currentImageIndex();
  331. if(currentIndex.isValid()){
  332. ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
  333. if(model){
  334. QModelIndex seriesIndex = currentIndex.parent();
  335. QModelIndex studyIndex = seriesIndex.parent();
  336. QModelIndex patientIndex = studyIndex.parent();
  337. int studyCount = model->rowCount(patientIndex);
  338. int studyID = studyIndex.row();
  339. studyID = (studyID + 1)%studyCount;
  340. QModelIndex nextIndex = studyIndex.sibling(studyID, 0);
  341. d->imagePreview->onModelSelected(nextIndex);
  342. d->thumbnailsWidget->selectThumbnail(nextIndex);
  343. }
  344. }
  345. }
  346. //----------------------------------------------------------------------------
  347. void ctkDICOMAppWidget::onPreviousStudy(){
  348. Q_D(ctkDICOMAppWidget);
  349. QModelIndex currentIndex = d->imagePreview->currentImageIndex();
  350. if(currentIndex.isValid()){
  351. ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
  352. if(model){
  353. QModelIndex seriesIndex = currentIndex.parent();
  354. QModelIndex studyIndex = seriesIndex.parent();
  355. QModelIndex patientIndex = studyIndex.parent();
  356. int studyCount = model->rowCount(patientIndex);
  357. int studyID = studyIndex.row();
  358. studyID--;
  359. if(studyID < 0) studyID += studyCount;
  360. QModelIndex prevIndex = studyIndex.sibling(studyID, 0);
  361. d->imagePreview->onModelSelected(prevIndex);
  362. d->thumbnailsWidget->selectThumbnail(prevIndex);
  363. }
  364. }
  365. }
  366. //----------------------------------------------------------------------------
  367. void ctkDICOMAppWidget::onTreeCollapsed(const QModelIndex &index){
  368. Q_D(ctkDICOMAppWidget);
  369. d->treeView->resizeColumnToContents(0);
  370. }
  371. //----------------------------------------------------------------------------
  372. void ctkDICOMAppWidget::onTreeExpanded(const QModelIndex &index){
  373. Q_D(ctkDICOMAppWidget);
  374. d->treeView->resizeColumnToContents(0);
  375. }