BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
CsvImportTable.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/CsvImportTable.cpp
6 //! @brief Implements class CsvImportTable
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 
17 
18 namespace {
19 
20 ScientificSpinBox* createMultiplierBox(double value = 1.0, bool enabled = false,
21  QWidget* parent = nullptr)
22 {
23  auto result = new ScientificSpinBox(parent);
24  result->setValue(value);
25  result->setEnabled(enabled);
26  return result;
27 }
28 } // namespace
29 
31  : QObject(parent), m_data(new csv::DataArray), m_n_header(0), m_n_footer(0)
32 {
33 }
34 
36 {
37  m_data.reset(new csv::DataArray(std::move(data)));
38  m_selected_cols.clear();
39  m_n_header = 0;
40  m_n_footer = 0;
41  m_discarded_rows.clear();
42 }
43 
45 {
47 
49  const int prev_assigned = column.columnNumber();
50  if (prev_assigned == col && type == column.name())
51  return prev_assigned;
52 
53  for (auto iter = m_selected_cols.begin(); iter != m_selected_cols.end();)
54  if (iter->second.columnNumber() == col && iter->first != role)
55  iter = m_selected_cols.erase(iter);
56  else
57  ++iter;
58 
59  column.setColNum(col);
60  column.setMultiplier(1.0); // resetting multiplier value
61  column.setValues(values(col));
62  column.setName(type);
63  return prev_assigned;
64 }
65 
67 {
68  if (m_selected_cols.find(type) == m_selected_cols.end())
69  return;
70 
71  m_selected_cols[type].setMultiplier(value);
72 }
73 
75 {
76  if (row >= nRows())
77  return;
78  m_n_header = row;
79 }
80 
81 void CsvImportData::setLastRow(size_t row)
82 {
83  if (row + 1 >= nRows())
84  return;
85  m_n_footer = nRows() - row - 1;
86 }
87 
88 void CsvImportData::toggleDiscardRows(std::set<int> rows)
89 {
90  if (rows.empty()) {
91  m_discarded_rows.clear();
92  return;
93  }
94  for (auto row : rows) {
95  if (m_discarded_rows.find(row) != m_discarded_rows.end()) {
96  m_discarded_rows.erase(row);
97  } else {
98  m_discarded_rows.insert(row);
99  }
100  }
101 }
102 
103 std::vector<CsvImportData::DATA_TYPE> CsvImportData::availableTypes()
104 {
105  return {Intensity, Coordinate};
106 }
107 
109 {
110  return *m_data.get();
111 }
112 
114 {
115  auto iter = m_selected_cols.find(type);
116  return iter == m_selected_cols.end() ? -1 : iter->second.columnNumber();
117 }
118 
120 {
121  if (col < 0 || col >= static_cast<int>(nCols()))
122  return {};
123 
124  const size_t size = m_data->size();
125  csv::DataColumn result(size);
126  for (size_t i = 0; i < size; ++i)
127  result[i] = (*m_data)[i][static_cast<size_t>(col)];
128  return result;
129 }
130 
132 {
133  csv::DataColumn result;
134  const int col = column(type);
135  if (col < 0 || col >= static_cast<int>(nCols()))
136  return result;
137 
138  double mult_value = multiplier(type);
139  csv::DataColumn col_values = values(col);
140  result.resize(col_values.size());
141  // FIXME: seems that csv::DataColumn and related
142  // classes can be based on QString
143  for (size_t i = 0; i < col_values.size(); i++) {
144  auto currentText = QString::fromStdString(col_values[i]);
145  double number = mult_value * currentText.toDouble();
146  // FIXME: find more elegant way to distinguish non-numerics
147  QString textToWrite = 0.0 == number ? currentText : QString::number(number);
148  result[i] = textToWrite.toStdString();
149  }
150  return result;
151 }
152 
154 {
155  if (m_selected_cols.find(type) == m_selected_cols.end())
156  return 1.0;
157  return m_selected_cols.at(type).multiplier();
158 }
159 
161 {
162  if (m_selected_cols.find(type) == m_selected_cols.end())
163  return "";
164  return csv::HeaderLabels[m_selected_cols.at(type).name()];
165 }
166 
168 {
169  if (column(Coordinate) < 0)
170  return {axisUnitLabel.at(Axes::Units::NBINS)};
171 
172  auto coordinate_type = m_selected_cols.at(Coordinate).name();
173  if (coordinate_type == csv::_q_)
174  return {axisUnitLabel.at(Axes::Units::QSPACE)};
175  else if (coordinate_type == csv::_theta_)
176  return {{axisUnitLabel.at(Axes::Units::DEGREES)}, {axisUnitLabel.at(Axes::Units::RADIANS)}};
177  return {axisUnitLabel.at(Axes::Units::NBINS)};
178 }
179 
180 size_t CsvImportData::nCols() const
181 {
182  if (nRows() == 0)
183  return 0;
184  return (*m_data)[0].size();
185 }
186 
187 size_t CsvImportData::nRows() const
188 {
189  return (*m_data).size();
190 }
191 
193 {
194  if (static_cast<size_t>(row) < m_n_header || static_cast<size_t>(row) + m_n_footer >= nRows())
195  return true;
196  if (m_discarded_rows.find(row) != m_discarded_rows.end())
197  return true;
198  return false;
199 }
200 
201 std::set<std::pair<int, int>> CsvImportData::checkData()
202 {
203  std::set<std::pair<int, int>> result;
204  for (auto type : availableTypes()) {
205  auto col_result = checkFormat(multipliedValues(type), type == Coordinate);
206  std::for_each(col_result.begin(), col_result.end(), [col = column(type), &result](int row) {
207  result.insert({row, col});
208  });
209  }
210  return result;
211 }
212 
214 {
215  m_n_header = 0;
216  m_n_footer = 0;
217  m_selected_cols.clear();
218  m_discarded_rows.clear();
219 }
220 
221 std::set<int> CsvImportData::checkFormat(const csv::DataColumn& values, bool check_ordering)
222 {
223  std::set<int> result;
224  if (values.empty())
225  return result;
226 
227  bool has_prev_value = false;
228  double prev_value = 0.0;
229  for (size_t i = m_n_header; i + m_n_footer < nRows(); ++i) {
230  if (m_discarded_rows.find(static_cast<int>(i)) != m_discarded_rows.end())
231  continue;
232 
233  auto cellText = QString::fromStdString(values[i]);
234  bool is_double;
235  double number = cellText.toDouble(&is_double);
236  if (!is_double || number <= 0.0) {
237  result.insert(static_cast<int>(i));
238  continue;
239  }
240 
241  if (!check_ordering)
242  continue;
243 
244  if (has_prev_value && prev_value >= number) {
245  result.insert(static_cast<int>(i));
246  continue;
247  }
248  prev_value = number;
249  has_prev_value = true;
250  }
251  return result;
252 }
253 
255  : QTableWidget(parent), m_import_data(new CsvImportData(this)), m_data_is_suitable(true)
256 {
257 }
258 
260 {
261  auto selectedRanges = this->selectedRanges();
262  if (selectedRanges.empty())
263  return -1;
264  auto front = selectedRanges.front();
265  auto row = front.topRow();
266  return row - rowOffset();
267 }
268 
269 std::set<int> CsvImportTable::selectedRows() const
270 {
271  std::set<int> accumulator;
272 
273  auto selection = selectedRanges();
274  if (selection.empty())
275  return {};
276 
277  int size = selection.size();
278  for (int rangenumber = 0; rangenumber < size; ++rangenumber) {
279  int row0 = selectedRanges()[rangenumber].topRow() - rowOffset();
280  int rowN = selectedRanges()[rangenumber].bottomRow() - rowOffset();
281  for (int r = row0; r <= rowN; ++r) {
282  accumulator.insert(r);
283  }
284  }
285  return accumulator;
286 }
287 
289 {
290  auto selectedRanges = this->selectedRanges();
291  if (selectedRanges.empty())
292  return -1;
293  auto front = selectedRanges.front();
294  auto col = front.leftColumn();
295  return col;
296 }
297 
299 {
300  if (data.empty()) {
301  clearContents();
302  setRowCount(0);
303  m_import_data->setData(std::move(data));
304  return;
305  }
306 
307  size_t nRows = data.size();
308  size_t nCols = data[0].size();
309  clearContents();
310  setColumnCount(int(nCols));
311  setRowCount(0);
312 
313  insertRow(rowCount());
314 
315  for (size_t i = 0; i < nRows; i++) {
316  insertRow(rowCount());
317  size_t I = size_t(rowCount()) - 1;
318  for (size_t j = 0; j < data[i].size(); j++) {
319  setItem(int(I), int(j), new QTableWidgetItem(QString::fromStdString(data[i][j])));
320  }
321  }
322 
323  m_import_data->setData(std::move(data));
325 }
326 
328 {
329  int prev_col = m_import_data->setColumnAs(col, type);
330  resetColumn(prev_col);
331  updateSelection();
332 }
333 
335 {
336  if (row == m_import_data->firstRow())
337  return;
339  updateSelection();
340 }
341 
343 {
344  if (row == m_import_data->lastRow())
345  return;
347  updateSelection();
348 }
349 
350 void CsvImportTable::discardRows(std::set<int> rows)
351 {
352  m_import_data->toggleDiscardRows(std::move(rows));
353  updateSelection();
354 }
355 
357 {
359  updateSelection();
360 }
361 
363 {
365 }
366 
368 {
370 }
371 
373 {
375 }
376 
378 {
379  setHeaders();
380  // FIXME: replace re-creation of all spin boxes with blocking/unlocking
384  if (checkData() != m_data_is_suitable) {
386  emit dataSanityChanged();
387  }
388 }
389 
390 // FIXME: put filling vertical headers here
392 {
393  // Reset header labels
394  QStringList headers;
395 
396  for (int j = 0; j < this->columnCount(); j++)
397  headers.append(QString::number(j + 1));
398  setHorizontalHeaderLabels(headers);
399 
400  for (auto type : CsvImportData::availableTypes()) {
401  int col = m_import_data->column(type);
402  if (col < 0)
403  continue;
404  setHorizontalHeaderItem(col, new QTableWidgetItem(m_import_data->columnLabel(type)));
405  }
406 }
407 
409 {
410  // FIXME: replace recreation of sell items with value assignment
411  for (auto type : CsvImportData::availableTypes()) {
413  if (values.empty())
414  continue;
415  int col = m_import_data->column(type);
416  for (size_t i = 0; i < values.size(); ++i)
417  setItem(static_cast<int>(i) + rowOffset(), col,
418  new QTableWidgetItem(QString::fromStdString(values[i])));
419  }
420 }
421 
423 {
424  const int n_cols = static_cast<int>(m_import_data->nCols());
425 
426  for (int n = 0; n < n_cols; ++n)
427  setCellWidget(0, n, createMultiplierBox());
428 
429  auto types = CsvImportData::availableTypes();
430  for (auto type : types)
431  if (m_import_data->column(type) >= 0) {
432  auto spin_box =
433  static_cast<ScientificSpinBox*>(cellWidget(0, m_import_data->column(type)));
434  spin_box->setEnabled(true);
435  spin_box->setValue(m_import_data->multiplier(type));
436  connect(spin_box, &ScientificSpinBox::editingFinished, this, [this, spin_box, type]() {
437  m_import_data->setMultiplier(type, spin_box->value());
438  updateSelection();
439  });
440  }
441 
442  // FIXME: move row headers initialization elsewhere
443  int nRows = this->rowCount();
444 
445  QStringList vhlabels;
446  vhlabels << "Multiplier: ";
447  for (int i = rowOffset(); i < nRows; i++)
448  vhlabels << QString::number(i);
449 
450  this->setVerticalHeaderLabels(vhlabels);
451 }
452 
454 {
455  int nRows = this->rowCount();
456  int nCols = this->columnCount();
457 
458  for (int i = rowOffset(); i < nRows; i++) {
459  Qt::GlobalColor color = m_import_data->rowExcluded(i - rowOffset()) ? Qt::gray : Qt::white;
460  for (int j = 0; j < nCols; j++)
461  markCell(i, j, color);
462  }
463 }
464 
466 {
467  auto to_highlight = m_import_data->checkData();
468  for (auto index : to_highlight)
469  markCell(index.first + rowOffset(), index.second, Qt::red);
470  return to_highlight.empty();
471 }
472 
474 {
475  if (columnCount() >= col || col < 0)
476  return;
477 
478  const csv::DataColumn data = m_import_data->values(col);
479  for (size_t i = 0; i < data.size(); i++) {
480  QString originalText = QString::fromStdString(data[i]);
481  setItem(static_cast<int>(i) + rowOffset(), int(col), new QTableWidgetItem(originalText));
482  }
483 }
484 
485 void CsvImportTable::markCell(int i, int j, Qt::GlobalColor color)
486 {
487  item(i, j)->setBackground(color);
488 }
constexpr complex_t I
Definition: Complex.h:21
Defines class CsvImportTable.
const std::map< Axes::Units, const char * > axisUnitLabel
Defines class ScientificSpinBox.
size_t lastRow()
size_t firstRow()
void setLastRow(size_t row)
CsvImportData(QObject *parent=nullptr)
size_t nRows() const
bool rowExcluded(int row)
std::set< int > m_discarded_rows
const csv::DataArray & data() const
std::set< std::pair< int, int > > checkData()
size_t m_n_footer
number of footer rows
void toggleDiscardRows(std::set< int > rows)
double multiplier(DATA_TYPE type) const
size_t nCols() const
int column(DATA_TYPE type) const
int setColumnAs(int col, csv::ColumnType type)
sets type to a column col.
size_t m_n_header
number of header rows
std::set< int > checkFormat(const csv::DataColumn &values, bool check_ordering)
Checks if selected data is suitable for import.
void setMultiplier(DATA_TYPE type, double value)
void setData(csv::DataArray data)
std::map< DATA_TYPE, CsvCoordinateColumn > m_selected_cols
QString columnLabel(DATA_TYPE type) const
csv::DataColumn multipliedValues(DATA_TYPE type) const
std::unique_ptr< const csv::DataArray > m_data
QList< QString > availableCoordinateUnits() const
csv::DataColumn values(int col) const
void setFirstRow(size_t row)
static std::vector< DATA_TYPE > availableTypes()
CsvImportTable(QWidget *parent=nullptr)
void setMultiplierFields()
void markCell(int i, int j, Qt::GlobalColor color)
void resetColumn(int col)
CsvImportData * m_import_data
void dataSanityChanged()
int rowOffset() const
std::set< int > selectedRows() const
double coordinateMultiplier() const
void setLastRow(size_t row)
void greyoutDiscardedRows()
void discardRows(std::set< int > rows)
int selectedRow() const
void setData(csv::DataArray data)
int selectedColumn() const
double intensityMultiplier() const
void setColumnAs(int col, csv::ColumnType type)
QList< QString > availableCoordinateUnits() const
void setFirstRow(size_t row)
void setValue(double val)
ColumnType
Definition: CsvNamespace.h:23
@ _q_
Definition: CsvNamespace.h:23
@ _theta_
Definition: CsvNamespace.h:23
@ _intensity_
Definition: CsvNamespace.h:23
std::vector< std::string > DataColumn
Definition: CsvNamespace.h:28
std::vector< std::vector< std::string > > DataArray
Definition: CsvNamespace.h:26
const QStringList HeaderLabels
Definition: CsvNamespace.h:24