BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
DataSelector.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/Views/ImportDataWidgets/CsvImportAssistant/DataSelector.cpp
6 //! @brief Implements class DataSelector
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 
19 #include <QFileDialog>
20 #include <QFormLayout>
21 #include <QGroupBox>
22 #include <QMenu>
23 #include <QMessageBox>
24 #include <QPushButton>
25 #include <QSettings>
26 #include <QTableWidget>
27 #include <QVBoxLayout>
28 #include <locale>
29 #include <sstream>
30 
31 namespace {
32 const QSize default_dialog_size(300, 400);
33 }
34 
35 DataSelector::DataSelector(csv::DataArray csvArray, QWidget* parent)
36  : QDialog(parent)
37  , m_data(csvArray)
38  , m_tableWidget(nullptr)
39  , m_separatorField(nullptr)
40  , m_firstDataRowSpinBox(nullptr)
41  , m_lastDataRowSpinBox(nullptr)
42  , m_coordinateUnitsComboBox(nullptr)
43  , m_importButton(nullptr)
44  , m_cancelButton(nullptr)
45  , m_errorLabel(nullptr)
46 {
47  setWindowTitle("Data Importer");
48  setMinimumSize(default_dialog_size);
49  resize(600, 600);
50  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
52  setLayout(createLayout());
53 
54  if (!updateData())
55  return;
56 }
57 
59 {
60  size_t lastRow = m_data.size();
61 
62  if (lastRow < 1) {
63  m_importButton->setDisabled(true);
64  CsvImportAssistant::showErrorMessage("There is no data to show");
65  return false;
66  }
67 
69  m_firstDataRowSpinBox->setMaximum(int(lastRow));
70  m_lastDataRowSpinBox->setMaximum(int(lastRow));
71  m_lastDataRowSpinBox->setValue(int(lastRow));
72 
73  return true;
74 }
75 
77 {
78  setColumnAs(ct);
79 }
80 
81 bool DataSelector::isInsideTable(const QPoint position)
82 {
83  auto item = m_tableWidget->itemAt(position);
84 
85  if (!item)
86  return false;
87 
88  auto row = item->row();
89  auto col = item->column();
90  if (row * col < 0)
91  return false;
92 
93  return true;
94 }
95 
96 void DataSelector::onColumnRightClick(const QPoint& position)
97 {
98  if (!isInsideTable(position))
99  return;
100 
101  auto globalPos = m_tableWidget->mapToGlobal(position);
102 
103  TableContextMenu contextMenu(this);
104  connect(&contextMenu, &TableContextMenu::setFirstRow, this, [this]() { setFirstRow(); });
105  connect(&contextMenu, &TableContextMenu::setLastRow, this, [this]() { setLastRow(); });
106  connect(&contextMenu, &TableContextMenu::setColumnAs, this, &DataSelector::setColumnSlot);
107  connect(&contextMenu, &TableContextMenu::discardRow, this, [this]() { discardRow(); });
108  connect(&contextMenu, &TableContextMenu::resetTable, this, [this]() {
109  resetSelection();
110  updateSelection();
111  });
112  contextMenu.exec(globalPos);
113 }
114 
116 {
117  m_importButton->setEnabled(false);
118  m_coordinateUnitsComboBox->setEnabled(false);
121 
122  if (!m_tableWidget->dataLooksGood()) {
123  m_errorLabel->setText("\n\n"
124  "Data selected for importing does not look good!\n\n"
125  "Make sure that:\n"
126  " 1. There are no repeated values in the coordinate column.\n"
127  " 2. The coordinate values are ascendingly sorted.\n"
128  " 3. Intensity and coordinate values are valid numbers.\n\n"
129  "Use the context menu of the table to manually discard some rows;\n"
130  "Alternatively, modify the file in an external editor.");
131  } else {
132  m_errorLabel->clear();
133  }
134 
135  // Enable import button only if the user has selected its columns for 1d
136  if (m_tableWidget->intensityColumn() > -1)
138  m_importButton->setEnabled(true);
139 
140  // Enable Coordinate Selector
141  if (m_tableWidget->coordinateColumn() > -1) {
142  m_coordinateUnitsComboBox->setEnabled(true);
143  } else {
144  m_coordinateUnitsComboBox->clear();
145  m_coordinateUnitsComboBox->addItem(axisUnitLabel.at(Axes::Units::NBINS));
146  }
147 }
148 
150 {
151  m_tableWidget->setColumnAs(col, coordOrInt);
153  updateSelection();
154 }
155 
157 {
158  QList<QString> available_units = m_tableWidget->availableCoordinateUnits();
159  m_coordinateUnitsComboBox->clear();
160  for (auto units : available_units)
162 }
163 
165 {
166  auto col = m_tableWidget->selectedColumn();
167  if (col < 0)
168  return;
169 
170  setColumnAs(col, coordOrInt);
171 }
172 
174 {
175  auto row = m_tableWidget->selectedRow();
176  if (row < 0)
177  return;
178 
179  auto currentMax = m_firstDataRowSpinBox->maximum();
180  auto desiredVal = row + 1;
181  auto newMax = std::max(currentMax, desiredVal);
182  m_firstDataRowSpinBox->setMaximum(newMax);
183  m_firstDataRowSpinBox->setValue(desiredVal);
184  m_tableWidget->setFirstRow(size_t(row));
185 }
186 
188 {
189  auto row = m_tableWidget->selectedRow();
190  if (row < 0)
191  return;
192 
193  auto currentMin = m_firstDataRowSpinBox->minimum();
194  auto desiredVal = row + 1;
195  auto newMin = std::min(currentMin, desiredVal);
196  m_lastDataRowSpinBox->setMinimum(newMin);
197  m_lastDataRowSpinBox->setValue(desiredVal);
198  m_tableWidget->setLastRow(size_t(row));
199 }
200 
202 {
203  std::set<int> selection = m_tableWidget->selectedRows();
204  m_tableWidget->discardRows(selection);
205 }
206 
208 {
209  m_firstDataRowSpinBox->setValue(0);
210  m_lastDataRowSpinBox->setValue(int(maxLines()));
212 }
213 
215 {
216  return size_t(m_firstDataRowSpinBox->value());
217 }
218 
220 {
221  return size_t(m_lastDataRowSpinBox->value());
222 }
223 
225 {
226  return size_t(m_lastDataRowSpinBox->maximum());
227 }
228 
230 {
231  for (int i = 0; i < csv::UnitsLabels.size(); i++) {
232  const Axes::Units u = static_cast<Axes::Units>(i);
233  if (m_coordinateUnitsComboBox->currentText() == QString(axisUnitLabel.at(u)))
234  return u;
235  }
236  return Axes::Units::NBINS; // default
237 }
238 
240 {
241  char separator;
242  QString tmpstr = m_separatorField->text();
243  if (tmpstr.size() < 1) {
244  separator = '\0';
245  } else {
246  separator = tmpstr.at(0).toLatin1();
247  }
248  return separator;
249 }
250 
252 {
253  reject();
254 }
255 
257 {
258  // We shouldn't be here if the data is
259  // not previously sanitised.
260  accept();
261 }
262 
264 {
265  // table Widget
267  m_tableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
268  m_tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
269  connect(m_tableWidget, &QTableWidget::customContextMenuRequested, this,
272  [this]() { updateSelection(); });
273 
274  // Error label
275  m_errorLabel = new QLabel();
276  m_errorLabel->setStyleSheet("QLabel { color : red; }");
277  m_errorLabel->setText("");
278 
279  // Import button
280  m_importButton = new QPushButton("Import");
281  m_importButton->setDefault(false);
282  m_importButton->setAutoDefault(false);
283  connect(m_importButton, &QPushButton::clicked, this, &DataSelector::onImportButton);
284 
285  // Reject button
286  m_cancelButton = new QPushButton("Cancel");
287  m_cancelButton->setDefault(false);
288  m_cancelButton->setAutoDefault(false);
289  connect(m_cancelButton, &QPushButton::clicked, this, [this]() { reject(); });
290 
291  // Separator field -- This needs to communicate with importAssistant
292  m_separatorField = new QLineEdit("");
293  m_separatorField->setMaxLength(1);
294  m_separatorField->setMaximumWidth(70);
295  m_separatorField->setMinimumWidth(70);
296  connect(m_separatorField, &QLineEdit::editingFinished, this,
297  [this]() { emit separatorChanged(separator()); });
298 
299  // First Row SpinBox
300  m_firstDataRowSpinBox = new QSpinBox();
301  m_firstDataRowSpinBox->setMinimum(1);
302  m_firstDataRowSpinBox->setMaximum(1);
303  m_firstDataRowSpinBox->setValue(1);
304  m_firstDataRowSpinBox->setMaximumWidth(70);
305  m_firstDataRowSpinBox->setMinimumWidth(70);
306  connect(m_firstDataRowSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
307  this, [this]() {
308  m_lastDataRowSpinBox->setMinimum(m_firstDataRowSpinBox->value());
309  updateSelection();
310  });
311 
312  // Last Row SpinBox
313  m_lastDataRowSpinBox = new QSpinBox();
314  m_lastDataRowSpinBox->setMinimum(1);
315  m_lastDataRowSpinBox->setMaximum(1);
316  m_lastDataRowSpinBox->setValue(1);
317  m_lastDataRowSpinBox->setMaximumWidth(70);
318  m_lastDataRowSpinBox->setMinimumWidth(70);
319  connect(m_lastDataRowSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
320  this, [this]() {
321  m_firstDataRowSpinBox->setMaximum(m_lastDataRowSpinBox->value());
322  updateSelection();
323  });
324 
325  // Coordinate units selector:
326  m_coordinateUnitsComboBox = new QComboBox();
327  m_coordinateUnitsComboBox->setMaximumWidth(70);
328  m_coordinateUnitsComboBox->setMinimumWidth(70);
329  m_coordinateUnitsComboBox->addItem(axisUnitLabel.at(Axes::Units::NBINS));
330 
331  auto layout = new QVBoxLayout;
332  layout->setContentsMargins(0, 0, 0, 0);
333 
334  // Separator:
335  auto separatorFieldLayout = new QFormLayout;
336  separatorFieldLayout->addRow(tr("&Separator: "), m_separatorField);
337 
338  // Place table Widget:
339  auto tableLayout = new QVBoxLayout;
340  tableLayout->setMargin(10);
341  tableLayout->addWidget(new QLabel("Right click on the table to select what will be imported"));
342  tableLayout->addWidget(m_tableWidget);
343  tableLayout->addLayout(separatorFieldLayout);
344  tableLayout->addLayout(separatorFieldLayout);
345  tableLayout->addWidget(m_errorLabel);
346 
347  // First and last data rows:
348  auto rowControlsLayout = new QFormLayout;
349  auto* rowControlsGroupBox = new QGroupBox;
350  rowControlsLayout->addRow(tr("&From row: "), m_firstDataRowSpinBox);
351  rowControlsLayout->addRow(tr("&To row: "), m_lastDataRowSpinBox);
352  rowControlsLayout->setMargin(10);
353  rowControlsGroupBox->setTitle(tr("&Data rows:"));
354  rowControlsGroupBox->setLayout(rowControlsLayout);
355 
356  // Unit selector
357  auto unitSelectionLayout = new QFormLayout;
358  unitSelectionLayout->addRow(tr("&Coordinate units: "), m_coordinateUnitsComboBox);
359  unitSelectionLayout->setMargin(10);
360 
361  // buttons layout
362  auto buttonsLayout = new QHBoxLayout;
363  buttonsLayout->addWidget(m_importButton);
364  buttonsLayout->addWidget(m_cancelButton);
365 
366  // place controls and import/reject buttons
367  auto controlsAndButtonsGrid = new QGridLayout;
368  controlsAndButtonsGrid->setMargin(10);
369  controlsAndButtonsGrid->addItem(new QSpacerItem(10000, 1), 1, 1, 2, 1);
370  controlsAndButtonsGrid->addWidget(rowControlsGroupBox, 1, 2, 1, 1, Qt::AlignRight);
371  controlsAndButtonsGrid->addLayout(unitSelectionLayout, 2, 2, Qt::AlignRight);
372  controlsAndButtonsGrid->addLayout(buttonsLayout, 3, 2, 1, 1, Qt::AlignRight);
373 
374  // build all the layout
375  layout->addLayout(separatorFieldLayout);
376  layout->addLayout(tableLayout);
377  layout->addLayout(controlsAndButtonsGrid);
378 
379  return layout;
380 }
Defines class DataSelector.
const std::map< Axes::Units, const char * > axisUnitLabel
DefinesStyleUtils namespace.
Defines class TableContextMenu.
static void showErrorMessage(std::string message)
void dataSanityChanged()
int intensityColumn() const
int coordinateColumn() const
std::set< int > selectedRows() const
void setLastRow(size_t row)
bool dataLooksGood() const
void discardRows(std::set< int > rows)
int selectedRow() const
void setData(csv::DataArray data)
int selectedColumn() const
void setColumnAs(int col, csv::ColumnType type)
QList< QString > availableCoordinateUnits() const
void setFirstRow(size_t row)
size_t lastLine() const
char separator() const
bool isInsideTable(QPoint point)
void setColumnSlot(csv::ColumnType ct)
QLabel * m_errorLabel
Definition: DataSelector.h:88
void setFirstRow()
csv::DataArray m_data
Definition: DataSelector.h:80
Axes::Units units() const
QLineEdit * m_separatorField
Definition: DataSelector.h:82
void populateUnitsComboBox()
void onCancelButton()
void setColumnAs(csv::ColumnType coordOrInt)
QPushButton * m_importButton
Definition: DataSelector.h:86
size_t firstLine() const
QComboBox * m_coordinateUnitsComboBox
Definition: DataSelector.h:85
QSpinBox * m_firstDataRowSpinBox
Definition: DataSelector.h:83
void separatorChanged(char newSeparator)
DataSelector(csv::DataArray csvArray, QWidget *parent=nullptr)
QSpinBox * m_lastDataRowSpinBox
Definition: DataSelector.h:84
void updateSelection()
bool updateData()
void onColumnRightClick(const QPoint &position)
void onImportButton()
QBoxLayout * createLayout()
size_t maxLines() const
QPushButton * m_cancelButton
Definition: DataSelector.h:87
CsvImportTable * m_tableWidget
Definition: DataSelector.h:81
void resetSelection()
void setColumnAs(csv::ColumnType)
Defines namespace Constants.
void setResizable(QDialog *dialog)
Make modal dialog resizable.
Definition: StyleUtils.cpp:87
const QStringList UnitsLabels
Definition: CsvNamespace.h:25
ColumnType
Definition: CsvNamespace.h:23
std::vector< std::vector< std::string > > DataArray
Definition: CsvNamespace.h:26