CustusX  15.3.4-beta
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxDICOMAppWidget.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Library: CTK
4 
5  Copyright (c) Kitware Inc.
6 
7  Licensed under the Apache License, Version 2.0 (the "License");
8  you may not use this file except in compliance with the License.
9  You may obtain a copy of the License at
10 
11  http://www.apache.org/licenses/LICENSE-2.0.txt
12 
13  Unless required by applicable law or agreed to in writing, software
14  distributed under the License is distributed on an "AS IS" BASIS,
15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  See the License for the specific language governing permissions and
17  limitations under the License.
18 
19 =========================================================================*/
20 
21 // std includes
22 #include <iostream>
23 
24 #include <dcmimage.h>
25 
26 // Qt includes
27 #include <QAction>
28 #include <QCoreApplication>
29 #include <QCheckBox>
30 #include <QDebug>
31 #include <QMessageBox>
32 #include <QMetaType>
33 #include <QModelIndex>
34 #include <QPersistentModelIndex>
35 #include <QProgressDialog>
36 #include <QSettings>
37 #include <QSlider>
38 #include <QTabBar>
39 #include <QTimer>
40 #include <QTreeView>
41 #include <QLabel>
42 #include <QHBoxLayout>
43 #include <QSplitter>
44 
45 // ctkWidgets includes
46 #include "ctkDirectoryButton.h"
47 #include "ctkFileDialog.h"
48 
49 // ctkDICOMCore includes
50 #include "ctkDICOMDatabase.h"
51 #include "ctkDICOMFilterProxyModel.h"
52 #include "ctkDICOMIndexer.h"
53 #include "cxDICOMModel.h"
54 
55 // ctkDICOMWidgets includes
56 #include "cxDICOMAppWidget.h"
58 #include "ctkThumbnailLabel.h"
59 #include "ctkDICOMQueryResultsTabWidget.h"
60 #include "ctkDICOMQueryRetrieveWidget.h"
61 #include "ctkDICOMQueryWidget.h"
62 #include <QToolBar>
63 //#include "ctkDICOMThumbnailListWidget.h"
65 
66 
67 #include "cxDicomImporter.h"
68 #include "cxLogger.h"
69 
70 //#include "ui_DICOMAppWidget.h"
71 
72 //logger
73 #include <ctkLogger.h>
74 static ctkLogger logger("org.commontk.DICOM.Widgets.DICOMAppWidget");
75 
76 Q_DECLARE_METATYPE(QPersistentModelIndex);
77 
78 
79 namespace cx
80 {
82 
83 //----------------------------------------------------------------------------
84 class DICOMAppWidgetPrivate: public QWidget
85 {
86 public:
89 
92  void setupUi(DICOMAppWidget* parent);
93 
94  QVBoxLayout* TopLayout;
95  QTreeView* TreeView;
96  QToolBar* ToolBar;
97 // ctkDICOMThumbnailListWidget* ThumbnailsWidget;
100  QAction* ActionImport;
101  QAction* ActionQuery;
102  QAction* ActionRemove;
104 
105  ctkDICOMQueryRetrieveWidget* QueryRetrieveWidget;
106 
107  QSharedPointer<ctkDICOMDatabase> DICOMDatabase;
108  QSharedPointer<ctkDICOMThumbnailGenerator> ThumbnailGenerator;
110  ctkDICOMFilterProxyModel DICOMProxyModel;
111  QProgressDialog *UpdateSchemaProgress;
112 
113  void showUpdateSchemaDialog();
114  std::map<ctkDICOMModel::IndexType, QStringList> getSelection() const;
115 
116  void removeSelection();
117 };
118 
119 //----------------------------------------------------------------------------
120 // DICOMAppWidgetPrivate methods
121 
123 {
124  DICOMDatabase = QSharedPointer<ctkDICOMDatabase> (new ctkDICOMDatabase);
125  ThumbnailGenerator = QSharedPointer <ctkDICOMThumbnailGenerator> (new ctkDICOMThumbnailGenerator);
126  DICOMDatabase->setThumbnailGenerator(ThumbnailGenerator.data());
128 }
129 
131 {
132  if ( UpdateSchemaProgress )
133  {
134  delete UpdateSchemaProgress;
135  }
136 }
137 
139 {
140  Q_Q(DICOMAppWidget);
141 
142  QHBoxLayout* layout = new QHBoxLayout(parent);
143  layout->setMargin(0);
144  layout->addWidget(this);
145 
146  TopLayout = new QVBoxLayout(this);
147 
148  ToolBar = new QToolBar;
149  TopLayout->addWidget(ToolBar);
150 
151  ActionImport = new QAction("Open", this);
152  ActionImport->setToolTip("Open and load a DICOM file or folder");
153  q->connect(ActionImport, SIGNAL(triggered()), &Importer, SLOT(openImportDialog()));
154  ToolBar->addAction(ActionImport);
155 
156  ActionQuery = new QAction("Query", this);
157  ActionQuery->setToolTip("Query and Retrieve DICOM studies from a DICOM node");
158  q->connect(ActionQuery, SIGNAL(triggered()), q, SLOT(openQueryDialog()));
159  ToolBar->addAction(ActionQuery);
160 
161  ActionRemove = new QAction("Remove", this);
162  ActionRemove->setToolTip("Remove selection from database");
163  q->connect(ActionRemove, SIGNAL(triggered()), q, SLOT(onRemoveAction()));
164  ToolBar->addAction(ActionRemove);
165 
166  QSplitter* splitter = new QSplitter;
167  splitter->setOrientation(Qt::Vertical);
168  TopLayout->addWidget(splitter);
169 
170  TreeView = new QTreeView;
171  TreeView->setAlternatingRowColors(true);
172  splitter->addWidget(TreeView);
173 
174  QWidget* ThumbnailsFullWidget = new QWidget;
175  splitter->addWidget(ThumbnailsFullWidget);
176  QVBoxLayout* ThumbnailsFullWidgetLayout = new QVBoxLayout(ThumbnailsFullWidget);
177  ThumbnailsFullWidgetLayout->setMargin(0);
178 
180  ThumbnailsWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
181  ThumbnailsWidget->setMinimumSize(QSize(0,200));
182  ThumbnailsFullWidgetLayout->addWidget(ThumbnailsWidget);
183 
184  ThumbnailWidthSlider = new QSlider;
185  ThumbnailWidthSlider->setMinimum(64);
186  ThumbnailWidthSlider->setMaximum(256);
187  ThumbnailWidthSlider->setValue(64);
188  ThumbnailWidthSlider->setOrientation(Qt::Horizontal);
189  connect(ThumbnailWidthSlider, SIGNAL(valueChanged(int)), q, SLOT(onThumbnailWidthSliderValueChanged(int)));
190  ThumbnailsFullWidgetLayout->addWidget(ThumbnailWidthSlider);
191 }
192 
194 {
195  Q_Q(DICOMAppWidget);
196 
197  ctkDICOMDatabase* db = DICOMDatabase.data();
198  q->connect(db, SIGNAL(schemaUpdateStarted(int)), q, SLOT(schemaUpdateStarted(int)));
199  q->connect(db, SIGNAL(schemaUpdateProgress(int)), q, SLOT(schemaUpdateProgress(int)));
200  q->connect(db, SIGNAL(schemaUpdateProgress(QString)), q, SLOT(schemaUpdateProgress(QString)));
201  q->connect(db, SIGNAL(schemaUpdated()), q, SLOT(schemaUpdated()));
202 
203 // if (UpdateSchemaProgress == 0)
204 // {
205 // //
206 // // Set up the Update Schema Progress Dialog
207 // //
208 // UpdateSchemaProgress = new QProgressDialog(
209 // q->tr("DICOM Schema Update"), "Cancel", 0, 100, q,
210 // Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
211 
212 // // We don't want the progress dialog to resize itself, so we bypass the label
213 // // by creating our own
214 // QLabel* progressLabel = new QLabel(q->tr("Initialization..."));
215 // UpdateSchemaProgress->setLabel(progressLabel);
216 // UpdateSchemaProgress->setWindowModality(Qt::ApplicationModal);
217 // UpdateSchemaProgress->setMinimumDuration(0);
218 // UpdateSchemaProgress->setValue(0);
219 
220 // //q->connect(UpdateSchemaProgress, SIGNAL(canceled()),
221 // // DICOMIndexer.data(), SLOT(cancel()));
222 // ctkDICOMDatabase* db = DICOMDatabase.data();
223 
224 // q->connect(db, SIGNAL(schemaUpdateStarted(int)), UpdateSchemaProgress, SLOT(setMaximum(int)));
225 // q->connect(db, SIGNAL(schemaUpdateProgress(int)), UpdateSchemaProgress, SLOT(setValue(int)));
226 // q->connect(db, SIGNAL(schemaUpdateProgress(QString)), progressLabel, SLOT(setText(QString)));
227 // // close the dialog
229 // // reset the database to show new data
230 // q->connect(db, SIGNAL(schemaUpdated()), &mDICOMModel, SLOT(reset()));
231 // // reset the database if canceled
232 // q->connect(UpdateSchemaProgress, SIGNAL(canceled()), &mDICOMModel, SLOT(reset()));
233 
234 
235 // q->connect(db, SIGNAL(schemaUpdateStarted(int)), q, SLOT(schemaUpdateStarted(int)));
236 // q->connect(db, SIGNAL(schemaUpdateProgress(int)), q, SLOT(schemaUpdateProgress(int)));
237 // q->connect(db, SIGNAL(schemaUpdateProgress(QString)), q, SLOT(schemaUpdateProgress(QString)));
238 // q->connect(db, SIGNAL(schemaUpdated()), q, SLOT(schemaUpdated()));
239 // }
240 // UpdateSchemaProgress->show();
241 }
242 
243 void DICOMAppWidget::schemaUpdateStarted(int)
244 {
245 // report("DICOM schema update started...");
246 }
247 void DICOMAppWidget::schemaUpdateProgress(QString val)
248 {
249 }
250 void DICOMAppWidget::schemaUpdateProgress(int val)
251 {
252 }
253 void DICOMAppWidget::schemaUpdated()
254 {
255 // report("DICOM schema updated");
256 }
257 
259 {
260  QModelIndexList selection = TreeView->selectionModel()->selectedIndexes();
261  QModelIndex index;
262 
263  foreach(index,selection)
264  {
265  if (index.column()!=0)
266  continue;
267  mDICOMModel.removeRows(index.row(), 1, index.parent());
268 // this->remove(index);
269  }
270 }
271 
272 std::map<ctkDICOMModel::IndexType, QStringList> DICOMAppWidgetPrivate::getSelection() const
273 {
274 // Q_Q(DICOMAppWidget);
275  std::map<ctkDICOMModel::IndexType, QStringList> retval;
276  QModelIndexList selection = TreeView->selectionModel()->selectedIndexes();
277  QModelIndex index;
278 
279  foreach(index,selection)
280  {
281  QModelIndex index0 = index.sibling(index.row(), 0);
283  QString uid = mDICOMModel.data(index0,ctkDICOMModel::UIDRole).toString();
284 
285  if (!retval.count(type))
286  retval[type] << uid;
287  }
288 
289  return retval;
290 }
291 
292 
293 //----------------------------------------------------------------------------
294 // DICOMAppWidget methods
295 
296 //----------------------------------------------------------------------------
297 DICOMAppWidget::DICOMAppWidget(QWidget* _parent):Superclass(_parent),
298  d_ptr(new DICOMAppWidgetPrivate(this))
299 {
300  Q_D(DICOMAppWidget);
301 
302  d->setupUi(this);
303 
304  connect(&d->Importer, SIGNAL(indexingCompleted()),
305  &d->mDICOMModel, SLOT(reset()));
306  connect(&d->Importer, SIGNAL(directoryImported()),
307  this, SIGNAL(directoryImported()));
308 
309  //Enable sorting in tree view
310  d->TreeView->setSortingEnabled(true);
311  d->TreeView->setSelectionBehavior(QAbstractItemView::SelectRows);
312  d->DICOMProxyModel.setSourceModel(&d->mDICOMModel);
313  d->TreeView->setModel(&d->mDICOMModel);
314 
315  d->ThumbnailsWidget->setThumbnailSize(
316  QSize(d->ThumbnailWidthSlider->value(), d->ThumbnailWidthSlider->value()));
317 
318  // Treeview signals
319  connect(d->TreeView, SIGNAL(collapsed(QModelIndex)), this, SLOT(onTreeCollapsed(QModelIndex)));
320  connect(d->TreeView, SIGNAL(expanded(QModelIndex)), this, SLOT(onTreeExpanded(QModelIndex)));
321 
322  //Set ToolBar button style
323  d->ToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
324 
325  //Initialize Q/R widget
326  d->QueryRetrieveWidget = new ctkDICOMQueryRetrieveWidget();
327  d->QueryRetrieveWidget->setWindowModality ( Qt::ApplicationModal );
328 
329  connect(d->TreeView->selectionModel(),
330  SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
331  this,
332  SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
333  connect(d->TreeView->selectionModel(),
334  SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
335  this,
336  SLOT(onCurrentChanged(const QModelIndex&, const QModelIndex&)));
337 
338  //connect signal and slots
339  connect(d->TreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(onModelSelected(QModelIndex)));
340 
341  connect(d->QueryRetrieveWidget, SIGNAL(canceled()), d->QueryRetrieveWidget, SLOT(hide()) );
342  connect(d->QueryRetrieveWidget, SIGNAL(canceled()), this, SLOT(onQueryRetrieveFinished()) );
343 }
344 
345 //----------------------------------------------------------------------------
347 {
348  Q_D(DICOMAppWidget);
349  d->QueryRetrieveWidget->deleteLater();
350 }
351 
352 //----------------------------------------------------------------------------
354 {
355  Q_D(DICOMAppWidget);
356  return d->Importer.displayImportSummary();
357 }
358 
359 //----------------------------------------------------------------------------
361 {
362  Q_D(DICOMAppWidget);
363  return d->Importer.setDisplayImportSummary(onOff);
364 }
365 
366 //----------------------------------------------------------------------------
368 {
369  Q_D(DICOMAppWidget);
370  return d->Importer.patientsAddedDuringImport();
371 }
372 
373 //----------------------------------------------------------------------------
375 {
376  Q_D(DICOMAppWidget);
377  return d->Importer.studiesAddedDuringImport();
378 }
379 
380 //----------------------------------------------------------------------------
382 {
383  Q_D(DICOMAppWidget);
384  return d->Importer.seriesAddedDuringImport();
385 }
386 
387 //----------------------------------------------------------------------------
389 {
390  Q_D(DICOMAppWidget);
391  return d->Importer.instancesAddedDuringImport();
392 }
393 
394 //----------------------------------------------------------------------------
396 {
397  Q_D(DICOMAppWidget);
398 
399  d->showUpdateSchemaDialog();
400  d->DICOMDatabase->updateSchemaIfNeeded();
401 }
402 
403 //----------------------------------------------------------------------------
404 void DICOMAppWidget::setDatabaseDirectory(const QString& directory)
405 {
406  Q_D(DICOMAppWidget);
407 
408  QSettings settings;
409  settings.setValue("DatabaseDirectory", directory);
410  settings.sync();
411 
412  //close the active DICOM database
413  d->DICOMDatabase->closeDatabase();
414 
415  //open DICOM database on the directory
416  QString databaseFileName = directory + QString("/ctkDICOM.sql");
417  try
418  {
419  d->DICOMDatabase->openDatabase( databaseFileName );
420 
421  CX_LOG_CHANNEL_INFO("dicom") << "open database " << databaseFileName.toStdString() << ", open= " << d->DICOMDatabase->isOpen();
422 // std::cout << "open database " << databaseFileName.toStdString() << ", open= " << d->DICOMDatabase->isOpen() << std::endl;
423  }
424  catch (std::exception e)
425  {
426  CX_LOG_CHANNEL_ERROR("dicom") << "Database error: " << qPrintable(d->DICOMDatabase->lastError());
427 // std::cerr << "Database error: " << qPrintable(d->DICOMDatabase->lastError()) << "\n";
428  d->DICOMDatabase->closeDatabase();
429  return;
430  }
431 
432  // update the database schema if needed and provide progress
434 
435  d->mDICOMModel.setDatabase(d->DICOMDatabase);
436  d->mDICOMModel.setEndLevel(ctkDICOMModel::SeriesType);
437  d->TreeView->resizeColumnToContents(0);
438  d->Importer.setDatabase(d->DICOMDatabase);
439 
440  d->QueryRetrieveWidget->setRetrieveDatabase(d->DICOMDatabase);
441  d->ThumbnailsWidget->setDatabaseDirectory(directory);
442  d->ThumbnailsWidget->setDatabase(d->DICOMDatabase);
443  emit databaseDirectoryChanged(directory);
444 }
445 
446 //----------------------------------------------------------------------------
448 {
449  QSettings settings;
450  return settings.value("DatabaseDirectory").toString();
451 }
452 
453 //------------------------------------------------------------------------------
454 void DICOMAppWidget::setTagsToPrecache( const QStringList tags)
455 {
456  Q_D(DICOMAppWidget);
457  d->DICOMDatabase->setTagsToPrecache(tags);
458 }
459 
460 //------------------------------------------------------------------------------
462 {
463  Q_D(DICOMAppWidget);
464  return d->DICOMDatabase->tagsToPrecache();
465 }
466 
467 //----------------------------------------------------------------------------
468 ctkDICOMDatabase* DICOMAppWidget::database(){
469  Q_D(DICOMAppWidget);
470  return d->DICOMDatabase.data();
471 }
472 
473 //----------------------------------------------------------------------------
475 {
476  Q_D(DICOMAppWidget);
477 
478  d->QueryRetrieveWidget->show();
479  d->QueryRetrieveWidget->raise();
480 
481 }
482 
483 //----------------------------------------------------------------------------
485 {
486  Q_D(DICOMAppWidget);
487  d->mDICOMModel.reset();
488  emit this->queryRetrieveFinished();
489 }
490 
492 {
493  Q_D(DICOMAppWidget);
494  return d->getSelection()[ctkDICOMModel::PatientType];
495 }
497 {
498  Q_D(DICOMAppWidget);
499  return d->getSelection()[ctkDICOMModel::StudyType];
500 }
502 {
503  Q_D(DICOMAppWidget);
504  return d->getSelection()[ctkDICOMModel::SeriesType];
505 }
506 
507 //----------------------------------------------------------------------------
509 {
510  Q_D(DICOMAppWidget);
511  d->removeSelection();
512 }
513 
514 //----------------------------------------------------------------------------
516 {
517  Q_D(DICOMAppWidget);
518  d->mDICOMModel.setDatabase(d->DICOMDatabase);
519 }
520 
521 //----------------------------------------------------------------------------
523 {
524  Q_D(DICOMAppWidget);
525  d->mDICOMModel.reset();
526 }
527 
529 void DICOMAppWidget::onImportDirectory(QString directory)
530 {
531  Q_D(DICOMAppWidget);
532  d->Importer.onImportDirectory(directory);
533 }
534 
535 void DICOMAppWidget::onSelectionChanged(const QItemSelection&, const QItemSelection&)
536 {
537 }
538 
539 void DICOMAppWidget::onCurrentChanged(const QModelIndex& next, const QModelIndex& last)
540 {
541  Q_D(DICOMAppWidget);
542  d->ThumbnailsWidget->addThumbnails(next);
543 }
544 
545 void DICOMAppWidget::onModelSelected(const QModelIndex &index)
546 {
547  Q_D(DICOMAppWidget);
548 
549  ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
550 
551  if(model)
552  {
553  QModelIndex index0 = index.sibling(index.row(), 0);
554 
555  d->ActionRemove->setEnabled(
556  model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::SeriesType) ||
557  model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::StudyType) ||
558  model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::PatientType) );
559  }
560  else
561  {
562  d->ActionRemove->setEnabled(false);
563  }
564 }
565 
566 //----------------------------------------------------------------------------
567 void DICOMAppWidget::onTreeCollapsed(const QModelIndex &index){
568  Q_UNUSED(index);
569  Q_D(DICOMAppWidget);
570  d->TreeView->resizeColumnToContents(0);
571 }
572 
573 //----------------------------------------------------------------------------
574 void DICOMAppWidget::onTreeExpanded(const QModelIndex &index){
575  Q_UNUSED(index);
576  Q_D(DICOMAppWidget);
577  d->TreeView->resizeColumnToContents(0);
578  d->TreeView->resizeColumnToContents(1);
579 }
580 
581 //----------------------------------------------------------------------------
583 {
584  Q_D(DICOMAppWidget);
585  d->ThumbnailsWidget->setThumbnailSize(QSize(val, val));
586 }
587 
589 {
590  Q_D(DICOMAppWidget);
591  d->ToolBar->addAction(action);
592 }
593 
594 
595 
596 } // namespace cx
#define CX_LOG_CHANNEL_INFO(channel)
Definition: cxLogger.h:121
void onTreeCollapsed(const QModelIndex &index)
QStringList getSelectedSeries()
ctkDICOMQueryRetrieveWidget * QueryRetrieveWidget
Q_DECLARE_PUBLIC(DICOMAppWidget)
void onTreeExpanded(const QModelIndex &index)
virtual bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex())
DICOMAppWidgetPrivate(DICOMAppWidget *)
ctkDICOMDatabase * database()
ctkDICOMFilterProxyModel DICOMProxyModel
std::map< ctkDICOMModel::IndexType, QStringList > getSelection() const
void addActionToToolbar(QAction *action)
void setDisplayImportSummary(bool)
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
void onSelectionChanged(const QItemSelection &, const QItemSelection &)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: cxSettings.cpp:99
QSharedPointer< ctkDICOMDatabase > DICOMDatabase
QSharedPointer< ctkDICOMThumbnailGenerator > ThumbnailGenerator
void onImportDirectory(QString directory)
void setTagsToPrecache(const QStringList tags)
DICOMModel ctkDICOMModel
void setValue(const QString &key, const QVariant &value)
Definition: cxSettings.cpp:91
QStringList getSelectedPatients()
QStringList getSelectedStudies()
void onCurrentChanged(const QModelIndex &, const QModelIndex &)
void databaseDirectoryChanged(const QString &)
QProgressDialog * UpdateSchemaProgress
QString databaseDirectory() const
Directory being used to store the dicom database.
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:42
DICOMAppWidget(QWidget *parent=0)
void queryRetrieveFinished()
void onThumbnailWidthSliderValueChanged(int val)
void setupUi(DICOMAppWidget *parent)
const QStringList tagsToPrecache()
void onModelSelected(const QModelIndex &index)
DICOMAppWidget *const q_ptr
void setDatabaseDirectory(const QString &directory)
Q_DECLARE_METATYPE(QPersistentModelIndex)
int patientsAddedDuringImport()
Accessors to status of last directory import operation.
#define CX_LOG_CHANNEL_ERROR(channel)
Definition: cxLogger.h:124
DICOMThumbnailListWidget * ThumbnailsWidget