ctkDICOMBrowser.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // std includes
  15. #include <iostream>
  16. // Qt includes
  17. #include <QAction>
  18. #include <QApplication>
  19. #include <QCoreApplication>
  20. #include <QCheckBox>
  21. #include <QComboBox>
  22. #include <QDebug>
  23. #include <QDialogButtonBox>
  24. #include <QFile>
  25. #include <QFormLayout>
  26. #include <QListView>
  27. #include <QMenu>
  28. #include <QMessageBox>
  29. #include <QProgressDialog>
  30. #include <QPushButton>
  31. #include <QSettings>
  32. #include <QStringListModel>
  33. #include <QWidgetAction>
  34. // ctkWidgets includes
  35. #include "ctkDirectoryButton.h"
  36. #include "ctkFileDialog.h"
  37. #include "ctkMessageBox.h"
  38. // ctkDICOMCore includes
  39. #include "ctkDICOMDatabase.h"
  40. #include "ctkDICOMIndexer.h"
  41. // ctkDICOMWidgets includes
  42. #include "ctkDICOMBrowser.h"
  43. #include "ctkDICOMQueryResultsTabWidget.h"
  44. #include "ctkDICOMQueryRetrieveWidget.h"
  45. #include "ctkDICOMQueryWidget.h"
  46. #include "ctkDICOMTableManager.h"
  47. #include "ui_ctkDICOMBrowser.h"
  48. //logger
  49. #include <ctkLogger.h>
  50. static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMBrowser");
  51. //----------------------------------------------------------------------------
  52. class ctkDICOMBrowserPrivate: public Ui_ctkDICOMBrowser
  53. {
  54. public:
  55. ctkDICOMBrowser* const q_ptr;
  56. Q_DECLARE_PUBLIC(ctkDICOMBrowser);
  57. ctkDICOMBrowserPrivate(ctkDICOMBrowser* );
  58. ~ctkDICOMBrowserPrivate();
  59. /// \brief Popup dialog asking user to confirm the directory import.
  60. ///
  61. /// If user check the "do not show again" checkbox, its choice is saved
  62. /// in the settings using the key `DICOM/DontConfirmImportDirectory`.
  63. bool confirmDirectoryImport();
  64. void importDirectory(QString directory, ctkDICOMBrowser::ImportDirectoryMode mode);
  65. void importOldSettings();
  66. ctkFileDialog* ImportDialog;
  67. ctkDICOMQueryRetrieveWidget* QueryRetrieveWidget;
  68. QSharedPointer<ctkDICOMDatabase> DICOMDatabase;
  69. QSharedPointer<ctkDICOMIndexer> DICOMIndexer;
  70. QProgressDialog *IndexerProgress;
  71. QProgressDialog *UpdateSchemaProgress;
  72. QProgressDialog *ExportProgress;
  73. void showIndexerDialog();
  74. void showUpdateSchemaDialog();
  75. // used when suspending the ctkDICOMModel
  76. QSqlDatabase EmptyDatabase;
  77. // local count variables to keep track of the number of items
  78. // added to the database during an import operation
  79. bool DisplayImportSummary;
  80. int PatientsAddedDuringImport;
  81. int StudiesAddedDuringImport;
  82. int SeriesAddedDuringImport;
  83. int InstancesAddedDuringImport;
  84. };
  85. //----------------------------------------------------------------------------
  86. class ctkDICOMImportStats
  87. {
  88. public:
  89. ctkDICOMImportStats(ctkDICOMBrowserPrivate* dicomBrowserPrivate) :
  90. DICOMBrowserPrivate(dicomBrowserPrivate)
  91. {
  92. // reset counts
  93. ctkDICOMBrowserPrivate* d = this->DICOMBrowserPrivate;
  94. d->PatientsAddedDuringImport = 0;
  95. d->StudiesAddedDuringImport = 0;
  96. d->SeriesAddedDuringImport = 0;
  97. d->InstancesAddedDuringImport = 0;
  98. }
  99. QString summary()
  100. {
  101. ctkDICOMBrowserPrivate* d = this->DICOMBrowserPrivate;
  102. QString message = "Directory import completed.\n\n";
  103. message += QString("%1 New Patients\n").arg(QString::number(d->PatientsAddedDuringImport));
  104. message += QString("%1 New Studies\n").arg(QString::number(d->StudiesAddedDuringImport));
  105. message += QString("%1 New Series\n").arg(QString::number(d->SeriesAddedDuringImport));
  106. message += QString("%1 New Instances\n").arg(QString::number(d->InstancesAddedDuringImport));
  107. return message;
  108. }
  109. ctkDICOMBrowserPrivate* DICOMBrowserPrivate;
  110. };
  111. //----------------------------------------------------------------------------
  112. // ctkDICOMBrowserPrivate methods
  113. //----------------------------------------------------------------------------
  114. ctkDICOMBrowserPrivate::ctkDICOMBrowserPrivate(ctkDICOMBrowser* parent): q_ptr(parent)
  115. {
  116. ImportDialog = 0;
  117. QueryRetrieveWidget = 0;
  118. DICOMDatabase = QSharedPointer<ctkDICOMDatabase> (new ctkDICOMDatabase);
  119. DICOMIndexer = QSharedPointer<ctkDICOMIndexer> (new ctkDICOMIndexer);
  120. IndexerProgress = 0;
  121. UpdateSchemaProgress = 0;
  122. ExportProgress = 0;
  123. DisplayImportSummary = true;
  124. PatientsAddedDuringImport = 0;
  125. StudiesAddedDuringImport = 0;
  126. SeriesAddedDuringImport = 0;
  127. InstancesAddedDuringImport = 0;
  128. }
  129. //----------------------------------------------------------------------------
  130. ctkDICOMBrowserPrivate::~ctkDICOMBrowserPrivate()
  131. {
  132. if ( IndexerProgress )
  133. {
  134. delete IndexerProgress;
  135. }
  136. if ( UpdateSchemaProgress )
  137. {
  138. delete UpdateSchemaProgress;
  139. }
  140. if ( ExportProgress )
  141. {
  142. delete ExportProgress;
  143. }
  144. }
  145. //----------------------------------------------------------------------------
  146. bool ctkDICOMBrowserPrivate::confirmDirectoryImport()
  147. {
  148. Q_Q(ctkDICOMBrowser);
  149. ctkMessageBox dialog(q);
  150. QString message = q->tr("Are you sure you want to import files ?");
  151. dialog.setText(message);
  152. dialog.setIcon(QMessageBox::Question);
  153. dialog.addButton(new QPushButton("Import"), QMessageBox::AcceptRole);
  154. dialog.addButton(QMessageBox::Cancel);
  155. dialog.setDontShowAgainSettingsKey("DICOM/DontConfirmImportDirectory");
  156. return dialog.exec() == QMessageBox::AcceptRole;
  157. }
  158. //----------------------------------------------------------------------------
  159. void ctkDICOMBrowserPrivate::showUpdateSchemaDialog()
  160. {
  161. Q_Q(ctkDICOMBrowser);
  162. if (UpdateSchemaProgress == 0)
  163. {
  164. //
  165. // Set up the Update Schema Progress Dialog
  166. //
  167. UpdateSchemaProgress = new QProgressDialog(
  168. q->tr("DICOM Schema Update"), "Cancel", 0, 100, q,
  169. Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
  170. // We don't want the progress dialog to resize itself, so we bypass the label
  171. // by creating our own
  172. QLabel* progressLabel = new QLabel(q->tr("Initialization..."));
  173. UpdateSchemaProgress->setLabel(progressLabel);
  174. UpdateSchemaProgress->setWindowModality(Qt::ApplicationModal);
  175. UpdateSchemaProgress->setMinimumDuration(0);
  176. UpdateSchemaProgress->setValue(0);
  177. q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdateStarted(int)),
  178. UpdateSchemaProgress, SLOT(setMaximum(int)));
  179. q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdateProgress(int)),
  180. UpdateSchemaProgress, SLOT(setValue(int)));
  181. q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdateProgress(QString)),
  182. progressLabel, SLOT(setText(QString)));
  183. // close the dialog
  184. q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdated()),
  185. UpdateSchemaProgress, SLOT(close()));
  186. }
  187. UpdateSchemaProgress->show();
  188. }
  189. //----------------------------------------------------------------------------
  190. void ctkDICOMBrowserPrivate::showIndexerDialog()
  191. {
  192. Q_Q(ctkDICOMBrowser);
  193. if (IndexerProgress == 0)
  194. {
  195. //
  196. // Set up the Indexer Progress Dialog
  197. //
  198. IndexerProgress = new QProgressDialog( q->tr("DICOM Import"), "Cancel", 0, 100, q,
  199. Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
  200. // We don't want the progress dialog to resize itself, so we bypass the label
  201. // by creating our own
  202. QLabel* progressLabel = new QLabel(q->tr("Initialization..."));
  203. IndexerProgress->setLabel(progressLabel);
  204. IndexerProgress->setWindowModality(Qt::ApplicationModal);
  205. IndexerProgress->setMinimumDuration(0);
  206. IndexerProgress->setValue(0);
  207. q->connect(IndexerProgress, SIGNAL(canceled()),
  208. DICOMIndexer.data(), SLOT(cancel()));
  209. q->connect(DICOMIndexer.data(), SIGNAL(progress(int)),
  210. IndexerProgress, SLOT(setValue(int)));
  211. q->connect(DICOMIndexer.data(), SIGNAL(indexingFilePath(QString)),
  212. progressLabel, SLOT(setText(QString)));
  213. q->connect(DICOMIndexer.data(), SIGNAL(indexingFilePath(QString)),
  214. q, SLOT(onFileIndexed(QString)));
  215. // close the dialog
  216. q->connect(DICOMIndexer.data(), SIGNAL(indexingComplete()),
  217. IndexerProgress, SLOT(close()));
  218. // stop indexing and reset the database if canceled
  219. q->connect(IndexerProgress, SIGNAL(canceled()),
  220. DICOMIndexer.data(), SLOT(cancel()));
  221. // allow users of this widget to know that the process has finished
  222. q->connect(IndexerProgress, SIGNAL(canceled()),
  223. q, SIGNAL(directoryImported()));
  224. q->connect(DICOMIndexer.data(), SIGNAL(indexingComplete()),
  225. q, SIGNAL(directoryImported()));
  226. }
  227. IndexerProgress->show();
  228. }
  229. //----------------------------------------------------------------------------
  230. // ctkDICOMBrowser methods
  231. //----------------------------------------------------------------------------
  232. ctkDICOMBrowser::ctkDICOMBrowser(QWidget* _parent):Superclass(_parent),
  233. d_ptr(new ctkDICOMBrowserPrivate(this))
  234. {
  235. Q_D(ctkDICOMBrowser);
  236. qRegisterMetaType<ctkDICOMBrowser::ImportDirectoryMode>("ctkDICOMBrowser::ImportDirectoryMode");
  237. d->setupUi(this);
  238. // signals related to tracking inserts
  239. connect(d->DICOMDatabase.data(), SIGNAL(patientAdded(int,QString,QString,QString)), this,
  240. SLOT(onPatientAdded(int,QString,QString,QString)));
  241. connect(d->DICOMDatabase.data(), SIGNAL(studyAdded(QString)), this, SLOT(onStudyAdded(QString)));
  242. connect(d->DICOMDatabase.data(), SIGNAL(seriesAdded(QString)), this, SLOT(onSeriesAdded(QString)));
  243. connect(d->DICOMDatabase.data(), SIGNAL(instanceAdded(QString)), this, SLOT(onInstanceAdded(QString)));
  244. connect(d->tableDensityComboBox ,SIGNAL(currentIndexChanged (const QString&)),
  245. this, SLOT(onTablesDensityComboBox(QString)));
  246. //Set ToolBar button style
  247. d->ToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
  248. //Initialize Q/R widget
  249. d->QueryRetrieveWidget = new ctkDICOMQueryRetrieveWidget();
  250. d->QueryRetrieveWidget->setWindowModality ( Qt::ApplicationModal );
  251. //initialize directory from settings, then listen for changes
  252. QSettings settings;
  253. if ( settings.value(Self::databaseDirectorySettingsKey(), "") == "" )
  254. {
  255. settings.setValue(Self::databaseDirectorySettingsKey(), QString("./ctkDICOM-Database"));
  256. settings.sync();
  257. }
  258. QString databaseDirectory = this->databaseDirectory();
  259. this->setDatabaseDirectory(databaseDirectory);
  260. d->DirectoryButton->setDirectory(databaseDirectory);
  261. d->dicomTableManager->setDICOMDatabase(d->DICOMDatabase.data());
  262. // TableView signals
  263. connect(d->dicomTableManager, SIGNAL(patientsSelectionChanged(const QItemSelection&, const QItemSelection&)),
  264. this, SLOT(onModelSelected(const QItemSelection&,const QItemSelection&)));
  265. connect(d->dicomTableManager, SIGNAL(studiesSelectionChanged(const QItemSelection&, const QItemSelection&)),
  266. this, SLOT(onModelSelected(const QItemSelection&,const QItemSelection&)));
  267. connect(d->dicomTableManager, SIGNAL(seriesSelectionChanged(const QItemSelection&, const QItemSelection&)),
  268. this, SLOT(onModelSelected(const QItemSelection&,const QItemSelection&)));
  269. // set up context menus for working on selected patients, studies, series
  270. connect(d->dicomTableManager, SIGNAL(patientsRightClicked(const QPoint&)),
  271. this, SLOT(onPatientsRightClicked(const QPoint&)));
  272. connect(d->dicomTableManager, SIGNAL(studiesRightClicked(const QPoint&)),
  273. this, SLOT(onStudiesRightClicked(const QPoint&)));
  274. connect(d->dicomTableManager, SIGNAL(seriesRightClicked(const QPoint&)),
  275. this, SLOT(onSeriesRightClicked(const QPoint&)));
  276. connect(d->DirectoryButton, SIGNAL(directoryChanged(QString)), this, SLOT(setDatabaseDirectory(QString)));
  277. // Initialize directoryMode widget
  278. QFormLayout *layout = new QFormLayout;
  279. QComboBox* importDirectoryModeComboBox = new QComboBox();
  280. importDirectoryModeComboBox->addItem("Add Link", ctkDICOMBrowser::ImportDirectoryAddLink);
  281. importDirectoryModeComboBox->addItem("Copy", ctkDICOMBrowser::ImportDirectoryCopy);
  282. importDirectoryModeComboBox->setToolTip(
  283. tr("Indicate if the files should be copied to the local database"
  284. " directory or if only links should be created ?"));
  285. layout->addRow(new QLabel("Import Directory Mode:"), importDirectoryModeComboBox);
  286. QCheckBox* skipConfirmImportDirectoryCheckBox = new QCheckBox();
  287. layout->addRow(new QLabel("Skip Import Directory Confirmation:"), skipConfirmImportDirectoryCheckBox);
  288. layout->setContentsMargins(0, 0, 0, 0);
  289. QWidget* importDirectoryBottomWidget = new QWidget();
  290. importDirectoryBottomWidget->setLayout(layout);
  291. // Default values
  292. importDirectoryModeComboBox->setCurrentIndex(
  293. importDirectoryModeComboBox->findData(this->importDirectoryMode()));
  294. skipConfirmImportDirectoryCheckBox->setChecked(this->skipConfirmImportDirectory());
  295. //Initialize import widget
  296. d->ImportDialog = new ctkFileDialog();
  297. d->ImportDialog->setBottomWidget(importDirectoryBottomWidget);
  298. d->ImportDialog->setFileMode(QFileDialog::Directory);
  299. // XXX Method setSelectionMode must be called after setFileMode
  300. d->ImportDialog->setSelectionMode(QAbstractItemView::ExtendedSelection);
  301. d->ImportDialog->setLabelText(QFileDialog::Accept,"Import");
  302. d->ImportDialog->setWindowTitle("Import DICOM files from directory ...");
  303. d->ImportDialog->setWindowModality(Qt::ApplicationModal);
  304. //connect signal and slots
  305. connect(d->ImportDialog, SIGNAL(filesSelected(QStringList)),
  306. this,SLOT(onImportDirectoriesSelected(QStringList)));
  307. connect(importDirectoryModeComboBox, SIGNAL(currentIndexChanged(int)),
  308. this, SLOT(onImportDirectoryComboBoxCurrentIndexChanged(int)));
  309. connect(skipConfirmImportDirectoryCheckBox, SIGNAL(toggled(bool)),
  310. this, SLOT(setSkipConfirmImportDirectory(bool)));
  311. connect(d->QueryRetrieveWidget, SIGNAL(canceled()), d->QueryRetrieveWidget, SLOT(hide()) );
  312. connect(d->QueryRetrieveWidget, SIGNAL(canceled()), this, SLOT(onQueryRetrieveFinished()) );
  313. }
  314. //----------------------------------------------------------------------------
  315. ctkDICOMBrowser::~ctkDICOMBrowser()
  316. {
  317. Q_D(ctkDICOMBrowser);
  318. d->QueryRetrieveWidget->deleteLater();
  319. d->ImportDialog->deleteLater();
  320. }
  321. //----------------------------------------------------------------------------
  322. bool ctkDICOMBrowser::displayImportSummary()
  323. {
  324. Q_D(ctkDICOMBrowser);
  325. return d->DisplayImportSummary;
  326. }
  327. //----------------------------------------------------------------------------
  328. void ctkDICOMBrowser::setDisplayImportSummary(bool onOff)
  329. {
  330. Q_D(ctkDICOMBrowser);
  331. d->DisplayImportSummary = onOff;
  332. }
  333. //----------------------------------------------------------------------------
  334. int ctkDICOMBrowser::patientsAddedDuringImport()
  335. {
  336. Q_D(ctkDICOMBrowser);
  337. return d->PatientsAddedDuringImport;
  338. }
  339. //----------------------------------------------------------------------------
  340. int ctkDICOMBrowser::studiesAddedDuringImport()
  341. {
  342. Q_D(ctkDICOMBrowser);
  343. return d->StudiesAddedDuringImport;
  344. }
  345. //----------------------------------------------------------------------------
  346. int ctkDICOMBrowser::seriesAddedDuringImport()
  347. {
  348. Q_D(ctkDICOMBrowser);
  349. return d->SeriesAddedDuringImport;
  350. }
  351. //----------------------------------------------------------------------------
  352. int ctkDICOMBrowser::instancesAddedDuringImport()
  353. {
  354. Q_D(ctkDICOMBrowser);
  355. return d->InstancesAddedDuringImport;
  356. }
  357. //----------------------------------------------------------------------------
  358. void ctkDICOMBrowser::updateDatabaseSchemaIfNeeded()
  359. {
  360. Q_D(ctkDICOMBrowser);
  361. d->showUpdateSchemaDialog();
  362. d->DICOMDatabase->updateSchemaIfNeeded();
  363. }
  364. //----------------------------------------------------------------------------
  365. void ctkDICOMBrowser::setDatabaseDirectory(const QString& directory)
  366. {
  367. Q_D(ctkDICOMBrowser);
  368. // If needed, create database directory
  369. if (!QDir(directory).exists())
  370. {
  371. QDir().mkdir(directory);
  372. }
  373. QSettings settings;
  374. settings.setValue(Self::databaseDirectorySettingsKey(), directory);
  375. settings.sync();
  376. //close the active DICOM database
  377. d->DICOMDatabase->closeDatabase();
  378. //open DICOM database on the directory
  379. QString databaseFileName = directory + QString("/ctkDICOM.sql");
  380. try
  381. {
  382. d->DICOMDatabase->openDatabase( databaseFileName );
  383. }
  384. catch (std::exception e)
  385. {
  386. std::cerr << "Database error: " << qPrintable(d->DICOMDatabase->lastError()) << "\n";
  387. d->DICOMDatabase->closeDatabase();
  388. return;
  389. }
  390. // update the database schema if needed and provide progress
  391. this->updateDatabaseSchemaIfNeeded();
  392. //pass DICOM database instance to Import widget
  393. d->QueryRetrieveWidget->setRetrieveDatabase(d->DICOMDatabase);
  394. // update the button and let any connected slots know about the change
  395. d->DirectoryButton->setDirectory(directory);
  396. d->dicomTableManager->updateTableViews();
  397. emit databaseDirectoryChanged(directory);
  398. }
  399. //----------------------------------------------------------------------------
  400. QString ctkDICOMBrowser::databaseDirectory() const
  401. {
  402. return QSettings().value(Self::databaseDirectorySettingsKey()).toString();
  403. }
  404. //------------------------------------------------------------------------------
  405. QString ctkDICOMBrowser::databaseDirectorySettingsKey()
  406. {
  407. return QLatin1String("DatabaseDirectory");
  408. }
  409. //------------------------------------------------------------------------------
  410. void ctkDICOMBrowser::setTagsToPrecache( const QStringList tags)
  411. {
  412. Q_D(ctkDICOMBrowser);
  413. d->DICOMDatabase->setTagsToPrecache(tags);
  414. }
  415. //------------------------------------------------------------------------------
  416. const QStringList ctkDICOMBrowser::tagsToPrecache()
  417. {
  418. Q_D(ctkDICOMBrowser);
  419. return d->DICOMDatabase->tagsToPrecache();
  420. }
  421. //----------------------------------------------------------------------------
  422. ctkDICOMDatabase* ctkDICOMBrowser::database(){
  423. Q_D(ctkDICOMBrowser);
  424. return d->DICOMDatabase.data();
  425. }
  426. //----------------------------------------------------------------------------
  427. ctkDICOMTableManager* ctkDICOMBrowser::dicomTableManager()
  428. {
  429. Q_D(ctkDICOMBrowser);
  430. return d->dicomTableManager;
  431. }
  432. //----------------------------------------------------------------------------
  433. void ctkDICOMBrowser::onFileIndexed(const QString& filePath)
  434. {
  435. Q_UNUSED(filePath);
  436. }
  437. //----------------------------------------------------------------------------
  438. void ctkDICOMBrowser::openImportDialog()
  439. {
  440. Q_D(ctkDICOMBrowser);
  441. d->ImportDialog->show();
  442. d->ImportDialog->raise();
  443. }
  444. //----------------------------------------------------------------------------
  445. void ctkDICOMBrowser::openExportDialog()
  446. {
  447. }
  448. //----------------------------------------------------------------------------
  449. void ctkDICOMBrowser::openQueryDialog()
  450. {
  451. Q_D(ctkDICOMBrowser);
  452. d->QueryRetrieveWidget->show();
  453. d->QueryRetrieveWidget->raise();
  454. }
  455. //----------------------------------------------------------------------------
  456. void ctkDICOMBrowser::onQueryRetrieveFinished()
  457. {
  458. emit this->queryRetrieveFinished();
  459. }
  460. //----------------------------------------------------------------------------
  461. void ctkDICOMBrowser::onRemoveAction()
  462. {
  463. Q_D(ctkDICOMBrowser);
  464. QStringList selectedSeriesUIDs = d->dicomTableManager->currentSeriesSelection();
  465. foreach (const QString& uid, selectedSeriesUIDs)
  466. {
  467. d->DICOMDatabase->removeSeries(uid);
  468. }
  469. QStringList selectedStudiesUIDs = d->dicomTableManager->currentStudiesSelection();
  470. foreach (const QString& uid, selectedStudiesUIDs)
  471. {
  472. d->DICOMDatabase->removeStudy(uid);
  473. }
  474. QStringList selectedPatientUIDs = d->dicomTableManager->currentPatientsSelection();
  475. foreach (const QString& uid, selectedPatientUIDs)
  476. {
  477. d->DICOMDatabase->removePatient(uid);
  478. }
  479. // Update the table views
  480. d->dicomTableManager->updateTableViews();
  481. }
  482. //----------------------------------------------------------------------------
  483. void ctkDICOMBrowser::onRepairAction()
  484. {
  485. Q_D(ctkDICOMBrowser);
  486. QMessageBox* repairMessageBox;
  487. repairMessageBox = new QMessageBox;
  488. repairMessageBox->setWindowTitle("Database Repair");
  489. QStringList allFiles(d->DICOMDatabase->allFiles());
  490. QSet<QString> corruptedSeries;
  491. QStringList::const_iterator it;
  492. for (it = allFiles.constBegin(); it!= allFiles.constEnd();++it)
  493. {
  494. QString fileName(*it);
  495. QFile dicomFile(fileName);
  496. if(!dicomFile.exists())
  497. {
  498. QString seriesUid = d->DICOMDatabase->seriesForFile(fileName);
  499. corruptedSeries.insert(seriesUid);
  500. }
  501. }
  502. if (corruptedSeries.size() == 0)
  503. {
  504. repairMessageBox->setText("All the files in the local database are available.");
  505. repairMessageBox->addButton(QMessageBox::Ok);
  506. repairMessageBox->exec();
  507. }
  508. else
  509. {
  510. repairMessageBox->addButton(QMessageBox::Yes);
  511. repairMessageBox->addButton(QMessageBox::No);
  512. QSet<QString>::iterator i;
  513. for (i = corruptedSeries.begin(); i != corruptedSeries.end(); ++i)
  514. {
  515. QStringList fileList (d->DICOMDatabase->filesForSeries(*i));
  516. QString unavailableFileNames;
  517. QStringList::const_iterator it;
  518. for (it= fileList.constBegin(); it!= fileList.constEnd();++it)
  519. {
  520. unavailableFileNames.append(*it+"\n");
  521. }
  522. QString firstFile (*(fileList.constBegin()));
  523. QHash<QString,QString> descriptions (d->DICOMDatabase->descriptionsForFile(firstFile));
  524. repairMessageBox->setText("The files for the following series are not available on the disk: \nPatient Name: "
  525. + descriptions["PatientsName"]+ "\n"+
  526. "Study Desciption: " + descriptions["StudyDescription"]+ "\n"+
  527. "Series Desciption: " + descriptions["SeriesDescription"]+ "\n"+
  528. "Do you want to remove the series from the DICOM database? ");
  529. repairMessageBox->setDetailedText(unavailableFileNames);
  530. int selection = repairMessageBox->exec();
  531. if (selection == QMessageBox::Yes)
  532. {
  533. d->DICOMDatabase->removeSeries(*i);
  534. d->dicomTableManager->updateTableViews();
  535. }
  536. }
  537. }
  538. }
  539. //----------------------------------------------------------------------------
  540. void ctkDICOMBrowser::onTablesDensityComboBox(QString density)
  541. {
  542. Q_D(ctkDICOMBrowser);
  543. if ( density == "Comfortable")
  544. {
  545. d->dicomTableManager->setDisplayDensity(ctkDICOMTableManager::Comfortable);
  546. }
  547. else if ( density == "Cozy")
  548. {
  549. d->dicomTableManager->setDisplayDensity(ctkDICOMTableManager::Cozy);
  550. }
  551. else if ( density == "Compact")
  552. {
  553. d->dicomTableManager->setDisplayDensity(ctkDICOMTableManager::Compact);
  554. }
  555. }
  556. //----------------------------------------------------------------------------
  557. void ctkDICOMBrowser::onPatientAdded(int databaseID, QString patientID, QString patientName, QString patientBirthDate )
  558. {
  559. Q_D(ctkDICOMBrowser);
  560. Q_UNUSED(databaseID);
  561. Q_UNUSED(patientID);
  562. Q_UNUSED(patientName);
  563. Q_UNUSED(patientBirthDate);
  564. ++d->PatientsAddedDuringImport;
  565. }
  566. //----------------------------------------------------------------------------
  567. void ctkDICOMBrowser::onStudyAdded(QString studyUID)
  568. {
  569. Q_D(ctkDICOMBrowser);
  570. Q_UNUSED(studyUID);
  571. ++d->StudiesAddedDuringImport;
  572. }
  573. //----------------------------------------------------------------------------
  574. void ctkDICOMBrowser::onSeriesAdded(QString seriesUID)
  575. {
  576. Q_D(ctkDICOMBrowser);
  577. Q_UNUSED(seriesUID);
  578. ++d->SeriesAddedDuringImport;
  579. }
  580. //----------------------------------------------------------------------------
  581. void ctkDICOMBrowser::onInstanceAdded(QString instanceUID)
  582. {
  583. Q_D(ctkDICOMBrowser);
  584. Q_UNUSED(instanceUID);
  585. ++d->InstancesAddedDuringImport;
  586. }
  587. //----------------------------------------------------------------------------
  588. void ctkDICOMBrowser::onImportDirectoriesSelected(QStringList directories)
  589. {
  590. Q_D(ctkDICOMBrowser);
  591. if (!d->confirmDirectoryImport())
  592. {
  593. return;
  594. }
  595. this->importDirectories(directories, this->importDirectoryMode(), /* confirm= */ false);
  596. // Clear selection
  597. d->ImportDialog->clearSelection();
  598. }
  599. //----------------------------------------------------------------------------
  600. void ctkDICOMBrowser::onImportDirectoryComboBoxCurrentIndexChanged(int index)
  601. {
  602. Q_D(ctkDICOMBrowser);
  603. Q_UNUSED(index);
  604. this->setImportDirectoryMode(this->importDirectoryMode());
  605. }
  606. //----------------------------------------------------------------------------
  607. void ctkDICOMBrowser::importDirectories(QStringList directories, ctkDICOMBrowser::ImportDirectoryMode mode, bool confirm)
  608. {
  609. Q_D(ctkDICOMBrowser);
  610. if(confirm && !d->confirmDirectoryImport())
  611. {
  612. return;
  613. }
  614. ctkDICOMImportStats stats(d);
  615. foreach (const QString& directory, directories)
  616. {
  617. d->importDirectory(directory, mode);
  618. }
  619. if (d->DisplayImportSummary)
  620. {
  621. QMessageBox::information(this,"DICOM Directory Import", stats.summary());
  622. }
  623. }
  624. //----------------------------------------------------------------------------
  625. void ctkDICOMBrowser::importDirectory(QString directory, ctkDICOMBrowser::ImportDirectoryMode mode, bool confirm)
  626. {
  627. Q_D(ctkDICOMBrowser);
  628. if(confirm && !d->confirmDirectoryImport())
  629. {
  630. return;
  631. }
  632. ctkDICOMImportStats stats(d);
  633. d->importDirectory(directory, mode);
  634. if (d->DisplayImportSummary)
  635. {
  636. QMessageBox::information(this,"DICOM Directory Import", stats.summary());
  637. }
  638. }
  639. //----------------------------------------------------------------------------
  640. void ctkDICOMBrowser::onImportDirectory(QString directory, ctkDICOMBrowser::ImportDirectoryMode mode, bool confirm)
  641. {
  642. this->importDirectory(directory, mode, confirm);
  643. }
  644. //----------------------------------------------------------------------------
  645. void ctkDICOMBrowserPrivate::importDirectory(QString directory, ctkDICOMBrowser::ImportDirectoryMode mode)
  646. {
  647. if (!QDir(directory).exists())
  648. {
  649. return;
  650. }
  651. QString targetDirectory;
  652. if (mode == ctkDICOMBrowser::ImportDirectoryCopy)
  653. {
  654. targetDirectory = this->DICOMDatabase->databaseDirectory();
  655. }
  656. // show progress dialog and perform indexing
  657. this->showIndexerDialog();
  658. this->DICOMIndexer->addDirectory(*this->DICOMDatabase, directory, targetDirectory);
  659. }
  660. //----------------------------------------------------------------------------
  661. void ctkDICOMBrowserPrivate::importOldSettings()
  662. {
  663. Q_Q(ctkDICOMBrowser);
  664. // Backward compatibility
  665. QSettings settings;
  666. int dontConfirmCopyOnImport = settings.value("MainWindow/DontConfirmCopyOnImport", static_cast<int>(QMessageBox::InvalidRole)).toInt();
  667. if (dontConfirmCopyOnImport == QMessageBox::AcceptRole)
  668. {
  669. q->setSkipConfirmImportDirectory(true);
  670. // settings.setValue("DICOM/DontConfirmImportDirectory", QMessageBox::AcceptRole);
  671. settings.setValue("DICOM/ImportDirectoryMode", static_cast<int>(ctkDICOMBrowser::ImportDirectoryCopy));
  672. settings.remove("MainWindow/DontConfirmCopyOnImport");
  673. }
  674. }
  675. //----------------------------------------------------------------------------
  676. bool ctkDICOMBrowser::skipConfirmImportDirectory()const
  677. {
  678. Q_D(const ctkDICOMBrowser);
  679. ctkDICOMBrowserPrivate* mutable_d =
  680. const_cast<ctkDICOMBrowserPrivate*>(d);
  681. mutable_d->importOldSettings();
  682. QSettings settings;
  683. return settings.value(
  684. "DICOM/DontConfirmImportDirectory",
  685. static_cast<int>(QMessageBox::InvalidRole)).toInt() == QMessageBox::AcceptRole;
  686. }
  687. //----------------------------------------------------------------------------
  688. void ctkDICOMBrowser::setSkipConfirmImportDirectory(bool value)
  689. {
  690. Q_D(ctkDICOMBrowser);
  691. QSettings settings;
  692. settings.setValue("DICOM/DontConfirmImportDirectory", value ? QMessageBox::AcceptRole : QMessageBox::RejectRole);
  693. if (!d->ImportDialog)
  694. {
  695. return;
  696. }
  697. QCheckBox* checkBox = d->ImportDialog->bottomWidget()->findChild<QCheckBox*>();
  698. checkBox->setChecked(value);
  699. }
  700. //----------------------------------------------------------------------------
  701. ctkDICOMBrowser::ImportDirectoryMode ctkDICOMBrowser::importDirectoryMode()const
  702. {
  703. Q_D(const ctkDICOMBrowser);
  704. ctkDICOMBrowserPrivate* mutable_d =
  705. const_cast<ctkDICOMBrowserPrivate*>(d);
  706. mutable_d->importOldSettings();
  707. QSettings settings;
  708. return static_cast<ctkDICOMBrowser::ImportDirectoryMode>(settings.value(
  709. "DICOM/ImportDirectoryMode", static_cast<int>(ctkDICOMBrowser::ImportDirectoryAddLink)).toInt());
  710. }
  711. //----------------------------------------------------------------------------
  712. void ctkDICOMBrowser::setImportDirectoryMode(ctkDICOMBrowser::ImportDirectoryMode mode)
  713. {
  714. Q_D(ctkDICOMBrowser);
  715. QSettings settings;
  716. settings.setValue("DICOM/ImportDirectoryMode", static_cast<int>(mode));
  717. if (!d->ImportDialog)
  718. {
  719. return;
  720. }
  721. QComboBox* comboBox = d->ImportDialog->bottomWidget()->findChild<QComboBox*>();
  722. comboBox->setCurrentIndex(comboBox->findData(mode));
  723. }
  724. //----------------------------------------------------------------------------
  725. void ctkDICOMBrowser::onModelSelected(const QItemSelection &item1, const QItemSelection &item2)
  726. {
  727. Q_UNUSED(item1);
  728. Q_UNUSED(item2);
  729. Q_D(ctkDICOMBrowser);
  730. d->ActionRemove->setEnabled(true);
  731. }
  732. //----------------------------------------------------------------------------
  733. bool ctkDICOMBrowser::confirmDeleteSelectedUIDs(QStringList uids)
  734. {
  735. Q_D(ctkDICOMBrowser);
  736. if (uids.isEmpty())
  737. {
  738. return false;
  739. }
  740. ctkMessageBox confirmDeleteDialog;
  741. QString message("Do you want to delete the following selected items?");
  742. // add the information about the selected UIDs
  743. int numUIDs = uids.size();
  744. for (int i = 0; i < numUIDs; ++i)
  745. {
  746. QString uid = uids.at(i);
  747. // try using the given UID to find a descriptive string
  748. QString patientName = d->DICOMDatabase->nameForPatient(uid);
  749. QString studyDescription = d->DICOMDatabase->descriptionForStudy(uid);
  750. QString seriesDescription = d->DICOMDatabase->descriptionForSeries(uid);
  751. if (!patientName.isEmpty())
  752. {
  753. message += QString("\n") + patientName;
  754. }
  755. else if (!studyDescription.isEmpty())
  756. {
  757. message += QString("\n") + studyDescription;
  758. }
  759. else if (!seriesDescription.isEmpty())
  760. {
  761. message += QString("\n") + seriesDescription;
  762. }
  763. else
  764. {
  765. // if all other descriptors are empty, use the UID
  766. message += QString("\n") + uid;
  767. }
  768. }
  769. confirmDeleteDialog.setText(message);
  770. confirmDeleteDialog.setIcon(QMessageBox::Question);
  771. confirmDeleteDialog.addButton("Delete", QMessageBox::AcceptRole);
  772. confirmDeleteDialog.addButton("Cancel", QMessageBox::RejectRole);
  773. confirmDeleteDialog.setDontShowAgainSettingsKey( "MainWindow/DontConfirmDeleteSelected");
  774. int response = confirmDeleteDialog.exec();
  775. if (response == QMessageBox::AcceptRole)
  776. {
  777. return true;
  778. }
  779. else
  780. {
  781. return false;
  782. }
  783. }
  784. //----------------------------------------------------------------------------
  785. void ctkDICOMBrowser::onPatientsRightClicked(const QPoint &point)
  786. {
  787. Q_D(ctkDICOMBrowser);
  788. // get the list of patients that are selected
  789. QStringList selectedPatientsUIDs = d->dicomTableManager->currentPatientsSelection();
  790. int numPatients = selectedPatientsUIDs.size();
  791. if (numPatients == 0)
  792. {
  793. qDebug() << "No patients selected!";
  794. return;
  795. }
  796. QMenu *patientsMenu = new QMenu(d->dicomTableManager);
  797. QString deleteString = QString("Delete ")
  798. + QString::number(numPatients)
  799. + QString(" selected patients");
  800. QAction *deleteAction = new QAction(deleteString, patientsMenu);
  801. patientsMenu->addAction(deleteAction);
  802. QString exportString = QString("Export ")
  803. + QString::number(numPatients)
  804. + QString(" selected patients to file system");
  805. QAction *exportAction = new QAction(exportString, patientsMenu);
  806. patientsMenu->addAction(exportAction);
  807. // the table took care of mapping it to a global position so that the
  808. // menu will pop up at the correct place over this table.
  809. QAction *selectedAction = patientsMenu->exec(point);
  810. if (selectedAction == deleteAction
  811. && this->confirmDeleteSelectedUIDs(selectedPatientsUIDs))
  812. {
  813. qDebug() << "Deleting " << numPatients << " patients";
  814. foreach (const QString& uid, selectedPatientsUIDs)
  815. {
  816. d->DICOMDatabase->removePatient(uid);
  817. d->dicomTableManager->updateTableViews();
  818. }
  819. }
  820. else if (selectedAction == exportAction)
  821. {
  822. ctkFileDialog* directoryDialog = new ctkFileDialog();
  823. directoryDialog->setOption(QFileDialog::DontUseNativeDialog);
  824. directoryDialog->setOption(QFileDialog::ShowDirsOnly);
  825. directoryDialog->setFileMode(QFileDialog::DirectoryOnly);
  826. bool res = directoryDialog->exec();
  827. if (res)
  828. {
  829. QStringList dirs = directoryDialog->selectedFiles();
  830. QString dirPath = dirs[0];
  831. this->exportSelectedPatients(dirPath, selectedPatientsUIDs);
  832. }
  833. delete directoryDialog;
  834. }
  835. }
  836. //----------------------------------------------------------------------------
  837. void ctkDICOMBrowser::onStudiesRightClicked(const QPoint &point)
  838. {
  839. Q_D(ctkDICOMBrowser);
  840. // get the list of studies that are selected
  841. QStringList selectedStudiesUIDs = d->dicomTableManager->currentStudiesSelection();
  842. int numStudies = selectedStudiesUIDs.size();
  843. if (numStudies == 0)
  844. {
  845. qDebug() << "No studies selected!";
  846. return;
  847. }
  848. QMenu *studiesMenu = new QMenu(d->dicomTableManager);
  849. QString deleteString = QString("Delete ")
  850. + QString::number(numStudies)
  851. + QString(" selected studies");
  852. QAction *deleteAction = new QAction(deleteString, studiesMenu);
  853. studiesMenu->addAction(deleteAction);
  854. QString exportString = QString("Export ")
  855. + QString::number(numStudies)
  856. + QString(" selected studies to file system");
  857. QAction *exportAction = new QAction(exportString, studiesMenu);
  858. studiesMenu->addAction(exportAction);
  859. // the table took care of mapping it to a global position so that the
  860. // menu will pop up at the correct place over this table.
  861. QAction *selectedAction = studiesMenu->exec(point);
  862. if (selectedAction == deleteAction
  863. && this->confirmDeleteSelectedUIDs(selectedStudiesUIDs))
  864. {
  865. foreach (const QString& uid, selectedStudiesUIDs)
  866. {
  867. d->DICOMDatabase->removeStudy(uid);
  868. d->dicomTableManager->updateTableViews();
  869. }
  870. }
  871. else if (selectedAction == exportAction)
  872. {
  873. ctkFileDialog* directoryDialog = new ctkFileDialog();
  874. directoryDialog->setOption(QFileDialog::DontUseNativeDialog);
  875. directoryDialog->setOption(QFileDialog::ShowDirsOnly);
  876. directoryDialog->setFileMode(QFileDialog::DirectoryOnly);
  877. bool res = directoryDialog->exec();
  878. if (res)
  879. {
  880. QStringList dirs = directoryDialog->selectedFiles();
  881. QString dirPath = dirs[0];
  882. this->exportSelectedStudies(dirPath, selectedStudiesUIDs);
  883. }
  884. delete directoryDialog;
  885. }
  886. }
  887. //----------------------------------------------------------------------------
  888. void ctkDICOMBrowser::onSeriesRightClicked(const QPoint &point)
  889. {
  890. Q_D(ctkDICOMBrowser);
  891. // get the list of series that are selected
  892. QStringList selectedSeriesUIDs = d->dicomTableManager->currentSeriesSelection();
  893. int numSeries = selectedSeriesUIDs.size();
  894. if (numSeries == 0)
  895. {
  896. qDebug() << "No series selected!";
  897. return;
  898. }
  899. QMenu *seriesMenu = new QMenu(d->dicomTableManager);
  900. QString deleteString = QString("Delete ")
  901. + QString::number(numSeries)
  902. + QString(" selected series");
  903. QAction *deleteAction = new QAction(deleteString, seriesMenu);
  904. seriesMenu->addAction(deleteAction);
  905. QString exportString = QString("Export ")
  906. + QString::number(numSeries)
  907. + QString(" selected series to file system");
  908. QAction *exportAction = new QAction(exportString, seriesMenu);
  909. seriesMenu->addAction(exportAction);
  910. // the table took care of mapping it to a global position so that the
  911. // menu will pop up at the correct place over this table.
  912. QAction *selectedAction = seriesMenu->exec(point);
  913. if (selectedAction == deleteAction
  914. && this->confirmDeleteSelectedUIDs(selectedSeriesUIDs))
  915. {
  916. foreach (const QString& uid, selectedSeriesUIDs)
  917. {
  918. d->DICOMDatabase->removeSeries(uid);
  919. d->dicomTableManager->updateTableViews();
  920. }
  921. }
  922. else if (selectedAction == exportAction)
  923. {
  924. ctkFileDialog* directoryDialog = new ctkFileDialog();
  925. directoryDialog->setOption(QFileDialog::DontUseNativeDialog);
  926. directoryDialog->setOption(QFileDialog::ShowDirsOnly);
  927. directoryDialog->setFileMode(QFileDialog::DirectoryOnly);
  928. bool res = directoryDialog->exec();
  929. if (res)
  930. {
  931. QStringList dirs = directoryDialog->selectedFiles();
  932. QString dirPath = dirs[0];
  933. this->exportSelectedSeries(dirPath, selectedSeriesUIDs);
  934. }
  935. delete directoryDialog;
  936. }
  937. }
  938. //----------------------------------------------------------------------------
  939. void ctkDICOMBrowser::exportSelectedSeries(QString dirPath, QStringList uids)
  940. {
  941. Q_D(ctkDICOMBrowser);
  942. foreach (const QString& uid, uids)
  943. {
  944. QStringList filesForSeries = d->DICOMDatabase->filesForSeries(uid);
  945. // Use the first file to get the overall series information
  946. QString firstFilePath = filesForSeries[0];
  947. QHash<QString,QString> descriptions (d->DICOMDatabase->descriptionsForFile(firstFilePath));
  948. QString patientName = descriptions["PatientsName"];
  949. QString patientIDTag = QString("0010,0020");
  950. QString patientID = d->DICOMDatabase->fileValue(firstFilePath, patientIDTag);
  951. QString studyDescription = descriptions["StudyDescription"];
  952. QString seriesDescription = descriptions["SeriesDescription"];
  953. QString studyDateTag = QString("0008,0020");
  954. QString studyDate = d->DICOMDatabase->fileValue(firstFilePath,studyDateTag);
  955. QString seriesNumberTag = QString("0020,0011");
  956. QString seriesNumber = d->DICOMDatabase->fileValue(firstFilePath,seriesNumberTag);
  957. QString sep = "/";
  958. QString nameSep = "-";
  959. QString destinationDir = dirPath + sep + patientID;
  960. if (!patientName.isEmpty())
  961. {
  962. destinationDir += nameSep + patientName;
  963. }
  964. destinationDir += sep + studyDate;
  965. if (!studyDescription.isEmpty())
  966. {
  967. destinationDir += nameSep + studyDescription;
  968. }
  969. destinationDir += sep + seriesNumber;
  970. if (!seriesDescription.isEmpty())
  971. {
  972. destinationDir += nameSep + seriesDescription;
  973. }
  974. destinationDir += sep;
  975. // make sure only ascii characters are in the directory path
  976. destinationDir = destinationDir.toLatin1();
  977. // replace any question marks that were used as replacements for non ascii
  978. // characters with underscore
  979. destinationDir.replace("?", "_");
  980. // create the destination directory if necessary
  981. if (!QDir().exists(destinationDir))
  982. {
  983. if (!QDir().mkpath(destinationDir))
  984. {
  985. QString errorString =
  986. QString("Unable to create export destination directory:\n\n")
  987. + destinationDir
  988. + QString("\n\nHalting export.");
  989. ctkMessageBox createDirectoryErrorMessageBox;
  990. createDirectoryErrorMessageBox.setText(errorString);
  991. createDirectoryErrorMessageBox.setIcon(QMessageBox::Warning);
  992. createDirectoryErrorMessageBox.exec();
  993. return;
  994. }
  995. }
  996. // show progress
  997. if (d->ExportProgress == 0)
  998. {
  999. d->ExportProgress = new QProgressDialog(this->tr("DICOM Export"), "Close", 0, 100, this, Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
  1000. d->ExportProgress->setWindowModality(Qt::ApplicationModal);
  1001. d->ExportProgress->setMinimumDuration(0);
  1002. }
  1003. QLabel *exportLabel = new QLabel(this->tr("Exporting series ") + seriesNumber);
  1004. d->ExportProgress->setLabel(exportLabel);
  1005. d->ExportProgress->setValue(0);
  1006. int fileNumber = 0;
  1007. int numFiles = filesForSeries.size();
  1008. d->ExportProgress->setMaximum(numFiles);
  1009. foreach (const QString& filePath, filesForSeries)
  1010. {
  1011. QString destinationFileName = destinationDir;
  1012. QString fileNumberString;
  1013. // sequentially number the files
  1014. fileNumberString.sprintf("%06d", fileNumber);
  1015. destinationFileName += fileNumberString + QString(".dcm");
  1016. // replace non ASCII characters
  1017. destinationFileName = destinationFileName.toLatin1();
  1018. // replace any question marks that were used as replacements for non ascii
  1019. // characters with underscore
  1020. destinationFileName.replace("?", "_");
  1021. if (!QFile::exists(filePath))
  1022. {
  1023. d->ExportProgress->setValue(numFiles);
  1024. QString errorString = QString("Export source file not found:\n\n")
  1025. + filePath
  1026. + QString("\n\nHalting export.\n\nError may be fixed via Repair.");
  1027. ctkMessageBox copyErrorMessageBox;
  1028. copyErrorMessageBox.setText(errorString);
  1029. copyErrorMessageBox.setIcon(QMessageBox::Warning);
  1030. copyErrorMessageBox.exec();
  1031. return;
  1032. }
  1033. if (QFile::exists(destinationFileName))
  1034. {
  1035. d->ExportProgress->setValue(numFiles);
  1036. QString errorString = QString("Export destination file already exists:\n\n")
  1037. + destinationFileName
  1038. + QString("\n\nHalting export.");
  1039. ctkMessageBox copyErrorMessageBox;
  1040. copyErrorMessageBox.setText(errorString);
  1041. copyErrorMessageBox.setIcon(QMessageBox::Warning);
  1042. copyErrorMessageBox.exec();
  1043. return;
  1044. }
  1045. bool copyResult = QFile::copy(filePath, destinationFileName);
  1046. if (!copyResult)
  1047. {
  1048. d->ExportProgress->setValue(numFiles);
  1049. QString errorString = QString("Failed to copy\n\n")
  1050. + filePath
  1051. + QString("\n\nto\n\n")
  1052. + destinationFileName
  1053. + QString("\n\nHalting export.");
  1054. ctkMessageBox copyErrorMessageBox;
  1055. copyErrorMessageBox.setText(errorString);
  1056. copyErrorMessageBox.setIcon(QMessageBox::Warning);
  1057. copyErrorMessageBox.exec();
  1058. return;
  1059. }
  1060. fileNumber++;
  1061. d->ExportProgress->setValue(fileNumber);
  1062. }
  1063. d->ExportProgress->setValue(numFiles);
  1064. }
  1065. }
  1066. //----------------------------------------------------------------------------
  1067. void ctkDICOMBrowser::exportSelectedStudies(QString dirPath, QStringList uids)
  1068. {
  1069. Q_D(ctkDICOMBrowser);
  1070. foreach (const QString& uid, uids)
  1071. {
  1072. QStringList seriesUIDs = d->DICOMDatabase->seriesForStudy(uid);
  1073. this->exportSelectedSeries(dirPath, seriesUIDs);
  1074. }
  1075. }
  1076. //----------------------------------------------------------------------------
  1077. void ctkDICOMBrowser::exportSelectedPatients(QString dirPath, QStringList uids)
  1078. {
  1079. Q_D(ctkDICOMBrowser);
  1080. foreach (const QString& uid, uids)
  1081. {
  1082. QStringList studiesUIDs = d->DICOMDatabase->studiesForPatient(uid);
  1083. this->exportSelectedStudies(dirPath, studiesUIDs);
  1084. }
  1085. }