BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
SpecularDataImportWidget.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/Views/SpecularDataWidgets/SpecularDataImportWidget.cpp
6 //! @brief Implements class SpecularDataImportWidget
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2021
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
28 #include "ui_SpecularDataImportWidget.h"
29 #include <QAction>
30 #include <QBoxLayout>
31 #include <QFileDialog>
32 #include <QMenu>
33 #include <QStringListModel>
34 #include <QTextStream>
35 #include <QTimer>
36 
38  : SessionItemWidget(parent), m_ui(new Ui::SpecularDataImportWidget), m_loader(nullptr)
39 {
40  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
41 
42  m_ui->setupUi(this);
43  m_ui->warningsIcon->setFixedSize(20, 20);
44  m_ui->warningsIcon->setPixmap(QPixmap(":/images/warning_16x16.png"));
45 
46  m_ui->linkedInstrumentGroup->hide(); // #baimport - remove from UI if not used in the future
47 
48  // #baUserDefLoaders - remove next line when implementation is complete
49  m_ui->createNewFormatButton->hide();
50 
53 
54  connect(m_ui->formatSelectionComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
56  connect(m_ui->createNewFormatButton, &QPushButton::clicked, this,
58 
59  connect(m_ui->originalRowCheckBox, &QCheckBox::stateChanged, this,
61 
62  connect(m_ui->rawDataCheckBox, &QCheckBox::stateChanged, this,
64 
65  connect(m_ui->calculatedDataCheckBox, &QCheckBox::stateChanged, this,
67 
68  connect(m_ui->specularDataCanvas->customPlot(), &QCustomPlot::axisClick, this,
70 
71  m_ui->specularDataCanvas->enableDeprecatedOnMousePress(false); // we have an own handler
72 
73  m_ui->plotToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
74  for (auto action : m_ui->specularDataCanvas->actionList())
75  m_ui->plotToolBar->addAction(action);
76 }
77 
79 {
80  SessionItemWidget::setItem(_realDataItem);
81  m_ui->specularDataCanvas->setItem(specularDataItem());
82 
84  ASSERT(m_loader); // only items which have a loader are allowed for this widget. Every other
85  // items do not support this widget
86 
88 
89  QSignalBlocker b(m_ui->formatSelectionComboBox);
90  m_ui->formatSelectionComboBox->setCurrentText(m_loader->name());
91 
93  updatePreview();
96 }
97 
99 {
100  return {};
101 }
102 
104 {
105  QMenu menu;
106  for (auto action : actionList())
107  menu.addAction(action);
108  menu.exec(point);
109 }
110 
111 void SpecularDataImportWidget::onPlotAxisClicked(QCPAxis* axis, QCPAxis::SelectablePart /*part*/,
112  QMouseEvent* event)
113 {
114  if (event->button() == Qt::RightButton && axis->axisType() == QCPAxis::atLeft) {
115  QMenu menu;
116 
117  QAction* lin = new QAction("Linear");
118  connect(lin, &QAction::triggered, [=]() { specularDataItem()->setLog(false); });
119  lin->setCheckable(true);
120  lin->setChecked(!specularDataItem()->isLog());
121 
122  QAction* log = new QAction("Logarithmic");
123  connect(log, &QAction::triggered, [=]() { specularDataItem()->setLog(true); });
124  log->setCheckable(true);
125  log->setChecked(specularDataItem()->isLog());
126 
127  auto ag = new QActionGroup(&menu);
128  ag->addAction(lin);
129  ag->addAction(log);
130 
131  menu.addAction(lin);
132  menu.addAction(log);
133 
134  menu.exec(event->globalPos());
135  }
136 }
137 
139 {
141 }
142 
144 {
145  return dynamic_cast<const RealDataItem*>(currentItem());
146 }
147 
149 {
150  return dynamic_cast<RealDataItem*>(currentItem());
151 }
152 
154 {
155  QSignalBlocker b(m_ui->formatSelectionComboBox);
156  m_ui->formatSelectionComboBox->clear();
157  for (auto loader : DataLoaders1D::instance().recentlyUsedLoaders()) {
158  m_ui->formatSelectionComboBox->addItem(loader->name());
159  }
160  for (auto loader : DataLoaders1D::instance().loaders()) {
161  m_ui->formatSelectionComboBox->addItem(loader->name());
162  }
163 
164  // e.g. legacy loader is not present in the combo by default. Add it here so it can be selected
165  if (m_loader != nullptr)
166  if (m_ui->formatSelectionComboBox->findText(m_loader->name()) < 0)
167  m_ui->formatSelectionComboBox->addItem(m_loader->name());
168 }
169 
171 {
172  for (auto child : m_ui->propertiesWidget->children()) {
173  delete child;
174  }
175 
176  if (m_ui->propertiesWidget->layout())
177  delete m_ui->propertiesWidget->layout();
178 
179  if (m_loader) {
180  m_loader->populateImportSettingsWidget(m_ui->propertiesWidget);
181  }
182 
183  const bool hasChildren = !m_ui->propertiesWidget->children().empty();
184 
185  m_ui->propertiesWidget->setVisible(hasChildren);
186 }
187 
189 {
190  const QString name = m_ui->formatSelectionComboBox->currentText();
191 
192  for (auto loader : DataLoaders1D::instance().loaders())
193  if (name == loader->name())
194  return loader;
195 
196  return nullptr;
197 }
198 
200 {
201  if (m_loader && m_loader->fileContent().isEmpty()) {
202 
203  QSignalBlocker b(m_ui->formatSelectionComboBox);
204  m_ui->formatSelectionComboBox->setCurrentText(m_loader->name());
205 
207  "Changing the loader is not possible because the original file "
208  "contents are not available any more.");
209 
210  return;
211  }
212 
213  if (m_loader)
214  m_loader->disconnect(this);
215 
216  m_loader = dynamic_cast<AbstractDataLoader1D*>(selectedLoader()->clone());
221  QApplication::setOverrideCursor(Qt::WaitCursor);
224  QApplication::restoreOverrideCursor();
225 
227  updatePreview();
230 }
231 
233 {
234  QApplication::setOverrideCursor(Qt::WaitCursor);
235 
236  if (m_loader) {
237  auto oldModel = m_ui->dataResultView->selectionModel(); // sic!! according to Qt docu
238  // of QAbstractItemView::setModel
239  auto resultModel = m_loader->createResultModel();
240  if (resultModel != nullptr) {
241 
242  const auto originalSections = resultModel->sectionsOfColumnType(
244 
245  const auto rawSections =
246  resultModel->sectionsOfColumnType(AbstractDataLoaderResultModel::ColumnType::raw);
247 
248  const auto processedSections = resultModel->sectionsOfColumnType(
250 
251  QSignalBlocker b1(m_ui->originalRowCheckBox);
252  QSignalBlocker b2(m_ui->rawDataCheckBox);
253  QSignalBlocker b3(m_ui->calculatedDataCheckBox);
254 
255  if (originalSections.isEmpty()) {
256  m_ui->originalRowCheckBox->setChecked(false);
257  m_ui->originalRowCheckBox->setEnabled(false);
258  } else
259  m_ui->originalRowCheckBox->setEnabled(true);
260 
261  if (rawSections.isEmpty()) {
262  m_ui->rawDataCheckBox->setChecked(false);
263  m_ui->rawDataCheckBox->setEnabled(false);
264  } else
265  m_ui->rawDataCheckBox->setEnabled(true);
266 
267  if (processedSections.isEmpty()) {
268  m_ui->calculatedDataCheckBox->setChecked(false);
269  m_ui->calculatedDataCheckBox->setEnabled(false);
270  } else
271  m_ui->calculatedDataCheckBox->setEnabled(true);
272 
273  m_ui->dataResultView->setModel(resultModel);
274  auto horHeader = m_ui->dataResultView->horizontalHeader();
275 
276  for (int section : originalSections)
277  horHeader->setSectionHidden(section, !m_ui->originalRowCheckBox->isChecked());
278 
279  for (int section : rawSections)
280  horHeader->setSectionHidden(section, !m_ui->rawDataCheckBox->isChecked());
281 
282  for (int section : processedSections)
283  horHeader->setSectionHidden(section, !m_ui->calculatedDataCheckBox->isChecked());
284 
285  // if the result model has a line column, then do not show the vertical header view
286  const bool hasLinesColumn =
287  !resultModel->sectionsOfColumnType(AbstractDataLoaderResultModel::ColumnType::line)
288  .isEmpty();
289  m_ui->dataResultView->verticalHeader()->setHidden(hasLinesColumn);
290 
291  m_ui->dataResultView->resizeColumnsToContents();
292  } else
293  m_ui->dataResultView->setModel(nullptr);
294 
295  delete oldModel;
296  }
297 
298  if (m_loader && m_loader->numErrors() > 0) {
299  m_ui->warningsIcon->show();
300  m_ui->warningsLabel->show();
301  m_ui->warningsListWidget->show();
302  m_ui->warningsListWidget->clear();
303 
304  auto warnings = m_loader->lineUnrelatedErrors();
305  const int nLineRelatedWarnings = m_loader->numErrors() - warnings.size();
306 
307  if (nLineRelatedWarnings == 1)
308  warnings << "1 line related warning. Please check the data tab on the right for more "
309  "information.";
310  else if (nLineRelatedWarnings > 1)
311  warnings
312  << QString(
313  "%1 line related warnings. Please check the data tab on the right for more "
314  "information.")
315  .arg(nLineRelatedWarnings);
316 
317  if (warnings.size() > 1)
318  for (auto& w : warnings)
319  w.prepend("* ");
320 
321  for (auto& w : warnings)
322  new QListWidgetItem(w, m_ui->warningsListWidget);
323 
324  } else {
325  m_ui->warningsIcon->hide();
326  m_ui->warningsLabel->hide();
327  m_ui->warningsListWidget->hide();
328  }
329 
330  QApplication::restoreOverrideCursor();
331 }
332 
334 {
335  bool ok;
336  QString name = QInputDialog::getText(
337  this, "New format", "Please enter a name for the new format", QLineEdit::Normal, "", &ok);
338  if (!ok || name.isEmpty())
339  return;
340 
342 
343  fillLoaderCombo();
344  m_ui->formatSelectionComboBox->setCurrentText(name);
346 }
347 
349 {
350  m_loader->applyImportSettings(); // #baimport: may be duplicate
351  QApplication::setOverrideCursor(Qt::WaitCursor);
353  QApplication::restoreOverrideCursor();
354 
355  // If there is a linked instrument, any change in import settings can break the compatibility.
356  // Therefore check the compatibility and break the link if necessary
357  if (realDataItem()->linkedInstrument() != nullptr) {
358  if (!realDataItem()->linkedInstrument()->alignedWith(realDataItem()))
360  }
361 
362  updatePreview();
363 }
364 
366 {
367  return realDataItem()->nativeFileName();
368 }
Defines class AbstractDataLoader1D.
Defines class AbstractDataLoaderResultModel.
Defines class AbstractDataLoader.
Defines class AppSvc.
#define ASSERT(condition)
Definition: Assert.h:31
Defines namespace DataItemUtils.
Defines class DataLoaders1D.
Defines class MainWindow.
Defines class ProjectManager.
Defines InstrumentItems classes.
Defines class QREDataLoader.
Defines class RealDataItem.
Defines class SpecularDataImportWidget.
Defines class SpecularDataItem.
Base class for data loaders for 1D import.
QVector< int > sectionsOfColumnType(ColumnType type) const
The table header sections which belong to the given column type.
Base class for all data loaders (classes which can import real data)
virtual void applyImportSettings()
Read all values from the properties UI into the internal variables.
virtual void populateImportSettingsWidget(QWidget *parent)
Fills the widget on the import dialog pane.
virtual AbstractDataLoaderResultModel * createResultModel() const
Create a table model which contains the import information like original file content,...
virtual void guessSettings()
Guess appropriate settings (for example the separator in a CSV file).
void importSettingsChanged()
Emitted whenever an import setting changed.
virtual QString name() const =0
The name shown in the format selection combo.
virtual void setFileContents(const QByteArray &fileContent)=0
Sets the file contents to be imported.
virtual int numErrors() const
Number of errors found while processing the content.
virtual void processContents()=0
Process the file contents.
virtual void initWithDefaultImportSettings()
Set import settings to defaults.
virtual QByteArray fileContent() const
Returns the original file content.
virtual QStringList lineUnrelatedErrors() const
Errors not related to a particular line.
virtual AbstractDataLoader * clone() const =0
Create a complete clone, including all internal states.
void setRealDataItem(RealDataItem *item)
Define the real data item on which the import shall work.
void cloneAsUserDefinedLoader(AbstractDataLoader *loader, const QString &name)
Clone the loader and create a user defined loader with its current settings and the given name.
static DataLoaders1D & instance()
The one and only instance.
static MainWindow * instance()
Returns the one and only instance of this class.
Definition: mainwindow.cpp:129
The RealDataItem class represents intensity data imported from file and intended for fitting.
Definition: RealDataItem.h:35
void clearInstrumentId()
AbstractDataLoader * dataLoader() const
QString nativeFileName() const
void setDataLoader(AbstractDataLoader *loader)
Takes ownership of loader.
The SessionItemWidget class is a base for all widgets representing the content of SessionItem.
SessionItem * currentItem()
virtual void setItem(SessionItem *item)
Widget to define and show 1D (specular) imports by data loaders, e.g.
Ui::SpecularDataImportWidget * m_ui
AbstractDataLoader1D * m_loader
void onPlotAxisClicked(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
AbstractDataLoader * selectedLoader()
void setItem(SessionItem *realDataItem)
SpecularDataImportWidget(QWidget *parent=nullptr)
void onContextMenuRequest(const QPoint &point)
const RealDataItem * realDataItem() const
void setLog(bool log_flag)
SpecularDataItem * specularDataItem(SessionItem *parent)
Returns SpecularDataItem contained as a child in givent parent.
void information(QWidget *parent, const QString &title, const QString &text, const QString &detailedText)
Definition: GUIHelpers.cpp:54
QString const & name(EShape k)
Definition: particles.cpp:21