CustusX  22.04-rc5
An IGT application
cxImportWidget.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include "cxImportWidget.h"
13 
14 #include <QTreeWidgetItem>
15 
16 #include <QFileDialog>
17 #include <QPushButton>
18 #include <QLabel>
19 #include <QTableWidget>
20 #include <QHeaderView>
21 #include <QApplication>
22 #include <QDesktopWidget>
23 #include <QStackedWidget>
24 #include "cxForwardDeclarations.h"
26 #include "cxVisServices.h"
27 #include "cxLogger.h"
28 #include "cxPatientModelService.h"
29 #include "cxProfile.h"
30 #include "cxImportDataTypeWidget.h"
32 #include "cxImage.h"
33 #include "cxMesh.h"
34 #include "cxPointMetric.h"
35 
36 namespace cx
37 {
38 /*
39  * TODOS:
40  * * implement coordinate system guess
41  * * implement parent guess
42  * * implement auto RAS to LPS conversion
43  */
44 
46  BaseWidget(NULL, "import_widget", "Import"),
47  mSelectedIndexInTable(0),
48  mFileManager(filemanager),
49  mVisServices(services)
50 {
51  //see https://wiki.qt.io/How_to_Use_QTableWidget
52  mTableWidget = new QTableWidget();
53  mTableWidget->setRowCount(0);
54  mTableWidget->setColumnCount(3);
55  mTableHeader<<""<<"Status"<<"Filename";
56  mTableWidget->setHorizontalHeaderLabels(mTableHeader);
57  mTableWidget->horizontalHeader()->setStretchLastSection(true);
58  mTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
59  mTableWidget->verticalHeader()->setVisible(false);
60  mTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
61  mTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
62  mTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
63  mTableWidget->setShowGrid(false);
64  mTableWidget->setStyleSheet("QTableView {selection-background-color: #ACCEF7;}");
65  mTableWidget->setGeometry(QApplication::desktop()->screenGeometry());
66  connect(mTableWidget, &QTableWidget::currentCellChanged, this, &ImportWidget::tableItemSelected);
67 
68  mStackedWidget = new QStackedWidget;
69 
70  mTopLayout = new QVBoxLayout();
71  this->setLayout(mTopLayout);
72  QPushButton *addMoreFilesButton = new QPushButton("Add more files...");
73  QHBoxLayout *buttonLayout = new QHBoxLayout();
74  QPushButton *importButton = new QPushButton("Import");
75  QPushButton *cancelButton = new QPushButton("Cancel");
76  buttonLayout->addWidget(importButton);
77  buttonLayout->addWidget(cancelButton);
78  buttonLayout->addStretch();
79  mTopLayout->addWidget(addMoreFilesButton);
80  mTopLayout->addWidget(new QLabel("Supports: "+this->generateFileTypeFilter()));
81  mTopLayout->addWidget(mTableWidget);
82  mTopLayout->addWidget(mStackedWidget);
83  mTopLayout->addStretch();
84  mTopLayout->addLayout(buttonLayout);
85 
86  connect(addMoreFilesButton, &QPushButton::clicked, this, &ImportWidget::addMoreFilesButtonClicked);
87  connect(importButton, &QPushButton::clicked, this, &ImportWidget::importButtonClicked);
88  connect(cancelButton, &QPushButton::clicked, this, &ImportWidget::cancelButtonClicked);
89  connect(this, &ImportWidget::finishedImporting, this, &ImportWidget::cleanUpAfterImport);
90 
91  QAction* addMoreFilesButtonClickedAction = new QAction("AddMoreFilesButtonClickedAction", this);
92  this->addAction(addMoreFilesButtonClickedAction);
93  connect(addMoreFilesButtonClickedAction, &QAction::triggered, this, &ImportWidget::addMoreFilesButtonClicked);
94 
95  QAction* importButtonClickedAction = new QAction("ImportButtonClickedAction", this);
96  this->addAction(importButtonClickedAction);
97  connect(importButtonClickedAction, &QAction::triggered, this, &ImportWidget::importButtonClicked);
98 }
99 
100 int ImportWidget::insertDataIntoTable(QString fullfilename, std::vector<DataPtr> data)
101 {
102  int newRowIndex = mTableWidget->rowCount();
103  mTableWidget->setRowCount(newRowIndex+1);
104  mTableWidget->selectRow(mSelectedIndexInTable);
105  QString status;
106  bool allDataOk = true;
107  for(unsigned i=0; i<data.size(); ++i)
108  {
109  if(!data[i])
110  allDataOk = false;
111  }
112  status = allDataOk ? "ok" : "error";
113  QFileInfo fileInfo(fullfilename);
114  QString filename = fileInfo.fileName();
115 
116  QIcon trashcan(":/icons/open_icon_library/edit-delete-2.png");
117  QPushButton *removeButton = new QPushButton(trashcan,"");
118  connect(removeButton, &QPushButton::clicked, this, &ImportWidget::removeRowFromTableAndRemoveFilenameFromImportList);
119 
120  mTableWidget->setCellWidget(newRowIndex, 0, removeButton);
121  mTableWidget->setItem(newRowIndex, 1, new QTableWidgetItem(status));
122  QTableWidgetItem *filenameItem = new QTableWidgetItem(filename);
123  filenameItem->setData(Qt::ToolTipRole, fullfilename);
124  mTableWidget->setItem(newRowIndex, 2, filenameItem);
125 
126  return newRowIndex;
127 }
128 
129 int ImportWidget::findRowIndexContainingButton(QPushButton *button) const
130 {
131  int retval = -1;
132  for(int i=0; i<mTableWidget->rowCount(); ++i)
133  {
134  int buttonColoumn = 0;
135  QTableWidgetItem *item = mTableWidget->item(i,buttonColoumn);
136  QWidget *cellWidget = mTableWidget->cellWidget(i,buttonColoumn);
137  if(button == cellWidget)
138  retval = i;
139  }
140  return retval;
141 }
142 
143 void ImportWidget::addMoreFilesButtonClicked()
144 {
145  QStringList filenames = this->openFileBrowserForSelectingFiles();
146 
147  bool addedDICOM = false;
148 
149  ImportDataTypeWidget *widget = NULL;
150  for(int i = 0; i < filenames.size(); ++i)
151  {
152  QString filename = filenames[i];
153  QString fileType = mFileManager->getFileReaderName(filename);
154  if(fileType == "DICOMReader")
155  {
156  //Only allow adding one dicom series to prevent duplicates, and long processing time
157  if(addedDICOM)
158  {
159  //If import of multiple series is something we need:
160  //All DICOM files can be added to a QStringList before sending them to the reader.
161  //Then the reader can determine which files are from the same/different series
162  CX_LOG_WARNING() << "Import of multiple DICOM series at once is not supported. Skipping series based on file: " << filename;
163  continue;
164  }
165  addedDICOM = true;
166  }
167 
168  mFileNames.push_back(filename);
169 
170  std::vector<DataPtr> newData = mFileManager->read(filename);
171  if(newData.size() > 0)
172  {
173  int index = this->insertDataIntoTable(filename, newData);
174  widget = new ImportDataTypeWidget(this, mVisServices, newData, mParentCandidates, filename);
175  mStackedWidget->insertWidget(index, widget);
176  mNotImportedData.insert(mNotImportedData.end(), newData.begin(), newData.end());//Update mNotImportedData with new data
177  }
178  }
179 
180  this->generateParentCandidates();
181 }
182 
183 void ImportWidget::importButtonClicked()
184 {
185  this->clearData();
186  emit readyToImport();
187  emit finishedImporting();
188 
189  mVisServices->session()->save();//AutoSave
190 }
191 
192 void ImportWidget::cancelButtonClicked()
193 {
194  this->clearData();
195  emit finishedImporting();
196 }
197 
198 void ImportWidget::clearData()
199 {
200  mParentCandidates.clear();
201  mNotImportedData.clear();
202 }
203 
204 void ImportWidget::removeWidget(QWidget *widget)
205 {
206  mTopLayout->removeWidget(widget);
207  widget->deleteLater();
208 }
209 
210 void ImportWidget::removeRowFromTableAndRemoveFilenameFromImportList()
211 {
212  QPushButton *button = qobject_cast<QPushButton*>(QObject::sender());
213  int rowindex = this->findRowIndexContainingButton(button);
214  int filenamecoloumn = 2;
215  QString fullfilename = mTableWidget->item(rowindex, filenamecoloumn)->data(Qt::ToolTipRole).toString();
216  if(rowindex != -1)
217  mTableWidget->removeRow(rowindex);
218  int numberOfRemovedEntries = mFileNames.removeAll(fullfilename);
219 
220  QWidget *widgetToRemove = mStackedWidget->widget(rowindex);
221  mStackedWidget->removeWidget(widgetToRemove);
222 }
223 
224 void ImportWidget::tableItemSelected(int currentRow, int currentColumn, int previousRow, int previousColumn)
225 {
226  if(currentRow == mSelectedIndexInTable)
227  return;
228 
229  mStackedWidget->setCurrentIndex(currentRow);
230  mSelectedIndexInTable = currentRow;
231 }
232 
233 void ImportWidget::cleanUpAfterImport()
234 {
235  //clear data
236  //mData.clear();
237  mFileNames.clear();
238 
239  //reset table
240  mTableWidget->clear();
241  mTableWidget->setRowCount(0);
242  mTableWidget->setHorizontalHeaderLabels(mTableHeader);
243  //clear the stacked widget
244  for(int i=mStackedWidget->count()-1; i>=0; --i)
245  {
246  QWidget *widgetToRemove = mStackedWidget->widget(i);
247  mStackedWidget->removeWidget(widgetToRemove);
248  delete widgetToRemove;
249  }
250 }
251 
252 QStringList ImportWidget::openFileBrowserForSelectingFiles()
253 {
254  QString file_type_filter = generateFileTypeFilter();
255 
256  QStringList fileName = QFileDialog::getOpenFileNames(this->parentWidget(), QString(tr("Select data file(s) for import")), profile()->getSessionRootFolder(), tr(file_type_filter.toStdString().c_str()));
257  if (fileName.empty())
258  {
259  report("Import canceled");
260  }
261 
262  return fileName;
263 
264 }
265 
266 QString ImportWidget::generateFileTypeFilter() const
267 {
268  QString file_type_filter;
269  std::vector<FileReaderWriterServicePtr> mesh_readers = mVisServices->file()->getImportersForDataType(Mesh::getTypeName());
270  std::vector<FileReaderWriterServicePtr> image_readers = mVisServices->file()->getImportersForDataType(Image::getTypeName());
271  std::vector<FileReaderWriterServicePtr> point_metric_readers = mVisServices->file()->getImportersForDataType(PointMetric::getTypeName());
272  std::vector<FileReaderWriterServicePtr> readers;
273  readers.insert( readers.end(), mesh_readers.begin(), mesh_readers.end() );
274  readers.insert( readers.end(), image_readers.begin(), image_readers.end() );
275  readers.insert( readers.end(), point_metric_readers.begin(), point_metric_readers.end() );
276 
277  file_type_filter = "Image/Mesh/PointMetrics (";
278  for(unsigned i=0; i<readers.size(); ++i)
279  {
280  QString suffix = readers[i]->getFileSuffix();
281  if(!suffix.isEmpty())
282  file_type_filter.append("*."+suffix+" ");
283  }
284  while(file_type_filter.endsWith( ' ' ))
285  file_type_filter.chop(1);
286  file_type_filter.append(")");
287 
288  return file_type_filter;
289 }
290 
291 void ImportWidget::generateParentCandidates()
292 {
293  for(unsigned i=0; i<mNotImportedData.size(); ++i)
294  {
295  if(!mNotImportedData[i])
296  {
297  CX_LOG_WARNING() << "ImportWidget::generateParentCandidates(): No data";
298  continue;
299  }
300  if(mNotImportedData[i]->getType() != PointMetric::getTypeName())
301  mParentCandidates.push_back(mNotImportedData[i]);
302  }
303  std::map<QString, DataPtr> loadedData = mVisServices->patient()->getDatas();
304  std::map<QString, DataPtr>::iterator it = loadedData.begin();
305  for(; it!=loadedData.end(); ++it)
306  {
307  mParentCandidates.push_back(it->second);
308  }
310 }
311 
312 
313 }
cxResource_EXPORT ProfilePtr profile()
Definition: cxProfile.cpp:160
boost::shared_ptr< class FileManagerService > FileManagerServicePtr
ImportWidget(FileManagerServicePtr filemanager, VisServicesPtr services)
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:40
static QString getTypeName()
Definition: cxImage.h:126
void parentCandidatesUpdated()
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:88
void report(QString msg)
Definition: cxLogger.cpp:69
#define CX_LOG_WARNING
Definition: cxLogger.h:98
static QString getTypeName()
Definition: cxPointMetric.h:58
void finishedImporting()
static QString getTypeName()
Definition: cxMesh.h:67
Namespace for all CustusX production code.