BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
CustomEditors.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/Views/PropertyEditor/CustomEditors.cpp
6 //! @brief Implements CustomEditors classes
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 
22 #include <QApplication>
23 #include <QBoxLayout>
24 #include <QCheckBox>
25 #include <QColorDialog>
26 #include <QComboBox>
27 #include <QDoubleSpinBox>
28 #include <QEvent>
29 #include <QKeyEvent>
30 #include <QLabel>
31 #include <QLineEdit>
32 #include <QToolButton>
33 #include <cmath>
34 
35 namespace {
36 //! Single step for QDoubleSpinBox.
37 
38 double singleStep(int decimals)
39 {
40  // For item with decimals=3 (i.e. 0.001) single step will be 0.1
41  return 1. / std::pow(10., decimals - 1);
42 }
43 
44 } // namespace
45 
46 //! Sets the data from the model to editor.
47 
48 void CustomEditor::setData(const QVariant& data)
49 {
50  m_data = data;
51  initEditor();
52 }
53 
54 //! Inits editor widgets from m_data.
55 
57 
58 //! Saves the data from the editor and informs external delegates.
59 
60 void CustomEditor::setDataIntern(const QVariant& data)
61 {
62  m_data = data;
64 }
65 
66 // --- MaterialPropertyEditor ---
67 
69  : CustomEditor(parent)
70  , m_textLabel(new QLabel)
71  , m_pixmapLabel(new QLabel)
72  , m_focusFilter(new LostFocusFilter(this))
73  , m_extDialogType("ExtMaterialEditor")
74 {
75  setMouseTracking(true);
76  setAutoFillBackground(true);
77 
78  auto layout = new QHBoxLayout;
79  layout->setContentsMargins(4, 0, 0, 0);
80 
81  ExternalProperty defProperty; // to get label and pixmap of undefined material
82  m_textLabel->setText(defProperty.text());
83  m_pixmapLabel->setPixmap(defProperty.pixmap());
84 
85  auto button = new QToolButton;
86  button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
87  button->setText(QLatin1String(" . . . "));
88  button->setToolTip("Material selector");
89  layout->addWidget(m_pixmapLabel);
90  layout->addWidget(m_textLabel);
91  layout->addStretch(1);
92  layout->addWidget(button);
93  setFocusPolicy(Qt::StrongFocus);
94  setAttribute(Qt::WA_InputMethodEnabled);
95  connect(button, &QToolButton::clicked, this, &ExternalPropertyEditor::buttonClicked);
96 
97  setLayout(layout);
98 }
99 
100 void ExternalPropertyEditor::setExternalDialogType(const QString& editorType)
101 {
102  m_extDialogType = editorType;
103 }
104 
106 {
107  // temporarily installing filter to prevent loss of focus caused by too insistent dialog
108  installEventFilter(m_focusFilter);
109  ExternalProperty property = m_data.value<ExternalProperty>();
110 
111  ExternalProperty newProperty;
112  if (m_extDialogType == "ExtMaterialEditor") {
113  newProperty = MaterialItemUtils::selectMaterialProperty(property);
114  } else if (m_extDialogType == "ExtColorEditor") {
115  newProperty = MaterialItemUtils::selectColorProperty(property);
116  } else {
117  throw GUIHelpers::Error("ExternalPropertyEditor::buttonClicked() -> Unexpected dialog");
118  }
119 
120  removeEventFilter(m_focusFilter);
121 
122  if (newProperty.isValid() && newProperty != property)
123  setDataIntern(newProperty.variant());
124 }
125 
127 {
128  ASSERT(m_data.canConvert<ExternalProperty>());
130  m_textLabel->setText(materialProperty.text());
131  m_pixmapLabel->setPixmap(materialProperty.pixmap());
132 }
133 
134 // --- CustomComboEditor ---
135 
137  : CustomEditor(parent), m_box(new QComboBox), m_wheel_event_filter(new WheelEventEater(this))
138 {
139  setAutoFillBackground(true);
140  setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
141 
142  auto layout = new QVBoxLayout;
143  layout->setMargin(0);
144  layout->setSpacing(0);
145  layout->addWidget(m_box);
146 
147  m_box->installEventFilter(m_wheel_event_filter);
148 
149  setLayout(layout);
150  setConnected(true);
151 }
152 
154 {
155  return m_box->sizeHint();
156 }
157 
159 {
160  return m_box->minimumSizeHint();
161 }
162 
164 {
165  ComboProperty comboProperty = m_data.value<ComboProperty>();
166 
167  if (comboProperty.currentIndex() != index) {
168  comboProperty.setCurrentIndex(index);
169  setDataIntern(QVariant::fromValue<ComboProperty>(comboProperty));
170  }
171 }
172 
174 {
175  setConnected(false);
176 
177  m_box->clear();
178  m_box->insertItems(0, internLabels());
179  m_box->setCurrentIndex(internIndex());
180 
181  setConnected(true);
182 }
183 
184 //! Returns list of labels for QComboBox
185 
187 {
188  if (!m_data.canConvert<ComboProperty>())
189  return {};
190  ComboProperty comboProperty = m_data.value<ComboProperty>();
191  return comboProperty.getValues();
192 }
193 
194 //! Returns index for QComboBox.
195 
197 {
198  if (!m_data.canConvert<ComboProperty>())
199  return 0;
200  ComboProperty comboProperty = m_data.value<ComboProperty>();
201  return comboProperty.currentIndex();
202 }
203 
204 void ComboPropertyEditor::setConnected(bool isConnected)
205 {
206  if (isConnected)
207  connect(m_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
208  &ComboPropertyEditor::onIndexChanged, Qt::UniqueConnection);
209  else
210  disconnect(m_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
212 }
213 
214 // --- ScientificDoublePropertyEditor ---
215 
217  : CustomEditor(parent), m_lineEdit(new QLineEdit), m_validator(nullptr)
218 {
219  setAutoFillBackground(true);
220 
221  auto layout = new QVBoxLayout;
222  layout->setMargin(0);
223  layout->setSpacing(0);
224 
225  layout->addWidget(m_lineEdit);
226 
227  m_validator = new QDoubleValidator(0.0, 1e+200, 1000, this);
228  m_validator->setNotation(QDoubleValidator::ScientificNotation);
229  m_lineEdit->setValidator(m_validator);
230 
231  connect(m_lineEdit, &QLineEdit::editingFinished, this,
233 
234  setLayout(layout);
235 }
236 
238 {
239  double minimum = limits.hasLowerLimit() ? std::max(limits.lowerLimit(), -1e+200) : -1e+200;
240  double maximum = limits.hasUpperLimit() ? std::min(limits.upperLimit(), +1e+200) : +1e+200;
241  m_validator->setRange(minimum, maximum, 1000);
242 }
243 
245 {
246  double new_value = m_lineEdit->text().toDouble();
247 
248  if (new_value != m_data.toDouble())
249  setDataIntern(QVariant::fromValue(new_value));
250 }
251 
253 {
254  ASSERT(m_data.type() == QVariant::Double);
255  m_lineEdit->setText(QString::number(m_data.toDouble(), 'g'));
256 }
257 
258 // --- DoubleEditor ---
259 
261  : CustomEditor(parent), m_doubleEditor(new QDoubleSpinBox)
262 {
263  setAutoFillBackground(true);
264  setFocusPolicy(Qt::StrongFocus);
265  m_doubleEditor->setFocusPolicy(Qt::StrongFocus);
266  m_doubleEditor->setKeyboardTracking(false);
267 
268  auto layout = new QVBoxLayout;
269  layout->setMargin(0);
270  layout->setSpacing(0);
271 
272  layout->addWidget(m_doubleEditor);
273 
274  connect(m_doubleEditor,
275  static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
276  [=] { this->onEditingFinished(); });
277 
278  setLayout(layout);
279 
280  setFocusProxy(m_doubleEditor);
281 }
282 
284 {
285  m_doubleEditor->setMaximum(std::numeric_limits<double>::max());
286  m_doubleEditor->setMinimum(std::numeric_limits<double>::lowest());
287 
288  if (limits.hasLowerLimit())
289  m_doubleEditor->setMinimum(limits.lowerLimit());
290  if (limits.hasUpperLimit())
291  m_doubleEditor->setMaximum(static_cast<int>(limits.upperLimit()));
292 }
293 
294 void DoubleEditor::setDecimals(int decimals)
295 {
296  m_doubleEditor->setDecimals(decimals);
297  m_doubleEditor->setSingleStep(singleStep(decimals));
298 }
299 
301 {
302  double new_value = m_doubleEditor->value();
303 
304  if (new_value != m_data.toDouble())
305  setDataIntern(QVariant::fromValue(new_value));
306 }
307 
309 {
310  ASSERT(m_data.type() == QVariant::Double);
311  m_doubleEditor->setValue(m_data.toDouble());
312 }
313 
314 // --- DoubleEditor ---
315 
317  : CustomEditor(parent), m_doubleEditor(new ScientificSpinBox)
318 {
319  setAutoFillBackground(true);
320  setFocusPolicy(Qt::StrongFocus);
321  m_doubleEditor->setFocusPolicy(Qt::StrongFocus);
322  m_doubleEditor->setKeyboardTracking(false);
323 
324  auto layout = new QVBoxLayout;
325  layout->setMargin(0);
326  layout->setSpacing(0);
327 
328  layout->addWidget(m_doubleEditor);
329 
331 
332  setLayout(layout);
333 
334  setFocusProxy(m_doubleEditor);
335 }
336 
338 {
340  : std::numeric_limits<double>::lowest());
342  : std::numeric_limits<double>::max());
343 }
344 
346 {
347  m_doubleEditor->setDecimals(decimals);
348  m_doubleEditor->setSingleStep(singleStep(decimals));
349 }
350 
352 {
354 }
355 
357 {
358  double new_value = m_doubleEditor->value();
359 
360  if (new_value != m_data.toDouble())
361  setDataIntern(QVariant::fromValue(new_value));
362 }
363 
365 {
366  ASSERT(m_data.type() == QVariant::Double);
367  m_doubleEditor->setValue(m_data.toDouble());
368 }
369 
370 // --- IntEditor ---
371 
372 IntEditor::IntEditor(QWidget* parent) : CustomEditor(parent), m_intEditor(new QSpinBox)
373 {
374  setAutoFillBackground(true);
375  m_intEditor->setFocusPolicy(Qt::StrongFocus);
376  m_intEditor->setKeyboardTracking(false);
377 
378  auto layout = new QVBoxLayout;
379  layout->setMargin(0);
380  layout->setSpacing(0);
381 
382  layout->addWidget(m_intEditor);
383 
384  connect(m_intEditor, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
385  [=] { this->onEditingFinished(); });
386 
387  setLayout(layout);
388 
389  setFocusProxy(m_intEditor);
390 }
391 
392 void IntEditor::setLimits(const RealLimits& limits)
393 {
394  m_intEditor->setMaximum(std::numeric_limits<int>::max());
395 
396  if (limits.hasLowerLimit())
397  m_intEditor->setMinimum(static_cast<int>(limits.lowerLimit()));
398  if (limits.hasUpperLimit())
399  m_intEditor->setMaximum(static_cast<int>(limits.upperLimit()));
400 }
401 
403 {
404  int new_value = m_intEditor->value();
405 
406  if (new_value != m_data.toInt())
407  setDataIntern(QVariant::fromValue(new_value));
408 }
409 
411 {
412  if (!m_data.isValid() || m_data.type() != QVariant::Int)
413  return;
414  m_intEditor->setValue(m_data.toInt());
415 }
416 
417 // --- BoolEditor ---
418 
419 BoolEditor::BoolEditor(QWidget* parent) : CustomEditor(parent), m_checkBox(new QCheckBox)
420 {
421  setAutoFillBackground(true);
422  auto layout = new QHBoxLayout;
423  layout->setContentsMargins(4, 0, 0, 0);
424  layout->addWidget(m_checkBox);
425  setLayout(layout);
426 
427  connect(m_checkBox, &QCheckBox::toggled, this, &BoolEditor::onCheckBoxChange);
428  setFocusProxy(m_checkBox);
429  m_checkBox->setText(tr("True"));
430 }
431 
433 {
434  if (value != m_data.toBool())
435  setDataIntern(QVariant(value));
436 }
437 
439 {
440  ASSERT(m_data.type() == QVariant::Bool);
441  bool value = m_data.toBool();
442 
443  m_checkBox->blockSignals(true);
444  m_checkBox->setChecked(value);
445  m_checkBox->setText(value ? "True" : "False");
446  m_checkBox->blockSignals(false);
447 }
#define ASSERT(condition)
Definition: Assert.h:31
Defines class ComboProperty.
Defines CustomEditors classes.
Defines classes releted to event filtering.
Defines class GUIHelpers functions.
Defines class GroupItemController.
Defines class MaterialItemUtils.
Defines class ScientificSpinBox.
BoolEditor(QWidget *parent=nullptr)
void initEditor()
Inits editor widgets from m_data.
QCheckBox * m_checkBox
void onCheckBoxChange(bool value)
virtual int internIndex()
Returns index for QComboBox.
void setConnected(bool isConnected)
virtual QStringList internLabels()
Returns list of labels for QComboBox.
virtual void onIndexChanged(int index)
QSize sizeHint() const
class WheelEventEater * m_wheel_event_filter
Definition: CustomEditors.h:90
QSize minimumSizeHint() const
void initEditor()
Inits editor widgets from m_data.
ComboPropertyEditor(QWidget *parent=nullptr)
Custom property to define list of string values with multiple selections.
Definition: ComboProperty.h:25
QStringList getValues() const
int currentIndex() const
void setCurrentIndex(int index)
Base class for all custom variants editors.
Definition: CustomEditors.h:28
QVariant m_data
Definition: CustomEditors.h:45
void dataChanged(const QVariant &data)
Signal emit then user changed the data through the editor.
virtual void initEditor()
Inits editor widgets from m_data.
void setData(const QVariant &data)
Sets the data from the model to editor.
void setDataIntern(const QVariant &data)
Saves the data from the editor and informs external delegates.
void setDecimals(int decimals)
void setLimits(const RealLimits &limits)
class QDoubleSpinBox * m_doubleEditor
DoubleEditor(QWidget *parent=nullptr)
void onEditingFinished()
void initEditor()
Inits editor widgets from m_data.
ExternalPropertyEditor(QWidget *parent=nullptr)
void setExternalDialogType(const QString &dialogType)
LostFocusFilter * m_focusFilter
Definition: CustomEditors.h:66
QString m_extDialogType
Type of the dialog which will be created on button click.
Definition: CustomEditors.h:67
void initEditor()
Inits editor widgets from m_data.
The ExternalProperty class defines custom QVariant property to carry the text, color and an identifie...
QString text() const
QPixmap pixmap() const
IntEditor(QWidget *parent=nullptr)
class QSpinBox * m_intEditor
void setLimits(const RealLimits &limits)
void initEditor()
Inits editor widgets from m_data.
void onEditingFinished()
Event filter to prevent lost of focus by custom material editor.
Limits for a real fit parameter.
Definition: RealLimits.h:24
bool hasUpperLimit() const
if has upper limit
Definition: RealLimits.cpp:57
double upperLimit() const
Returns upper limit.
Definition: RealLimits.cpp:62
double lowerLimit() const
Returns lower limit.
Definition: RealLimits.cpp:40
bool hasLowerLimit() const
if has lower limit
Definition: RealLimits.cpp:35
void initEditor()
Inits editor widgets from m_data.
void setLimits(const RealLimits &limits)
ScientificDoublePropertyEditor(QWidget *parent=nullptr)
class QDoubleValidator * m_validator
void setDecimals(int decimals)
void setLimits(const RealLimits &limits)
void initEditor()
Inits editor widgets from m_data.
void setSingleStep(double step)
ScientificSpinBoxEditor(QWidget *parent=nullptr)
class ScientificSpinBox * m_doubleEditor
void setMaximum(double max)
void setMinimum(double min)
void setValue(double val)
void setSingleStep(double step)
void valueChanged(double value)
Event filter to install on combo boxes and spin boxes to not to react on wheel events during scrollin...
ExternalProperty selectMaterialProperty(const ExternalProperty &previous=ExternalProperty())
Calls material selector dialog.
ExternalProperty materialProperty(const SessionItem &materialItem)
Constructs material property corresponding to given material.
ExternalProperty selectColorProperty(const ExternalProperty &previous=ExternalProperty())
Calls color selector dialog.