ctkDICOMBrowser.cpp 40 KB

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