Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
QmitkIOUtil.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 #include "QmitkIOUtil.h"
18 
19 #include "mitkCoreServices.h"
20 #include "mitkCustomMimeType.h"
21 #include "mitkFileReaderRegistry.h"
22 #include "mitkFileWriterRegistry.h"
23 #include "mitkIMimeTypeProvider.h"
24 #include "mitkMimeType.h"
25 #include <mitkCoreObjectFactory.h>
26 #include <mitkIOUtil.h>
27 
30 
31 // QT
32 #include <QDebug>
33 #include <QFileDialog>
34 #include <QMessageBox>
35 #include <QSet>
36 
37 // ITK
38 #include <itksys/SystemTools.hxx>
39 
40 #include <algorithm>
41 
42 struct QmitkIOUtil::Impl
43 {
44  struct ReaderOptionsDialogFunctor : public ReaderOptionsFunctorBase
45  {
46  virtual bool operator()(LoadInfo &loadInfo) override
47  {
48  QmitkFileReaderOptionsDialog dialog(loadInfo);
49  if (dialog.exec() == QDialog::Accepted)
50  {
51  return !dialog.ReuseOptions();
52  }
53  else
54  {
55  loadInfo.m_Cancel = true;
56  return true;
57  }
58  }
59  };
60 
61  struct WriterOptionsDialogFunctor : public WriterOptionsFunctorBase
62  {
63  virtual bool operator()(SaveInfo &saveInfo) override
64  {
65  QmitkFileWriterOptionsDialog dialog(saveInfo);
66  if (dialog.exec() == QDialog::Accepted)
67  {
68  return !dialog.ReuseOptions();
69  }
70  else
71  {
72  saveInfo.m_Cancel = true;
73  return true;
74  }
75  }
76  };
77 };
78 
79 struct MimeTypeComparison : public std::unary_function<mitk::MimeType, bool>
80 {
81  MimeTypeComparison(const std::string &mimeTypeName) : m_Name(mimeTypeName) {}
82  bool operator()(const mitk::MimeType &mimeType) const { return mimeType.GetName() == m_Name; }
83  const std::string m_Name;
84 };
85 
87 {
88  QString filters;
89 
91  std::vector<std::string> categories = mimeTypeProvider->GetCategories();
92  for (std::vector<std::string>::iterator cat = categories.begin(); cat != categories.end(); ++cat)
93  {
94  QSet<QString> filterExtensions;
95  std::vector<mitk::MimeType> mimeTypes = mimeTypeProvider->GetMimeTypesForCategory(*cat);
96  for (std::vector<mitk::MimeType>::iterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
97  {
98  std::vector<std::string> extensions = mt->GetExtensions();
99  for (std::vector<std::string>::iterator ext = extensions.begin(); ext != extensions.end(); ++ext)
100  {
101  filterExtensions << QString::fromStdString(*ext);
102  }
103  }
104 
105  QString filter = QString::fromStdString(*cat) + " (";
106  foreach (const QString &extension, filterExtensions)
107  {
108  filter += "*." + extension + " ";
109  }
110  filter = filter.replace(filter.size() - 1, 1, ')');
111  filters += ";;" + filter;
112  }
113  filters.prepend("All (*)");
114  return filters;
115 }
116 
117 QList<mitk::BaseData::Pointer> QmitkIOUtil::Load(const QStringList &paths, QWidget *parent)
118 {
119  std::vector<LoadInfo> loadInfos;
120  foreach (const QString &file, paths)
121  {
122  loadInfos.push_back(LoadInfo(file.toStdString()));
123  }
124 
125  Impl::ReaderOptionsDialogFunctor optionsCallback;
126  std::string errMsg = Load(loadInfos, NULL, NULL, &optionsCallback);
127  if (!errMsg.empty())
128  {
129  QMessageBox::warning(parent, "Error reading files", QString::fromStdString(errMsg));
130  mitkThrow() << errMsg;
131  }
132 
133  QList<mitk::BaseData::Pointer> qResult;
134  for (std::vector<LoadInfo>::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd;
135  ++iter)
136  {
137  for (const auto &elem : iter->m_Output)
138  {
139  qResult << elem;
140  }
141  }
142  return qResult;
143 }
144 
146  mitk::DataStorage &storage,
147  QWidget *parent)
148 {
149  std::vector<LoadInfo> loadInfos;
150  foreach (const QString &file, paths)
151  {
152  loadInfos.push_back(LoadInfo(QFile::encodeName(file).constData()));
153  }
154 
156  Impl::ReaderOptionsDialogFunctor optionsCallback;
157  std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback);
158  if (!errMsg.empty())
159  {
160  QMessageBox::warning(parent, "Error reading files", QString::fromStdString(errMsg));
161  }
162  return nodeResult;
163 }
164 
165 QList<mitk::BaseData::Pointer> QmitkIOUtil::Load(const QString &path, QWidget *parent)
166 {
167  QStringList paths;
168  paths << path;
169  return Load(paths, parent);
170 }
171 
173  mitk::DataStorage &storage,
174  QWidget *parent)
175 {
176  QStringList paths;
177  paths << path;
178  return Load(paths, storage, parent);
179 }
180 
181 QString QmitkIOUtil::Save(const mitk::BaseData *data,
182  const QString &defaultBaseName,
183  const QString &defaultPath,
184  QWidget *parent)
185 {
186  std::vector<const mitk::BaseData *> dataVector;
187  dataVector.push_back(data);
188  QStringList defaultBaseNames;
189  defaultBaseNames.push_back(defaultBaseName);
190  return Save(dataVector, defaultBaseNames, defaultPath, parent).back();
191 }
192 
193 QStringList QmitkIOUtil::Save(const std::vector<const mitk::BaseData *> &data,
194  const QStringList &defaultBaseNames,
195  const QString &defaultPath,
196  QWidget *parent)
197 {
198  QStringList fileNames;
199  QString currentPath = defaultPath;
200 
201  std::vector<SaveInfo> saveInfos;
202 
203  int counter = 0;
204  for (std::vector<const mitk::BaseData *>::const_iterator dataIter = data.begin(), dataIterEnd = data.end();
205  dataIter != dataIterEnd;
206  ++dataIter, ++counter)
207  {
208  SaveInfo saveInfo(*dataIter, mitk::MimeType(), std::string());
209 
210  SaveFilter filters(saveInfo);
211 
212  // If there is only the "__all__" filter string, it means there is no writer for this base data
213  if (filters.Size() < 2)
214  {
215  QMessageBox::warning(
216  parent,
217  "Saving not possible",
218  QString("No writer available for type \"%1\"").arg(QString::fromStdString((*dataIter)->GetNameOfClass())));
219  continue;
220  }
221 
222  // Construct a default path and file name
223  QString filterString = filters.ToString();
224  QString selectedFilter = filters.GetDefaultFilter();
225  QString fileName = currentPath;
226  QString dialogTitle = "Save " + QString::fromStdString((*dataIter)->GetNameOfClass());
227  if (counter < defaultBaseNames.size())
228  {
229  dialogTitle += " \"" + defaultBaseNames[counter] + "\"";
230  fileName += QDir::separator() + defaultBaseNames[counter];
231  // We do not append an extension to the file name by default. The extension
232  // is chosen by the user by either selecting a filter or writing the
233  // extension in the file name himself (in the file save dialog).
234  /*
235  QString defaultExt = filters.GetDefaultExtension();
236  if (!defaultExt.isEmpty())
237  {
238  fileName += "." + defaultExt;
239  }
240  */
241  }
242 
243  // Ask the user for a file name
244  QString nextName = QFileDialog::getSaveFileName(parent, dialogTitle, fileName, filterString, &selectedFilter);
245 
246  if (nextName.isEmpty())
247  {
248  // We stop asking for further file names, but we still save the
249  // data where the user already confirmed the save dialog.
250  break;
251  }
252 
253  fileName = nextName;
254  std::string stdFileName = QFile::encodeName(fileName).constData();
255  QFileInfo fileInfo(fileName);
256  currentPath = fileInfo.absolutePath();
257  QString suffix = fileInfo.completeSuffix();
258  mitk::MimeType filterMimeType = filters.GetMimeTypeForFilter(selectedFilter);
259  mitk::MimeType selectedMimeType;
260 
261  if (fileInfo.exists() && !fileInfo.isFile())
262  {
263  QMessageBox::warning(parent, "Saving not possible", QString("The path \"%1\" is not a file").arg(fileName));
264  continue;
265  }
266 
267  // Theoretically, the user could have entered an extension that does not match the selected filter
268  // The extension then has prioritry over the filter
269  // Check if one of the available mime-types match the filename
270  std::vector<mitk::MimeType> filterMimeTypes = filters.GetMimeTypes();
271  for (std::vector<mitk::MimeType>::const_iterator mimeTypeIter = filterMimeTypes.begin(),
272  mimeTypeIterEnd = filterMimeTypes.end();
273  mimeTypeIter != mimeTypeIterEnd;
274  ++mimeTypeIter)
275  {
276  if (mimeTypeIter->MatchesExtension(stdFileName))
277  {
278  selectedMimeType = *mimeTypeIter;
279  break;
280  }
281  }
282 
283  if (!selectedMimeType.IsValid())
284  {
285  // The file name either does not contain an extension or the
286  // extension is unknown.
287 
288  // If the file already exists, we stop here because we are unable
289  // to (over)write the file without adding a custom suffix. If the file
290  // does not exist, we add the default extension from the currently
291  // selected filter. If the "All" filter was selected, we only add the
292  // default extensions if the file name itself does not already contain
293  // an extension.
294  if (!fileInfo.exists())
295  {
296  if (filterMimeType == SaveFilter::ALL_MIMETYPE())
297  {
298  if (suffix.isEmpty())
299  {
300  // Use the highest ranked mime-type from the list
301  selectedMimeType = filters.GetDefaultMimeType();
302  }
303  }
304  else
305  {
306  selectedMimeType = filterMimeType;
307  }
308 
309  if (selectedMimeType.IsValid())
310  {
311  suffix = QString::fromStdString(selectedMimeType.GetExtensions().front());
312  fileName += "." + suffix;
313  stdFileName = QFile::encodeName(fileName).constData();
314  // We changed the file name (added a suffix) so ask in case
315  // the file aready exists.
316  fileInfo = QFileInfo(fileName);
317  if (fileInfo.exists())
318  {
319  if (!fileInfo.isFile())
320  {
321  QMessageBox::warning(
322  parent, "Saving not possible", QString("The path \"%1\" is not a file").arg(fileName));
323  continue;
324  }
325  if (QMessageBox::question(
326  parent,
327  "Replace File",
328  QString("A file named \"%1\" already exists. Do you want to replace it?").arg(fileName)) ==
329  QMessageBox::No)
330  {
331  continue;
332  }
333  }
334  }
335  }
336  }
337 
338  if (!selectedMimeType.IsValid())
339  {
340  // The extension/filename is not valid (no mime-type found), bail out
341  QMessageBox::warning(
342  parent, "Saving not possible", QString("No mime-type available which can handle \"%1\".").arg(fileName));
343  continue;
344  }
345 
346  if (!QFileInfo(fileInfo.absolutePath()).isWritable())
347  {
348  QMessageBox::warning(parent, "Saving not possible", QString("The path \"%1\" is not writable").arg(fileName));
349  continue;
350  }
351 
352  fileNames.push_back(fileName);
353  saveInfo.m_Path = stdFileName;
354  saveInfo.m_MimeType = selectedMimeType;
355  // pre-select the best writer for the chosen mime-type
356  saveInfo.m_WriterSelector.Select(selectedMimeType.GetName());
357  saveInfos.push_back(saveInfo);
358  }
359 
360  if (!saveInfos.empty())
361  {
362  Impl::WriterOptionsDialogFunctor optionsCallback;
363  std::string errMsg = Save(saveInfos, &optionsCallback);
364  if (!errMsg.empty())
365  {
366  QMessageBox::warning(parent, "Error writing files", QString::fromStdString(errMsg));
367  mitkThrow() << errMsg;
368  }
369  }
370 
371  return fileNames;
372 }
373 
374 void QmitkIOUtil::SaveBaseDataWithDialog(mitk::BaseData *data, std::string fileName, QWidget * /*parent*/)
375 {
376  Save(data, fileName);
377 }
378 
379 void QmitkIOUtil::SaveSurfaceWithDialog(mitk::Surface::Pointer surface, std::string fileName, QWidget * /*parent*/)
380 {
381  Save(surface, fileName);
382 }
383 
384 void QmitkIOUtil::SaveImageWithDialog(mitk::Image::Pointer image, std::string fileName, QWidget * /*parent*/)
385 {
386  Save(image, fileName);
387 }
388 
389 void QmitkIOUtil::SavePointSetWithDialog(mitk::PointSet::Pointer pointset, std::string fileName, QWidget * /*parent*/)
390 {
391  Save(pointset, fileName);
392 }
393 
394 struct QmitkIOUtil::SaveFilter::Impl
395 {
396  Impl(const mitk::IOUtil::SaveInfo &saveInfo) : m_SaveInfo(saveInfo)
397  {
398  // Add an artifical filter for "All"
399  m_MimeTypes.push_back(ALL_MIMETYPE());
400  m_FilterStrings.push_back("All (*.*)");
401 
402  // Get all writers and their mime types for the given base data type
403  // (this is sorted already)
404  std::vector<mitk::MimeType> mimeTypes = saveInfo.m_WriterSelector.GetMimeTypes();
405 
406  for (std::vector<mitk::MimeType>::const_reverse_iterator iter = mimeTypes.rbegin(), iterEnd = mimeTypes.rend();
407  iter != iterEnd;
408  ++iter)
409  {
410  QList<QString> filterExtensions;
411  mitk::MimeType mimeType = *iter;
412  std::vector<std::string> extensions = mimeType.GetExtensions();
413  for (auto &extension : extensions)
414  {
415  filterExtensions << QString::fromStdString(extension);
416  }
417  if (m_DefaultExtension.isEmpty())
418  {
419  m_DefaultExtension = QString::fromStdString(extensions.front());
420  }
421 
422  QString filter = QString::fromStdString(mimeType.GetComment()) + " (";
423  foreach (const QString &extension, filterExtensions)
424  {
425  filter += "*." + extension + " ";
426  }
427  filter = filter.replace(filter.size() - 1, 1, ')');
428  m_MimeTypes.push_back(mimeType);
429  m_FilterStrings.push_back(filter);
430  }
431  }
432 
433  const mitk::IOUtil::SaveInfo m_SaveInfo;
434  std::vector<mitk::MimeType> m_MimeTypes;
435  QStringList m_FilterStrings;
436  QString m_DefaultExtension;
437 };
438 
440 {
441  static mitk::CustomMimeType allMimeType(std::string("__all__"));
442  return mitk::MimeType(allMimeType, -1, -1);
443 }
444 
446 {
447 }
448 
449 QmitkIOUtil::SaveFilter::SaveFilter(const SaveInfo &saveInfo) : d(new Impl(saveInfo))
450 {
451 }
452 
454 {
455  d.reset(new Impl(*other.d));
456  return *this;
457 }
458 
459 std::vector<mitk::MimeType> QmitkIOUtil::SaveFilter::GetMimeTypes() const
460 {
461  return d->m_MimeTypes;
462 }
463 
464 QString QmitkIOUtil::SaveFilter::GetFilterForMimeType(const std::string &mimeType) const
465 {
466  std::vector<mitk::MimeType>::const_iterator iter =
467  std::find_if(d->m_MimeTypes.begin(), d->m_MimeTypes.end(), MimeTypeComparison(mimeType));
468  if (iter == d->m_MimeTypes.end())
469  {
470  return QString();
471  }
472  int index = static_cast<int>(iter - d->m_MimeTypes.begin());
473  if (index < 0 || index >= d->m_FilterStrings.size())
474  {
475  return QString();
476  }
477  return d->m_FilterStrings[index];
478 }
479 
481 {
482  int index = d->m_FilterStrings.indexOf(filter);
483  if (index < 0)
484  {
485  return mitk::MimeType();
486  }
487  return d->m_MimeTypes[index];
488 }
489 
491 {
492  if (d->m_FilterStrings.size() > 1)
493  {
494  return d->m_FilterStrings.at(1);
495  }
496  else if (d->m_FilterStrings.size() > 0)
497  {
498  return d->m_FilterStrings.front();
499  }
500  return QString();
501 }
502 
504 {
505  return d->m_DefaultExtension;
506 }
507 
509 {
510  if (d->m_MimeTypes.size() > 1)
511  {
512  return d->m_MimeTypes.at(1);
513  }
514  else if (d->m_MimeTypes.size() > 0)
515  {
516  return d->m_MimeTypes.front();
517  }
518  return mitk::MimeType();
519 }
520 
522 {
523  return d->m_FilterStrings.join(";;");
524 }
525 
527 {
528  return d->m_FilterStrings.size();
529 }
530 
532 {
533  return d->m_FilterStrings.isEmpty();
534 }
535 
536 bool QmitkIOUtil::SaveFilter::ContainsMimeType(const std::string &mimeType)
537 {
538  return std::find_if(d->m_MimeTypes.begin(), d->m_MimeTypes.end(), MimeTypeComparison(mimeType)) !=
539  d->m_MimeTypes.end();
540 }
QString GetDefaultExtension() const
Data management class that handles 'was created by' relations.
itk::SmartPointer< Self > Pointer
FileWriterSelector m_WriterSelector
Contains a set of IFileWriter objects.
Definition: mitkIOUtil.h:74
static IMimeTypeProvider * GetMimeTypeProvider(us::ModuleContext *context=us::GetModuleContext())
Get an IMimeTypeProvider instance.
static void SavePointSetWithDialog(mitk::PointSet::Pointer pointset, std::string fileName="", QWidget *parent=NULL)
SavePointSetWithDialog Convenience method to save a pointset with a Qt dialog.
Base of all data objects.
Definition: mitkBaseData.h:39
bool IsValid() const
static mitk::MimeType ALL_MIMETYPE()
bool Select(const std::string &mimeType)
QString GetFilterForMimeType(const std::string &mimeType) const
bool ContainsMimeType(const std::string &mimeType)
std::vector< std::string > GetExtensions() const
std::string GetName() const
The CustomMimeType class represents a custom mime-type which may be registered as a service object...
MimeType m_MimeType
The selected mime-type, used to restrict results from FileWriterSelector.
Definition: mitkIOUtil.h:76
static QString GetFileOpenFilterString()
GetFilterString.
Definition: QmitkIOUtil.cpp:86
virtual std::vector< MimeType > GetMimeTypesForCategory(const std::string &category) const =0
mitk::MimeType GetMimeTypeForFilter(const QString &filter) const
#define mitkThrow()
virtual std::vector< std::string > GetCategories() const =0
Get a sorted and unique list of mime-type categories.
static void SaveImageWithDialog(mitk::Image::Pointer image, std::string fileName="", QWidget *parent=NULL)
SaveImageWithDialog Convenience method to save an image with a Qt dialog.
static QList< mitk::BaseData::Pointer > Load(const QStringList &paths, QWidget *parent=NULL)
Loads the specified files.
std::vector< MimeType > GetMimeTypes() const
static void SaveBaseDataWithDialog(mitk::BaseData *data, std::string fileName, QWidget *parent=NULL)
SaveBaseDataWithDialog Convenience method to save any data with a Qt dialog.
SaveFilter & operator=(const SaveFilter &other)
std::vector< mitk::MimeType > GetMimeTypes() const
The MimeType class represens a registered mime-type. It is an immutable wrapper for mitk::CustomMimeT...
Definition: mitkMimeType.h:45
QString GetDefaultFilter() const
SaveFilter(const SaveFilter &other)
QString ToString() const
static void SaveSurfaceWithDialog(mitk::Surface::Pointer surface, std::string fileName="", QWidget *parent=NULL)
SaveSurfaceWithDialog Convenience method to save a surface with a Qt dialog.
mitk::MimeType GetDefaultMimeType() const
std::string m_Path
The path to write the BaseData object to.
Definition: mitkIOUtil.h:78
static QString Save(const mitk::BaseData *data, const QString &defaultBaseName, const QString &defaultPath=QString(), QWidget *parent=NULL)
std::string GetComment() const
A RAII helper class for core service objects.
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.