CustusX  22.04-rc3
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  emit readyToImport();
186  emit finishedImporting();
187 
188  mVisServices->session()->save();//AutoSave
189 }
190 
191 void ImportWidget::cancelButtonClicked()
192 {
193  emit finishedImporting();
194 }
195 
196 void ImportWidget::removeWidget(QWidget *widget)
197 {
198  mTopLayout->removeWidget(widget);
199  widget->deleteLater();
200 }
201 
202 void ImportWidget::removeRowFromTableAndRemoveFilenameFromImportList()
203 {
204  QPushButton *button = qobject_cast<QPushButton*>(QObject::sender());
205  int rowindex = this->findRowIndexContainingButton(button);
206  int filenamecoloumn = 2;
207  QString fullfilename = mTableWidget->item(rowindex, filenamecoloumn)->data(Qt::ToolTipRole).toString();
208  if(rowindex != -1)
209  mTableWidget->removeRow(rowindex);
210  int numberOfRemovedEntries = mFileNames.removeAll(fullfilename);
211 
212  QWidget *widgetToRemove = mStackedWidget->widget(rowindex);
213  mStackedWidget->removeWidget(widgetToRemove);
214 }
215 
216 void ImportWidget::tableItemSelected(int currentRow, int currentColumn, int previousRow, int previousColumn)
217 {
218  if(currentRow == mSelectedIndexInTable)
219  return;
220 
221  mStackedWidget->setCurrentIndex(currentRow);
222  mSelectedIndexInTable = currentRow;
223 }
224 
225 void ImportWidget::cleanUpAfterImport()
226 {
227  //clear data
228  //mData.clear();
229  mFileNames.clear();
230 
231  //reset table
232  mTableWidget->clear();
233  mTableWidget->setRowCount(0);
234  mTableWidget->setHorizontalHeaderLabels(mTableHeader);
235  //clear the stacked widget
236  for(int i=mStackedWidget->count()-1; i>=0; --i)
237  {
238  QWidget *widgetToRemove = mStackedWidget->widget(i);
239  mStackedWidget->removeWidget(widgetToRemove);
240  delete widgetToRemove;
241  }
242 }
243 
244 QStringList ImportWidget::openFileBrowserForSelectingFiles()
245 {
246  QString file_type_filter = generateFileTypeFilter();
247 
248  QStringList fileName = QFileDialog::getOpenFileNames(this->parentWidget(), QString(tr("Select data file(s) for import")), profile()->getSessionRootFolder(), tr(file_type_filter.toStdString().c_str()));
249  if (fileName.empty())
250  {
251  report("Import canceled");
252  }
253 
254  return fileName;
255 
256 }
257 
258 QString ImportWidget::generateFileTypeFilter() const
259 {
260  QString file_type_filter;
261  std::vector<FileReaderWriterServicePtr> mesh_readers = mVisServices->file()->getImportersForDataType(Mesh::getTypeName());
262  std::vector<FileReaderWriterServicePtr> image_readers = mVisServices->file()->getImportersForDataType(Image::getTypeName());
263  std::vector<FileReaderWriterServicePtr> point_metric_readers = mVisServices->file()->getImportersForDataType(PointMetric::getTypeName());
264  std::vector<FileReaderWriterServicePtr> readers;
265  readers.insert( readers.end(), mesh_readers.begin(), mesh_readers.end() );
266  readers.insert( readers.end(), image_readers.begin(), image_readers.end() );
267  readers.insert( readers.end(), point_metric_readers.begin(), point_metric_readers.end() );
268 
269  file_type_filter = "Image/Mesh/PointMetrics (";
270  for(unsigned i=0; i<readers.size(); ++i)
271  {
272  QString suffix = readers[i]->getFileSuffix();
273  if(!suffix.isEmpty())
274  file_type_filter.append("*."+suffix+" ");
275  }
276  while(file_type_filter.endsWith( ' ' ))
277  file_type_filter.chop(1);
278  file_type_filter.append(")");
279 
280  return file_type_filter;
281 }
282 
283 void ImportWidget::generateParentCandidates()
284 {
285  for(unsigned i=0; i<mNotImportedData.size(); ++i)
286  {
287  if(!mNotImportedData[i])
288  {
289  CX_LOG_WARNING() << "ImportWidget::generateParentCandidates(): No data";
290  continue;
291  }
292  if(mNotImportedData[i]->getType() != PointMetric::getTypeName())
293  mParentCandidates.push_back(mNotImportedData[i]);
294  }
295  std::map<QString, DataPtr> loadedData = mVisServices->patient()->getDatas();
296  std::map<QString, DataPtr>::iterator it = loadedData.begin();
297  for(; it!=loadedData.end(); ++it)
298  {
299  mParentCandidates.push_back(it->second);
300  }
302 }
303 
304 
305 }
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.