BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
RealDataSelectorWidget.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/Import/RealDataSelectorWidget.cpp
6 //! @brief Implements class RealDataSelectorWidget
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
16 #include "Device/Data/DataUtils.h"
17 #include "Device/Data/Datafield.h"
23 #include "GUI/Util/Error.h"
31 #include <QFileDialog>
32 #include <QItemSelectionModel>
33 #include <QLineEdit>
34 #include <QListView>
35 #include <QMenu>
36 #include <QSplitter>
37 #include <QTreeView>
38 #include <QVBoxLayout>
39 
41  : QWidget(parent)
42  , m_itemTree(new QTreeView(this))
43  , m_itemTreeModel(new RealDataTreeModel(this, document->realDataModel()))
44  , m_propertiesWidget(new RealDataPropertiesWidget(this, document))
45  , m_document(document)
46  , m_import1dDataAction(new QAction(this))
47  , m_import2dDataAction(new QAction(this))
48  , m_renameDataAction(new QAction(this))
49  , m_removeDataAction(new QAction(this))
50 {
51  setProperty("stylable", true);
52  setAttribute(Qt::WA_StyledBackground, true);
53 
54  setMinimumSize(250, 600);
55  setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
56  setWindowTitle("RealDataSelectorWidget");
57 
58  m_import2dDataAction->setText("Import 2D data");
59  m_import2dDataAction->setIcon(QIcon(":/images/shape-square-plus.svg"));
60  m_import2dDataAction->setIconText("2D");
61  m_import2dDataAction->setToolTip("Import 2D data");
62  connect(m_import2dDataAction, &QAction::triggered, [this]() { importData2D(); });
63 
64  m_import1dDataAction->setText("Import 1D data");
65  m_import1dDataAction->setIcon(QIcon(":/images/shape-square-plus.svg"));
66  m_import1dDataAction->setIconText("1D");
67  m_import1dDataAction->setToolTip("Import 1D data");
68  connect(m_import1dDataAction, &QAction::triggered, [this]() { importData1D(); });
69 
70  m_renameDataAction->setText("Rename");
71  m_renameDataAction->setIcon(QIcon()); // #baTODO: Icon needed?
72  m_renameDataAction->setIconText("Rename");
73  m_renameDataAction->setToolTip("Rename data");
74  connect(m_renameDataAction, &QAction::triggered, this,
76 
77  m_removeDataAction->setText("Remove");
78  m_removeDataAction->setIcon(QIcon(":/images/delete.svg"));
79  m_removeDataAction->setIconText("Remove");
80  m_removeDataAction->setToolTip("Remove selected data");
81  connect(m_removeDataAction, &QAction::triggered, this,
83 
84  // #baimport ++ following line necessary for tree as well? relevant for RealItems at all?
85  // m_itemTree->setAttribute(Qt::WA_MacShowFocusRect, false);
86  m_itemTree->setItemsExpandable(false);
87  m_itemTree->setRootIsDecorated(false);
88  m_itemTree->setHeaderHidden(true);
89  m_itemTree->setContextMenuPolicy(Qt::CustomContextMenu);
90  m_itemTree->setModel(m_itemTreeModel);
91 
92  auto* splitter = new QSplitter;
93  splitter->setOrientation(Qt::Vertical);
94  splitter->addWidget(m_itemTree);
95  splitter->addWidget(m_propertiesWidget);
96  splitter->setChildrenCollapsible(true);
97 
98  auto* mainLayout = new QVBoxLayout;
99  mainLayout->setMargin(0);
100  mainLayout->setSpacing(0);
101  mainLayout->setContentsMargins(0, 0, 0, 0);
102  mainLayout->addWidget(splitter);
103 
104  setLayout(mainLayout);
105 
106  connect(m_itemTree, &QTreeView::customContextMenuRequested, this,
108 
109  connect(m_document, &ProjectDocument::modified, this,
111 
112  connect(m_itemTreeModel, &QAbstractItemModel::modelReset,
113  [this]() { m_itemTree->expandAll(); });
114 
115  connect(m_itemTreeModel, &QAbstractItemModel::rowsInserted,
116  [this]() { m_itemTree->expandAll(); });
117 
118  connect(m_itemTree->selectionModel(), &QItemSelectionModel::selectionChanged, this,
119  &RealDataSelectorWidget::onSelectionChanged, Qt::UniqueConnection);
120 
121  m_itemTree->expandAll();
124 
125  // ensure a current item when widget is shown
127  if (currentItem())
129 
131  m_itemTree, [=](const QModelIndex& i, bool h) { return getOverlayActions(i, h); });
132 }
133 
135 {
136  return QSize(200, 400);
137 }
138 
140 {
141  return QSize(128, 200);
142 }
143 
145 {
147 }
148 
150 {
151  m_itemTree->selectionModel()->clearSelection();
152  QModelIndex index = m_itemTreeModel->indexForItem(item);
153  if (index.isValid())
154  m_itemTree->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent);
155 }
156 
158 {
159  return m_itemTree->selectionModel()->currentIndex();
160 }
161 
162 QList<QAction*> RealDataSelectorWidget::getOverlayActions(const QModelIndex& index, bool asHover)
163 {
164  if (m_itemTreeModel->isHeadline(index)) {
165  if (asHover)
166  return {};
167 
168  return (index == m_itemTreeModel->indexOfHeadline(1))
169  ? QList<QAction*>({m_import1dDataAction})
170  : QList<QAction*>({m_import2dDataAction});
171  }
172 
173  // -- index belongs to item
174  if (!asHover)
175  return {};
176 
177  auto* item = m_itemTreeModel->itemForIndex(index);
178  if (item == nullptr)
179  return {};
180 
181  auto* removeAction = new QAction(this);
182  removeAction->setText("Remove");
183  removeAction->setIcon(QIcon(":/images/delete.svg"));
184  removeAction->setIconText("Remove");
185  removeAction->setToolTip("Remove this data");
186  connect(removeAction, &QAction::triggered, [=]() { m_itemTreeModel->removeItem(item); });
187 
188  return {removeAction};
189 }
190 
192 {
196 }
197 
199 {
200  auto* realDataItemAtPoint = m_itemTreeModel->itemForIndex(m_itemTree->indexAt(point));
201  updateActionEnabling(realDataItemAtPoint);
202 
203  QMenu menu;
204  menu.setToolTipsVisible(true);
205 
206  if (realDataItemAtPoint != nullptr) {
207  menu.addAction(m_renameDataAction);
208  menu.addAction(m_removeDataAction);
209  menu.addSeparator();
210  }
211 
212  menu.addAction(m_import2dDataAction);
213  menu.addAction(m_import1dDataAction);
214  menu.exec(m_itemTree->mapToGlobal(point));
215 }
216 
218 {
219  QMap<QString, AbstractDataLoader1D*> loaderOfFilter;
220  QString filters;
221  for (auto* loader : DataLoaders1D::instance().loaders()) {
222  const QString filter =
223  loader->name() + " (*.txt *.csv *.dat)"; // #baimport - take file filters from loader
224  loaderOfFilter[filter] = loader;
225 
226  if (!filters.isEmpty())
227  filters += ";;";
228  filters += filter;
229  }
230  filters += ";;Other (*.*)";
231 
232  QString selectedFilter = ProjectManager::instance()->recentlyUsedImportFilter1D();
233  const QString dirname = ProjectManager::instance()->userImportDir();
234 
235  const QStringList fileNames = QFileDialog::getOpenFileNames(
236  Q_NULLPTR, "Open Intensity Files", dirname, filters, &selectedFilter,
237  appSettings->useNativeFileDialog() ? QFileDialog::Options()
238  : QFileDialog::DontUseNativeDialog);
239 
240  if (fileNames.isEmpty())
241  return;
242 
245 
246  const AbstractDataLoader* selectedLoader = loaderOfFilter.value(selectedFilter, nullptr);
247  for (const auto& fileName : fileNames) {
249  realDataItem->setDataName(QFileInfo(fileName).baseName());
250  realDataItem->setNativeFileName(fileName);
251 
252  const QString errorText =
253  GUI::View::ImportDataUtils::Import1dData(realDataItem, selectedLoader);
254  if (errorText.isEmpty())
255  setCurrentItem(realDataItem);
256  else {
257  m_itemTreeModel->removeItem(realDataItem);
258  GUI::View::Helpers::warning(this, "File import",
259  QString("The file '%1' could not be imported.")
260  .arg(QDir::toNativeSeparators(fileName)),
261  errorText);
262  }
263  }
264 }
265 
267 {
268  QMap<QString, IOFactory::LoaderSelector> loaderOfFilter;
269  QString filters;
270 
271  const auto addFilter = [&](IOFactory::LoaderSelector loaderSelector, const QString& filter) {
272  if (!filters.isEmpty())
273  filters += ";;";
274  filters += filter;
275  loaderOfFilter[filter] = loaderSelector;
276  };
277  addFilter(IOFactory::tiff, "TIFF (*.tif *.tiff *.tif.gz)");
278  addFilter(IOFactory::nicos, "Nicos/SANSDRaw (*.001)");
279  addFilter(IOFactory::bornagain, "BornAgain (*.int.gz)");
280  addFilter(IOFactory::automatic, "CSV (*.txt *.csv *.dat)");
281  addFilter(IOFactory::automatic, "All (*.*)");
282 
283  QString selectedFilter = ProjectManager::instance()->recentlyUsedImportFilter2D();
284 
285  const QString dirname = ProjectManager::instance()->userImportDir();
286  const QStringList fileNames = QFileDialog::getOpenFileNames(
287  Q_NULLPTR, "Open Intensity Files", dirname, filters, &selectedFilter,
288  appSettings->useNativeFileDialog() ? QFileDialog::Options()
289  : QFileDialog::DontUseNativeDialog);
290  if (fileNames.isEmpty())
291  return;
292 
295 
296  const auto selectedLoader = loaderOfFilter.value(selectedFilter, IOFactory::automatic);
297 
298  for (const auto& fileName : fileNames) {
299  std::unique_ptr<Datafield> data =
300  GUI::View::ImportDataUtils::Import2dData(fileName, selectedLoader);
301  if (!data)
302  continue;
304  realDataItem->setDataName(QFileInfo(fileName).baseName());
305 
306  try {
307  if (data->rank() != 2)
308  throw Error("The content could not be interpreted correctly as 2-dimensional.");
309 
310  realDataItem->setDatafield(data.release());
311  setCurrentItem(realDataItem);
312  } catch (std::exception& ex) {
313  m_itemTreeModel->removeItem(realDataItem);
314 
315  const QString message = QString("Error while trying to read file\n\n'%1'\n\n%2")
316  .arg(fileName)
317  .arg(QString::fromStdString(std::string(ex.what())));
318  GUI::View::Helpers::warning(this, "File import", message);
319  }
320  }
321 }
322 
324 {
325  if (currentIndex().isValid()) {
326  // First delete any existing index widget. This is necessary if overlay buttons are used,
327  // because these buttons use the indexWidget() functionality, which itself uses the same
328  // internals as an item editor
329  m_itemTree->setIndexWidget(currentIndex(), nullptr);
330  m_itemTree->edit(currentIndex());
331  }
332 }
333 
335 {
337 }
338 
340 {
342 }
343 
345 {
346  m_import2dDataAction->setEnabled(true);
347  m_import1dDataAction->setEnabled(true);
348 
349  m_removeDataAction->setEnabled(item != nullptr);
350  m_renameDataAction->setEnabled(item != nullptr);
351 }
352 
354 {
355  QSet<int> visibleRanks;
357  visibleRanks << 1;
361  visibleRanks << 2;
362 
363  m_itemTreeModel->setVisibleRanks(visibleRanks);
364 }
Defines class AbstractDataLoader1D.
ApplicationSettings * appSettings
global pointer to the instance
Defines class ApplicationSettings.
Defines class DataLoaders1D.
Defines error class.
Defines ImportDataUtils namespace.
Defines class ItemViewOverlayButtons.
Defines class GUIHelpers functions.
Defines class ProjectManager.
Defines class RealDataItem.
Defines class RealDataModel.
Defines class RealDataPropertiesWidget.
Defines class RealDataSelectorWidget.
Defines class RealDataTreeModel.
Defines class StyledToolbar.
Abstract base class for all data loaders (classes to import real data).
bool useNativeFileDialog() const
static DataLoaders1D & instance()
The one and only instance.
static void install(QAbstractItemView *view, FnGetActions fnGetActions)
Project document class handles all data related to the opened project (sample, job,...
Functionalities functionalities() const
void modified()
Emitted for any modifications in the document.
void setRecentlyUsedImportFilter1D(const QString &filter)
static ProjectManager * instance()
void setImportDirFromFilePath(const QString &filePath)
Sets user import directory in system settings.
QString recentlyUsedImportFilter2D() const
void setRecentlyUsedImportFilter2D(const QString &filter)
QString userImportDir() const
Returns directory name which was used by the user to import files.
QString recentlyUsedImportFilter1D() const
Provides access to experimental data, for display and fitting. Owns an AbstractDataLoader.
Definition: RealDataItem.h:33
void setDataName(const QString &name)
void setNativeFileName(const QString &filename)
The name from where the native data was originally imported.
void setDatafield(Datafield *data)
Sets Datafield to underlying item. Creates it if not existing.
The RealDataPropertiesWidget class holds instrument selector to link with RealDataItem....
void setItem(RealDataItem *item)
Set current RealDataItem to display in instrument selector.
void selectionChanged(RealDataItem *)
RealDataSelectorWidget(QWidget *parent, ProjectDocument *document)
void setCurrentItem(RealDataItem *item)
RealDataTreeModel * m_itemTreeModel
void onContextMenuRequest(const QPoint &point)
QSize minimumSizeHint() const override
QList< QAction * > getOverlayActions(const QModelIndex &index, bool asHover)
RealDataPropertiesWidget * m_propertiesWidget
QSize sizeHint() const override
Tree model for real data item selection. Used for the tree in the import view.
QModelIndex indexForItem(RealDataItem *item) const
RealDataItem * insertIntensityDataItem()
bool isHeadline(const QModelIndex &index) const
RealDataItem * topMostItem() const
The topmost visible item. Can be null of course.
RealDataItem * insertSpecularDataItem()
RealDataItem * itemForIndex(const QModelIndex &index) const
void removeItem(RealDataItem *item)
void setVisibleRanks(QSet< int > visibleRanks)
QModelIndex indexOfHeadline(int rank) const
QString baseName(const QString &fileName)
Returns base name of file.
Definition: Path.cpp:133
void warning(QWidget *parent, const QString &title, const QString &text, const QString &detailedText)
Definition: MessageBox.cpp:37
QString Import1dData(RealDataItem *realDataItem, const AbstractDataLoader *selectedLoader)
Imports 1D data into the given item.
std::unique_ptr< Datafield > Import2dData(const QString &fileName, IOFactory::LoaderSelector loader)
Imports 2D data, stores them as Datafield, and returns owning pointer.