diff --git a/Base/QTCore/qSlicerCoreIOManager.cxx b/Base/QTCore/qSlicerCoreIOManager.cxx index aa38017716ce72ea754786dfcf3083e121ade3fc..bbadb9017b9ed4e35e343bf683c0fd3c16749250 100644 --- a/Base/QTCore/qSlicerCoreIOManager.cxx +++ b/Base/QTCore/qSlicerCoreIOManager.cxx @@ -781,3 +781,19 @@ void qSlicerCoreIOManager::setDefaultSceneFileType(QString fileType) Q_D(qSlicerCoreIOManager); d->DefaultSceneFileType = fileType; } + +//----------------------------------------------------------------------------- +bool qSlicerCoreIOManager::examineFileInfoList(QFileInfoList &fileInfoList, QFileInfo &archetypeFileInfo, QString &readerDescription, qSlicerIO::IOProperties &ioProperties)const +{ + Q_D(const qSlicerCoreIOManager); + QList res; + foreach(qSlicerFileReader* reader, d->Readers) + { + if (reader->examineFileInfoList(fileInfoList, archetypeFileInfo, ioProperties)) + { + readerDescription = reader->description(); + return (true); + } + } + return (false); +} diff --git a/Base/QTCore/qSlicerCoreIOManager.h b/Base/QTCore/qSlicerCoreIOManager.h index d646a63143e37f7dce59e6056fdb608263137630..bbd6f785860f77cf54108bad4b66d55493fe6024 100644 --- a/Base/QTCore/qSlicerCoreIOManager.h +++ b/Base/QTCore/qSlicerCoreIOManager.h @@ -22,8 +22,9 @@ #define __qSlicerCoreIOManager_h // Qt includes -#include +#include #include +#include #include #include #include @@ -170,6 +171,21 @@ public: /// Defines the file format that should be offered by default when the scene is saved. Q_INVOKABLE QString defaultSceneFileType()const; + /// Iterates through readers looking at the fileInfoList to see if there is an entry that can serve as + /// an archetype for loading multiple fileInfos. If so, the reader removes the recognized + /// fileInfos from the list and sets the ioProperties so that the corresponding + /// loader will read these files. The archetypeEntry will contain the fileInfo + /// for the archetype and the method returns true. If no pattern is recognized + /// the method returns false. + /// The specific motivating use case is when the file + /// list contains a set of related files, such as a list of image files that + /// are recognized as a volume. But other cases could also make sense, such as when + /// a file format has a set or related files such as textures or material files + /// for a surface model. + /// \sa qSlicerDataDialog + /// \sa qSlicerFileReader + Q_INVOKABLE bool examineFileInfoList(QFileInfoList &fileInfoList, QFileInfo &archetypeEntry, QString &readerDescription, qSlicerIO::IOProperties &ioProperties)const; + public slots: /// Defines the file format that should be offered by default when the scene is saved. diff --git a/Base/QTCore/qSlicerFileReader.cxx b/Base/QTCore/qSlicerFileReader.cxx index 4a39ebe583d176dc6cc941f8f107ffeb79f30a04..c48fd972371b499eedd2560e1647ac8c1112f4ac 100644 --- a/Base/QTCore/qSlicerFileReader.cxx +++ b/Base/QTCore/qSlicerFileReader.cxx @@ -119,3 +119,9 @@ QStringList qSlicerFileReader::loadedNodes()const Q_D(const qSlicerFileReader); return d->LoadedNodes; } + +//---------------------------------------------------------------------------- +bool qSlicerFileReader::examineFileInfoList(QFileInfoList &fileInfoList, QFileInfo &archetypeFileInfo, qSlicerIO::IOProperties &ioProperties)const +{ + return(false); +} diff --git a/Base/QTCore/qSlicerFileReader.h b/Base/QTCore/qSlicerFileReader.h index b3ca9cfad68e09797b07013e6cd039858723f3f4..2e4d863beea85551891a6e79fb70a2738bc1eb1e 100644 --- a/Base/QTCore/qSlicerFileReader.h +++ b/Base/QTCore/qSlicerFileReader.h @@ -21,6 +21,9 @@ #ifndef __qSlicerFileReader_h #define __qSlicerFileReader_h +// Qt includes +#include + // QtCore includes #include "qSlicerIO.h" #include "qSlicerBaseQTCoreExport.h" @@ -64,6 +67,11 @@ public: /// \sa setLoadedNodes(), load() QStringList loadedNodes()const; + /// Implements the file list examination for the corresponding method in the core + /// IO manager. + /// \sa qSlicerCoreIOManager + virtual bool examineFileInfoList(QFileInfoList &fileInfoList, QFileInfo &archetypeFileInfo, qSlicerIO::IOProperties &ioProperties)const; + protected: /// Must be called in load() on success with the list of nodes added into the /// scene. diff --git a/Base/QTGUI/qSlicerDataDialog.cxx b/Base/QTGUI/qSlicerDataDialog.cxx index 8067217d915ffe07bfd7bdd6c919fc1842439c8b..88ba0a08389d0775e22596f1e6e7bddef2d3d7bd 100644 --- a/Base/QTGUI/qSlicerDataDialog.cxx +++ b/Base/QTGUI/qSlicerDataDialog.cxx @@ -163,7 +163,43 @@ void qSlicerDataDialogPrivate::addDirectory(const QDir& directory) bool recursive = true; QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::Readable | QDir::NoDotAndDotDot; - foreach(QFileInfo entry, directory.entryInfoList(filters)) + QFileInfoList fileInfoList = directory.entryInfoList(filters); + + // + // check to see if any readers recognize the directory contents + // and provide an archetype. + // + qSlicerCoreIOManager* coreIOManager = + qSlicerCoreApplication::application()->coreIOManager(); + QString readerDescription; + qSlicerIO::IOProperties ioProperties; + QFileInfo archetypeEntry; + if (coreIOManager->examineFileInfoList(fileInfoList, archetypeEntry, readerDescription, ioProperties)) + { + this->addFile(archetypeEntry); + QString filePath = archetypeEntry.absoluteFilePath(); + QList items = this->FileWidget->findItems(filePath, Qt::MatchExactly); + if (items.isEmpty()) + { + qWarning() << "Couldn't add archetype widget for file: " << filePath; + } + else + { + QTableWidgetItem *item = items[0]; + QWidget *cellWidget = this->FileWidget->cellWidget(item->row(), TypeColumn); + QComboBox *descriptionComboBox = dynamic_cast(cellWidget); + descriptionComboBox->setCurrentIndex(descriptionComboBox->findText(readerDescription)); + cellWidget = this->FileWidget->cellWidget(item->row(), OptionsColumn); + qSlicerIOOptionsWidget *ioOptionsWidget = dynamic_cast (cellWidget); + ioOptionsWidget->updateGUI(ioProperties); + } + } + + // + // now add any files and directories that weren't filtered + // out by the ioManager + // + foreach(QFileInfo entry, fileInfoList) { if (entry.isFile()) { diff --git a/Modules/Loadable/Volumes/qSlicerVolumesIOOptionsWidget.cxx b/Modules/Loadable/Volumes/qSlicerVolumesIOOptionsWidget.cxx index 7da12a3125d5cafd7d3939bb26e208c37108a9d1..3791e766d9c902cc8d592a8a10cee8a4be552ac0 100644 --- a/Modules/Loadable/Volumes/qSlicerVolumesIOOptionsWidget.cxx +++ b/Modules/Loadable/Volumes/qSlicerVolumesIOOptionsWidget.cxx @@ -223,3 +223,14 @@ void qSlicerVolumesIOOptionsWidget::updateColorSelector() } } } + +//------------------------------------------------------------------------------ +void qSlicerVolumesIOOptionsWidget::updateGUI(const qSlicerIO::IOProperties& ioProperties) +{ + Q_D(qSlicerVolumesIOOptionsWidget); + qSlicerIOOptionsWidget::updateGUI(ioProperties); + if (ioProperties.contains("singleFile")) + { + d->SingleFileCheckBox->setChecked(ioProperties["singleFile"].toBool()); + } +} diff --git a/Modules/Loadable/Volumes/qSlicerVolumesIOOptionsWidget.h b/Modules/Loadable/Volumes/qSlicerVolumesIOOptionsWidget.h index 15c12480e146b8ffa97353d373356b93c58a7baf..43e82da3a38cfa265aaafabf949d59d7e90f705d 100644 --- a/Modules/Loadable/Volumes/qSlicerVolumesIOOptionsWidget.h +++ b/Modules/Loadable/Volumes/qSlicerVolumesIOOptionsWidget.h @@ -41,6 +41,11 @@ public: qSlicerVolumesIOOptionsWidget(QWidget *parent=0); virtual ~qSlicerVolumesIOOptionsWidget(); + /// Allows custom handling of image sets as volumes + /// \sa qSlicerVolumesReader + /// \sa qSlicerDataDialog::addDirectory + void updateGUI(const qSlicerIO::IOProperties& ioProperties); + public slots: virtual void setFileName(const QString& fileName); virtual void setFileNames(const QStringList& fileNames); diff --git a/Modules/Loadable/Volumes/qSlicerVolumesReader.cxx b/Modules/Loadable/Volumes/qSlicerVolumesReader.cxx index 50fbd4af4cf0018733f4c7314642f9460e15b540..a4a0664cbd9342465ef8e16a3ba59fc06135ee49 100644 --- a/Modules/Loadable/Volumes/qSlicerVolumesReader.cxx +++ b/Modules/Loadable/Volumes/qSlicerVolumesReader.cxx @@ -18,7 +18,12 @@ ==============================================================================*/ +// std includes +#include +#include + // Qt includes +#include #include // SlicerQt includes @@ -39,6 +44,9 @@ #include #include +// ITK includes +#include + //----------------------------------------------------------------------------- class qSlicerVolumesReaderPrivate { @@ -205,3 +213,40 @@ bool qSlicerVolumesReader::load(const IOProperties& properties) } return node != 0; } + +//----------------------------------------------------------------------------- +bool qSlicerVolumesReader::examineFileInfoList(QFileInfoList &fileInfoList, QFileInfo &archetypeFileInfo, qSlicerIO::IOProperties &ioProperties)const +{ + + // + // Check each file to see if it's recognzied as part of a series. If so, + // keep it as the archetype and remove all the others from the list + // + foreach(QFileInfo fileInfo, fileInfoList) + { + itk::ArchetypeSeriesFileNames::Pointer seriesNames = itk::ArchetypeSeriesFileNames::New(); + std::vector candidateFiles; + seriesNames->SetArchetype(fileInfo.absoluteFilePath().toStdString()); + candidateFiles = seriesNames->GetFileNames(); + if (candidateFiles.size() > 1) + { + archetypeFileInfo = fileInfo; + QMutableListIterator fileInfoIterator(fileInfoList); + while (fileInfoIterator.hasNext()) + { + const QString &path = fileInfoIterator.next().absoluteFilePath(); + if (path == archetypeFileInfo.absoluteFilePath()) + { + continue; + } + if (std::find(candidateFiles.begin(), candidateFiles.end(), path.toStdString()) != candidateFiles.end()) + { + fileInfoIterator.remove(); + } + } + ioProperties["singleFile"] = false; + return true; + } + } + return false; +} diff --git a/Modules/Loadable/Volumes/qSlicerVolumesReader.h b/Modules/Loadable/Volumes/qSlicerVolumesReader.h index 48f6b1d9fdc436e8c2452a18a3fbf04f1eb78d21..842f5c40a0c3ce6e3002983fc3dbd9a42a8442e1 100644 --- a/Modules/Loadable/Volumes/qSlicerVolumesReader.h +++ b/Modules/Loadable/Volumes/qSlicerVolumesReader.h @@ -47,6 +47,12 @@ public: virtual qSlicerIOOptions* options()const; virtual bool load(const IOProperties& properties); + + /// Implements the file list examination for the corresponding method in the core + /// IO manager. + /// \sa qSlicerCoreIOManager + virtual bool examineFileInfoList(QFileInfoList &fileInfoList, QFileInfo &archetypeFileInfo, qSlicerIO::IOProperties &ioProperties)const; + protected: QScopedPointer d_ptr;