BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
parserpropertywidget.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file gui2/dataloader/parserpropertywidget.cpp
6 //! @brief Implements class CLASS?
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2020
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
19 #include <QButtonGroup>
20 #include <QComboBox>
21 #include <QDebug>
22 #include <QGridLayout>
23 #include <QGroupBox>
24 #include <QLabel>
25 #include <QLineEdit>
26 #include <QRadioButton>
27 #include <QRegExpValidator>
28 #include <QSettings>
29 #include <QVBoxLayout>
30 
31 namespace {
32 
33 //! Creates widget with label and little space above. Intended for grid layouts.
34 QWidget* createSectionWidget(const QString& text)
35 {
36  auto widget = new QWidget;
37  auto layout = new QVBoxLayout(widget);
38  layout->addSpacing(10);
39  auto label = new QLabel(text);
40  QFont font = label->font();
41  font.setPointSize(ModelView::Utils::SystemPointSize() * 1.05);
42  label->setFont(font);
43  layout->addWidget(label);
44  return widget;
45 }
46 
47 const QString separatorgroupid_setting_name()
48 {
49  const QString key = "separatorgroup_id";
50  return gui2::Constants::ParserPropertyGroupKey + "/" + key;
51 }
52 
53 const QString customseparatortext_setting_name()
54 {
55  const QString key = "customseparator_text";
56  return gui2::Constants::ParserPropertyGroupKey + "/" + key;
57 }
58 
59 } // namespace
60 
61 namespace gui2 {
62 
63 ParserPropertyWidget::ParserPropertyWidget(QWidget* parent) : QWidget(parent)
64 {
65  auto layout = new QVBoxLayout(this);
66  layout->addLayout(createGridLayout());
67  layout->addStretch(1);
68 
69  readSettings();
70 }
71 
73 {
74  writeSettings();
75 }
76 
77 //! Creates parser from parser properties.
78 
79 std::unique_ptr<ParserInterface> ParserPropertyWidget::createParser() const
80 {
81  ParserOptions options = m_options;
82 
83  // sanity check for empty separator
84  options.m_separator = options.m_separator.empty() ? std::string(" ") : options.m_separator;
85  return std::make_unique<DefaultParser>(options);
86 }
87 
88 //! Sets list of canvas names as possible import targets.
89 
90 void ParserPropertyWidget::setTargetCanvas(const QStringList& canvas_names, int current_index)
91 {
92  m_targetCanvasCombo->insertItems(0, canvas_names);
93  m_targetCanvasCombo->setCurrentIndex(current_index);
94 }
95 
97 {
98  qDebug() << "option"
99  << "header:" << QString::fromStdString(m_options.m_header_prefix)
100  << "separator:" << QString::fromStdString(m_options.m_separator) << "pattern"
101  << QString::fromStdString(m_options.m_skip_index_pattern);
102  emit parserPropertyChanged();
103 }
104 
105 //! Reads widget settings.
106 
108 {
109  QSettings settings;
110 
111  if (settings.contains(separatorgroupid_setting_name())) {
112  int button_id = settings.value(separatorgroupid_setting_name()).toInt();
113  if (auto button = m_separatorButtonGroup->button(button_id); button) {
114  button->click();
115  }
116  }
117 
118  if (settings.contains(customseparatortext_setting_name()))
119  m_customSeparatorLineEdit->setText(
120  settings.value(customseparatortext_setting_name()).toString());
121 }
122 
123 //! Writes widget settings.
124 
126 {
127  QSettings settings;
128 
129  settings.setValue(separatorgroupid_setting_name(), m_separatorButtonGroup->checkedId());
130  settings.setValue(customseparatortext_setting_name(), m_customSeparatorLineEdit->text());
131 }
132 
134 {
135  auto grid_layout = new QGridLayout;
136 
137  addSectionLabel("Separator", grid_layout);
138  m_separatorButtonGroup = new QButtonGroup(this);
141 
142  addSectionLabel("Ignore lines", grid_layout);
143  addIgnoreStringPatternRow(grid_layout);
144  addIgnoreNumbersPatternRow(grid_layout);
145 
146  addSectionLabel("Import target", grid_layout);
147  addImportToBlock(grid_layout);
148 
149  // make first colum with invisible label fixed
150  for (int col = 0; col < grid_layout->columnCount(); ++col)
151  grid_layout->setColumnStretch(col, 10);
152  grid_layout->setColumnStretch(0, 0);
153 
154  return grid_layout;
155 }
156 
157 void ParserPropertyWidget::addSectionLabel(const QString& text, QGridLayout* layout)
158 {
159  int row = layout->rowCount();
160  layout->addWidget(createSectionWidget(text), row, 0, 1, 3, Qt::AlignLeft);
161 }
162 
163 //! Adds row to the grid: elements with standard separator settings.
164 
165 void ParserPropertyWidget::addStandardSeparatorRow(QGridLayout* layout, QButtonGroup* group)
166 {
167  // automatic separator
168  int row = layout->rowCount();
169  auto automaticRadio = new QRadioButton;
170  automaticRadio->setChecked(true);
171  automaticRadio->setText("Automatic");
172  automaticRadio->setToolTip("Try to guess column separator");
173  connect(automaticRadio, &QRadioButton::clicked, [this](auto) {
174  m_options.m_separator.clear();
176  });
177 
178  // space separator
179  auto spaceRadio = new QRadioButton;
180  spaceRadio->setText("Space");
181  spaceRadio->setToolTip("Use empty space as column separator");
182  connect(spaceRadio, &QRadioButton::clicked, [this](auto) {
183  m_options.m_separator = " ";
185  });
186 
187  // comma separator
188  auto commaRadio = new QRadioButton;
189  commaRadio->setText("Comma");
190  commaRadio->setToolTip("Use comma as column separator");
191  connect(commaRadio, &QRadioButton::clicked, [this](auto) {
192  m_options.m_separator = ",";
194  });
195 
196  // adding all to layout
197  layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft);
198  layout->addWidget(automaticRadio, row, 1, Qt::AlignLeft);
199  layout->addWidget(spaceRadio, row, 2, Qt::AlignLeft);
200  layout->addWidget(commaRadio, row, 3, Qt::AlignLeft);
201  group->addButton(automaticRadio, AUTOMATIC);
202  group->addButton(spaceRadio, SPACE);
203  group->addButton(commaRadio, COMMA);
204 }
205 
206 //! Adds row to the grid: elements with custom separator settings.
207 
208 void ParserPropertyWidget::addCustomSeparatorRow(QGridLayout* layout, QButtonGroup* group)
209 {
210  int row = layout->rowCount();
211  m_customSeparatorLineEdit = new QLineEdit;
212  auto customRadio = new QRadioButton;
213 
214  // custom separator radio
215  customRadio->setText("Custom");
216  customRadio->setToolTip("Use given symbols as column separator");
217  auto on_custom_separator = [this](auto) {
218  m_options.m_separator = m_customSeparatorLineEdit->text().toStdString();
220  };
221  connect(customRadio, &QRadioButton::clicked, on_custom_separator);
222 
223  // custom separator text
225  m_customSeparatorLineEdit->setToolTip("Use given symbols as column separator");
226  auto on_custom_lineedit = [this, customRadio]() {
227  if (customRadio->isChecked())
228  m_options.m_separator = m_customSeparatorLineEdit->text().toStdString();
230  };
231  connect(m_customSeparatorLineEdit, &QLineEdit::editingFinished, on_custom_lineedit);
232 
233  // adding to the layout
234  layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft);
235  layout->addWidget(customRadio, row, 1, Qt::AlignLeft);
236  layout->addWidget(m_customSeparatorLineEdit, row, 2, Qt::AlignLeft);
237  group->addButton(customRadio, CUSTOM);
238 }
239 
240 //! Adds row to the grid: elements with pattern to ignore lines.
241 
243 {
244  auto startingWithRadio = new QRadioButton;
245  auto startingFromLineEdit = new QLineEdit;
246 
247  // radio settings
248  int row = layout->rowCount();
249  startingWithRadio->setText("Starting with");
250  startingWithRadio->setAutoExclusive(false);
251  startingWithRadio->setChecked(true);
252  startingWithRadio->setToolTip("Ignore lines starting with a given character(s)");
253  auto on_startingfrom_radio = [this, startingFromLineEdit](auto checked) {
255  checked ? startingFromLineEdit->text().toStdString() : std::string();
257  };
258  connect(startingWithRadio, &QRadioButton::clicked, on_startingfrom_radio);
259 
260  // line edit settings
261  startingFromLineEdit->setText("#");
262  startingFromLineEdit->setToolTip("Ignore lines starting with a given character(s)");
263  auto on_startingfrom_lineedit = [this, startingWithRadio, startingFromLineEdit]() {
264  if (startingWithRadio->isChecked())
265  m_options.m_header_prefix = startingFromLineEdit->text().toStdString();
267  };
268  connect(startingFromLineEdit, &QLineEdit::editingFinished, on_startingfrom_lineedit);
269 
270  // adding to layout
271  layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft);
272  layout->addWidget(startingWithRadio, row, 1, Qt::AlignLeft);
273  layout->addWidget(startingFromLineEdit, row, 2, Qt::AlignLeft);
274 }
275 
276 //! Adds row to the grid: elements with pattern to ignore line numbers.
277 
279 {
280  auto lineNumbersRadio = new QRadioButton;
281  auto lineNumbersLineEdit = new QLineEdit;
282 
283  // radio settings
284  int row = layout->rowCount();
285  lineNumbersRadio->setAutoExclusive(false);
286  lineNumbersRadio->setText("Line numbers");
287  lineNumbersRadio->setToolTip("Ignore lines with line numbers matching the pattern");
288  auto on_linenumbers_radio = [this, lineNumbersLineEdit](auto checked) {
290  checked ? lineNumbersLineEdit->text().toStdString() : std::string();
292  };
293  connect(lineNumbersRadio, &QRadioButton::clicked, on_linenumbers_radio);
294 
295  // line edit settings
296  lineNumbersLineEdit->setPlaceholderText("Example: 1-5,42");
297  lineNumbersLineEdit->setToolTip("Ignore lines with line numbers matching the pattern");
298  auto on_linenumbers_lineedit = [this, lineNumbersRadio, lineNumbersLineEdit]() {
299  if (lineNumbersRadio->isChecked())
300  m_options.m_skip_index_pattern = lineNumbersLineEdit->text().toStdString();
302  };
303  connect(lineNumbersLineEdit, &QLineEdit::editingFinished, on_linenumbers_lineedit);
304 
305  // adding to the layout
306  layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft);
307  layout->addWidget(lineNumbersRadio, row, 1, Qt::AlignLeft);
308  layout->addWidget(lineNumbersLineEdit, row, 2, Qt::AlignLeft);
309 
310  // validator
311  auto validator = new QRegExpValidator(QRegExp("^[0-9,-]*$"), this);
312  lineNumbersLineEdit->setValidator(validator);
313 }
314 
315 //! Adds row to the grid: elements related to the import target.
316 
317 void ParserPropertyWidget::addImportToBlock(QGridLayout* layout)
318 {
319  auto newCanvasRadio = new QRadioButton;
320  auto existingCanvasRadio = new QRadioButton;
321  m_targetCanvasCombo = new QComboBox;
322 
323  // radio settings
324  newCanvasRadio->setText("New canvas");
325  newCanvasRadio->setChecked(true);
326  newCanvasRadio->setToolTip("Data will be imported into the new canvas");
327  auto on_newcanvas_radio = [this](auto checked) {
328  if (checked)
330  };
331  connect(newCanvasRadio, &QRadioButton::clicked, on_newcanvas_radio);
332 
333  existingCanvasRadio->setText("Existing canvas");
334  existingCanvasRadio->setToolTip("Data will be imported into existing canvas");
335  auto on_existingcanvas_radio = [this](auto checked) {
336  if (checked)
337  targetCanvasChanged(m_targetCanvasCombo->currentIndex());
338  };
339  connect(existingCanvasRadio, &QRadioButton::clicked, on_existingcanvas_radio);
340 
341  // combo settings
342  m_targetCanvasCombo->setToolTip("Data will be imported into existing canvas");
343  auto on_canvas_combo = [this, existingCanvasRadio](int index) {
344  if (existingCanvasRadio->isChecked())
345  targetCanvasChanged(index);
346  };
347  connect(m_targetCanvasCombo,
348  static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
349  on_canvas_combo);
350 
351  // adding to the layout
352  int row = layout->rowCount();
353  layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft);
354  layout->addWidget(newCanvasRadio, row, 1, Qt::AlignLeft);
355  row = layout->rowCount();
356  layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft);
357  layout->addWidget(existingCanvasRadio, row, 1, Qt::AlignLeft);
358  layout->addWidget(m_targetCanvasCombo, row, 2, Qt::AlignLeft);
359  auto buttonGroup = new QButtonGroup(this);
360  buttonGroup->addButton(newCanvasRadio);
361  buttonGroup->addButton(existingCanvasRadio);
362 }
363 
364 } // namespace gui2
Defines class CLASS?
void addSectionLabel(const QString &text, QGridLayout *layout)
void addIgnoreNumbersPatternRow(QGridLayout *layout)
Adds row to the grid: elements with pattern to ignore line numbers.
int targetCanvasChanged(int canvas_index)
void addCustomSeparatorRow(QGridLayout *layout, QButtonGroup *group)
Adds row to the grid: elements with custom separator settings.
ParserPropertyWidget(QWidget *parent=nullptr)
void writeSettings()
Writes widget settings.
std::unique_ptr< ParserInterface > createParser() const
Creates parser from parser properties.
void addImportToBlock(QGridLayout *layout)
Adds row to the grid: elements related to the import target.
void addIgnoreStringPatternRow(QGridLayout *layout)
Adds row to the grid: elements with pattern to ignore lines.
void addStandardSeparatorRow(QGridLayout *layout, QButtonGroup *group)
Adds row to the grid: elements with standard separator settings.
void setTargetCanvas(const QStringList &canvas_names, int current_index)
Sets list of canvas names as possible import targets.
void readSettings()
Reads widget settings.
Defines class CLASS?
MVVM_VIEW_EXPORT int WidthOfLetterM()
Returns width of the letter 'M' deduced from current font metrics.
MVVM_VIEW_EXPORT int SystemPointSize()
Returns size in points of default system font.
std::string toString(PyObject *obj)
Converts PyObject into string, if possible, or throws exception.
Definition: PyUtils.cpp:24
const QString ParserPropertyGroupKey
Definition: app_constants.h:25
Based on Qt example "codeeditor" Copyright (C) 2016 The Qt Company Ltd.
Definition: app_constants.h:20
Defines class CLASS?
Parser options to process multi column ASCII files.
std::string m_skip_index_pattern
pattern denoting line to skip (i.e. '1,10-12,42')
std::string m_header_prefix
prefix denoting header line
std::string m_separator
column separator
Defines class CLASS?