DynExp
Highly flexible laboratory automation for dynamically changing experiments.
ParamsConfig.cpp
Go to the documentation of this file.
1 // This file is part of DynExp.
2 
3 #include "stdafx.h"
4 #include "moc_ParamsConfig.cpp"
5 #include "ParamsConfig.h"
6 #include "Object.h"
7 #include "DynExpCore.h"
8 
13 template <typename DestinyType>
15 
21 template <typename DestinyType>
22 using ParamRefWrapperType = std::reference_wrapper<ParamWrapperType<DestinyType>>;
23 
27 using LinkParamRefWrapperType = std::reference_wrapper<DynExp::ParamsBase::LinkParamBase>;
28 
32 using LinkListParamRefWrapperType = std::reference_wrapper<DynExp::ParamsBase::LinkListParamBase>;
33 
34 ParamsConfigDialog::ParamsConfigDialog(QWidget* parent, const DynExp::DynExpCore& Core, std::string Title)
35  : QDialog(parent, Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
36  Core(Core), Object(nullptr), ResetRequired(false)
37 {
38  ui.setupUi(this);
39 
40  setWindowTitle(QString::fromStdString(Title));
41 }
42 
43 void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const NumberType Value,
44  const NumberType MinValue, const NumberType MaxValue, const NumberType Precision, const NumberType Increment)
45 {
46  auto SpinBox = new QDoubleSpinBox(this);
47  SpinBox->setDecimals(Precision);
48  SpinBox->setMinimum(MinValue);
49  SpinBox->setMaximum(MaxValue);
50  SpinBox->setSingleStep(Increment);
51  SpinBox->setValue(Value);
52 
53  Param ParamData(Param::ParamType::Number, SpinBox, Destiny);
54  InsertWidget(std::move(Info), std::move(ParamData));
55 }
56 
57 void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const TextType Value, const DynExp::TextUsageType TextUsage)
58 {
59  auto LineEdit = new QLineEdit(this);
60  LineEdit->setText(QString::fromStdString(Value));
61 
63  (TextUsage == DynExp::TextUsageType::Code ? Param::ParamType::Code : Param::ParamType::Text), LineEdit, Destiny);
64  InsertWidget(std::move(Info), std::move(ParamData));
65 }
66 
67 void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const TextRefType Value,
68  const TextListType& TextList, const bool AllowResetToDefault)
69 {
70  if (TextList.empty())
71  throw Util::EmptyException("A parameter's text list must not be empty.");
72 
73  auto ComboBox = new QComboBox(this);
74  int SelectedIndex = 0;
75 
76  for (const auto& Entry : TextList)
77  {
78  if (Value == Entry)
79  SelectedIndex = ComboBox->count();
80 
81  ComboBox->addItem(QString::fromStdString(Entry));
82  }
83  ComboBox->setCurrentIndex(SelectedIndex);
84 
85  Param ParamData(Param::ParamType::TextList, ComboBox, Destiny, 0, AllowResetToDefault);
86  InsertWidget(std::move(Info), std::move(ParamData));
87 }
88 
89 void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const TextListIndexType Value,
90  const TextListIndexType DefaultValue, const TextListType& TextList)
91 {
92  if (TextList.empty())
93  throw Util::EmptyException("A parameter's text list must not be empty.");
94  if (Value >= TextList.size())
95  throw Util::OutOfRangeException("A parameter's text list does not contain enough entries to select the specified initial value.");
96  if (DefaultValue >= TextList.size())
97  throw Util::OutOfRangeException("A parameter's text list does not contain enough entries to define a default value.");
98 
99  auto ComboBox = new QComboBox(this);
100  for (const auto& Entry : TextList)
101  ComboBox->addItem(QString::fromStdString(Entry));
102  ComboBox->setCurrentIndex(Util::NumToT<int>(Value));
103 
104  Param ParamData(Param::ParamType::IndexedTextList, ComboBox, Destiny, DefaultValue);
105  InsertWidget(std::move(Info), std::move(ParamData));
106 }
107 
108 void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const DynExp::ItemIDType Value,
109  bool IsOptional, std::string_view IconResourcePath, FunctionsToCallIfAcceptedType FunctionToCallIfAccepted,
110  Util::TextValueListType<IndexType>&& ItemIDsWithLabels)
111 {
112  auto ComboBox = new QComboBox(this);
113  int SelectedIndex = 0;
114 
115  if (IsOptional)
116  ComboBox->addItem("< None >", QVariant(static_cast<IndexType>(DynExp::ItemIDNotSet)));
117 
118  FunctionsToCallIfAccepted.push_back(FunctionToCallIfAccepted);
119 
120  for (const auto& TextValuePair : ItemIDsWithLabels)
121  {
122  if (TextValuePair.second == Value)
123  SelectedIndex = ComboBox->count();
124 
125  if (!IconResourcePath.empty())
126  ComboBox->addItem(QIcon(IconResourcePath.data()), QString::fromStdString(TextValuePair.first), QVariant(static_cast<IndexType>(TextValuePair.second)));
127  else
128  ComboBox->addItem(QString::fromStdString(TextValuePair.first), QVariant(static_cast<IndexType>(TextValuePair.second)));
129  }
130  ComboBox->setCurrentIndex(SelectedIndex);
131 
132  Param ParamData(Param::ParamType::Index, ComboBox, Destiny, false);
133  InsertWidget(std::move(Info), std::move(ParamData));
134 }
135 
136 void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const std::vector<DynExp::ItemIDType>& Values,
137  bool IsOptional, std::string_view IconResourcePath, FunctionsToCallIfAcceptedType FunctionToCallIfAccepted,
138  Util::TextValueListType<IndexType>&& ItemIDsWithLabels)
139 {
140  auto ChoiceListDlg = new ChoiceListDialog(this, std::move(ItemIDsWithLabels), Info.Title, IsOptional, IconResourcePath, Values);
141 
142  FunctionsToCallIfAccepted.push_back(FunctionToCallIfAccepted);
143 
144  auto SetButton = new QPushButton(this);
145  SetButton->setProperty("ChoiceListDlg", QVariant::fromValue(ChoiceListDlg));
146  SetButton->setText("Select...");
147  if (!IconResourcePath.empty())
148  SetButton->setIcon(QIcon(IconResourcePath.data()));
149  connect(SetButton, &QPushButton::clicked, ChoiceListDlg, &QDialog::open);
150 
151  Param ParamData(Param::ParamType::IndexList, SetButton, Destiny, false);
152  InsertWidget(std::move(Info), std::move(ParamData));
153 }
154 
156 {
157  this->Object = Object;
158  ResetRequired = false;
159 
160  TextEditorDialogs.clear();
161 
162  adjustSize();
163  layout()->setSizeConstraint(QLayout::SetFixedSize);
164 
165  return exec() == DialogCode::Accepted;
166 }
167 
169 {
170  auto Label = new QLabel(QString::fromStdString(Info.Title), this);
171  Label->setToolTip(QString::fromStdString(Info.Description));
172  ParamData.Widget->setToolTip(QString::fromStdString(Info.Description));
173 
174  auto SubLayout = std::make_unique<QHBoxLayout>();
175  SubLayout->addWidget(ParamData.Widget);
176 
177  ParamList.push_back(std::move(ParamData));
178 
179  if (ParamData.Type == Param::ParamType::Path || ParamData.Type == Param::ParamType::Code)
180  {
181  auto OpenButton = new QPushButton(QIcon(DynExpUI::Icons::Open), "", this);
182  OpenButton->setToolTip("Browse...");
183  OpenButton->setMaximumWidth(24);
184  OpenButton->setProperty("ParamID", QVariant::fromValue(ParamList.size() - 1));
185 
186  // functor-based syntax does not work with inheriting QDialog privately, so use string-based syntax
187  connect(OpenButton, SIGNAL(clicked()), this, SLOT(OnOpenParam()));
188 
189  SubLayout->addWidget(OpenButton);
190 
191  if (ParamData.Type == Param::ParamType::Code)
192  {
193  auto EditButton = new QPushButton(QIcon(DynExpUI::Icons::Edit), "", this);
194  EditButton->setToolTip("Edit...");
195  EditButton->setMaximumWidth(24);
196  EditButton->setProperty("ParamID", QVariant::fromValue(ParamList.size() - 1));
197 
198  // functor-based syntax does not work with inheriting QDialog privately, so use string-based syntax
199  connect(EditButton, SIGNAL(clicked()), this, SLOT(OnEditParam()));
200 
201  SubLayout->addWidget(EditButton);
202  }
203  }
204 
205  if (ParamData.Type != Param::ParamType::Index && ParamData.Type != Param::ParamType::IndexList && ParamData.AllowResetToDefault)
206  {
207  auto ResetButton = new QPushButton(QIcon(DynExpUI::Icons::Undo), "", this);
208  ResetButton->setToolTip("Reset to default value");
209  ResetButton->setMaximumWidth(24);
210  ResetButton->setProperty("ParamID", QVariant::fromValue(ParamList.size() - 1));
211 
212  // functor-based syntax does not work with inheriting QDialog privately, so use string-based syntax
213  connect(ResetButton, SIGNAL(clicked()), this, SLOT(OnResetParam()));
214 
215  SubLayout->addWidget(ResetButton);
216  }
217 
218  ui.MainLayout->addRow(Label, SubLayout.release());
219 }
220 
222 {
223  for (auto EditorDlg : TextEditorDialogs)
224  {
225  if (EditorDlg->isVisible())
226  {
227  // setParent() hides the dialog and resets flags, so show it again and restore flags.
228  auto Flags = EditorDlg->windowFlags();
229  EditorDlg->setParent(nullptr);
230  EditorDlg->setWindowFlags(Flags);
231  EditorDlg->setWindowIcon(QIcon(DynExpUI::Icons::DynExp));
232  EditorDlg->showNormal();
233  EditorDlg->setAttribute(Qt::WA_DeleteOnClose);
234  }
235  }
236 }
237 
238 // Thread-safe since it only reads constants
240 {
241  auto& ParamToOpen = ParamList.at(qobject_cast<QPushButton*>(sender())->property("ParamID").value<size_t>());
242 
243  if (ParamToOpen.Type != Param::ParamType::Path && ParamToOpen.Type != Param::ParamType::Code)
244  throw Util::InvalidStateException("A parameter's type is corrupted.");
245 
246  QString Filename = Util::PromptOpenFilePath(this, "Select file for parameter", "", "", "");
247  if (!Filename.isEmpty())
248  static_cast<QLineEdit*>(ParamToOpen.Widget)->setText(Filename);
249 }
250 
251 // Thread-safe since it only reads constants
253 {
254  auto& ParamToEdit = ParamList.at(qobject_cast<QPushButton*>(sender())->property("ParamID").value<size_t>());
255 
256  if (ParamToEdit.Type != Param::ParamType::Code)
257  throw Util::InvalidStateException("A parameter's type is corrupted.");
258 
259  auto Filename = static_cast<QLineEdit*>(ParamToEdit.Widget)->text();
260  if (!Filename.isEmpty())
261  {
262  // ParamsConfigDialog is parent to allow editing despite ParamsConfigDialog being a modal dialog.
263  TextEditorDialogs.emplace_back(new TextEditor(this, Core.ToAbsolutePath(Filename.toStdString())));
264  TextEditorDialogs.back()->show();
265  }
266 }
267 
268 // Thread-safe since it only reads constants
270 {
271  auto& ParamToReset = ParamList.at(qobject_cast<QPushButton*>(sender())->property("ParamID").value<size_t>());
272 
273  switch (ParamToReset.Type)
274  {
276  static_cast<QDoubleSpinBox*>(ParamToReset.Widget)->setValue(
277  std::any_cast<ParamRefWrapperType<NumberType>>(ParamToReset.Destiny).get().GetDefaultValue());
278  break;
279  case Param::ParamType::SignedInteger: [[fallthrough]];
281  static_cast<QComboBox*>(ParamToReset.Widget)->setCurrentIndex(Util::NumToT<int>(ParamToReset.DefaultIndex));
282  break;
286  static_cast<QLineEdit*>(ParamToReset.Widget)->setText(
287  QString::fromStdString(std::any_cast<ParamRefWrapperType<TextType>>(ParamToReset.Destiny).get().GetDefaultValue()));
288  break;
290  static_cast<QComboBox*>(ParamToReset.Widget)->setCurrentText(
291  QString::fromStdString(std::any_cast<ParamRefWrapperType<TextType>>(ParamToReset.Destiny).get().GetDefaultValue()));
292  break;
294  static_cast<QComboBox*>(ParamToReset.Widget)->setCurrentIndex(Util::NumToT<int>(
295  std::any_cast<ParamRefWrapperType<IndexType>>(ParamToReset.Destiny).get().GetDefaultValue()));
296  break;
297  case Param::ParamType::Index: [[fallthrough]];
298  case Param::ParamType::IndexList: [[fallthrough]];
299  default:
300  throw Util::InvalidStateException("A parameter's type is corrupted.");
301  }
302 }
303 
305 {
306  try
307  {
308  // Ensure thread-safety in case of editing an existing Object. See e.g. class ObjectLink<T>!
310  if (Object)
311  lock = Object->GetParams();
312 
313  ChoiceListDialog* ChoiceListDlg = nullptr;
314 
315  for (const auto& ParamEntry : ParamList)
316  switch (ParamEntry.Type)
317  {
319  Assign(std::any_cast<ParamRefWrapperType<NumberType>>(ParamEntry.Destiny).get(),
320  static_cast<QDoubleSpinBox*>(ParamEntry.Widget)->value());
321  break;
323  Assign(std::any_cast<ParamRefWrapperType<DynExp::ParamsBase::EnumParamSignedIntegerType>>(ParamEntry.Destiny).get(),
324  static_cast<QComboBox*>(ParamEntry.Widget)->currentData().value<DynExp::ParamsBase::EnumParamSignedIntegerType>());
325  break;
327  Assign(std::any_cast<ParamRefWrapperType<DynExp::ParamsBase::EnumParamUnsignedIntegerType>>(ParamEntry.Destiny).get(),
328  static_cast<QComboBox*>(ParamEntry.Widget)->currentData().value<DynExp::ParamsBase::EnumParamUnsignedIntegerType>());
329  break;
333  Assign(std::any_cast<ParamRefWrapperType<TextType>>(ParamEntry.Destiny).get(),
334  static_cast<QLineEdit*>(ParamEntry.Widget)->text().toStdString());
335  break;
337  Assign(std::any_cast<ParamRefWrapperType<TextType>>(ParamEntry.Destiny).get(),
338  static_cast<QComboBox*>(ParamEntry.Widget)->currentText().toStdString());
339  break;
341  Assign(std::any_cast<ParamRefWrapperType<IndexType>>(ParamEntry.Destiny).get(),
342  static_cast<QComboBox*>(ParamEntry.Widget)->currentIndex());
343  break;
345  Assign(std::any_cast<LinkParamRefWrapperType>(ParamEntry.Destiny).get(),
346  static_cast<QComboBox*>(ParamEntry.Widget)->currentData().value<IndexType>());
347  break;
349  ChoiceListDlg = static_cast<QPushButton*>(ParamEntry.Widget)->property("ChoiceListDlg").value<decltype(ChoiceListDlg)>();
350  if (!ChoiceListDlg)
351  throw Util::InvalidDataException("A parameter's \"ChoiceListDlg\" property is corrupted.");
352  if (!ChoiceListDlg->IsOptional() && ChoiceListDlg->GetSelection().empty())
353  {
354  QMessageBox::warning(this, "DynExp - Parameter empty",
355  QString("The parameter \"") + ChoiceListDlg->GetParamName().data() + "\" must not be empty. Please assign at least one item.");
356  return;
357  }
358  Assign(std::any_cast<LinkListParamRefWrapperType>(ParamEntry.Destiny).get(), ChoiceListDlg->GetSelection());
359  break;
360  default:
361  throw Util::InvalidStateException("A parameter's type is corrupted.");
362  }
363 
364  for (const auto& Function : FunctionsToCallIfAccepted)
365  Function();
366  }
367  catch (...)
368  {
369  reject();
370  }
371 
373 
374  QDialog::accept();
375 }
376 
378 {
380 
381  QDialog::reject();
382 }
Defines DynExp's core module as an interface between the UI and DynExp objects.
Implementation of DynExp objects as the base for derived resources and implementation of the object p...
std::reference_wrapper< DynExp::ParamsBase::LinkListParamBase > LinkListParamRefWrapperType
Alias for a reference to a list parameter describing links to multiple DynExp::Object instances.
std::reference_wrapper< ParamWrapperType< DestinyType > > ParamRefWrapperType
Wraps ParamWrapperType into a std::reference_wrapper, thus defining a reference to a typed parameter.
std::reference_wrapper< DynExp::ParamsBase::LinkParamBase > LinkParamRefWrapperType
Alias for a reference to a parameter describing a link to another DynExp::Object instance.
Implements a configuration dialog which allows users to set values of parameters derived from DynExp:...
std::string_view GetParamName() const
bool IsOptional() const noexcept
const auto & GetSelection() const
DynExp's core class acts as the interface between the user interface and DynExp's internal data like ...
Definition: DynExpCore.h:127
std::filesystem::path ToAbsolutePath(const std::filesystem::path &Path) const
Transforms the path Path into an absolute path relative to ProjectParams::ProjectFilename.
Definition: DynExpCore.cpp:279
Base class for all DynExp Objects like hardware adapters (DynExp::HardwareAdapterBase),...
Definition: Object.h:1971
ParamsConstTypeSyncPtrType GetParams(const std::chrono::milliseconds Timeout=GetParamsTimeoutDefault) const
Locks the mutex of the parameter class instance Params assigned to this Object instance and returns a...
Definition: Object.cpp:436
Base class of parameters containing a single value.
Definition: Object.h:539
uintmax_t EnumParamUnsignedIntegerType
Parameter type to convert unsigned eumeration parameters to.
Definition: Object.h:354
intmax_t EnumParamSignedIntegerType
Parameter type to convert signed eumeration parameters to.
Definition: Object.h:353
Util::TextListIndexType TextListIndexType
List index type of Util::TextListType.
Definition: ParamsConfig.h:80
std::vector< TextEditor * > TextEditorDialogs
Keeps a list of text editor dialogs so that their ownership can be removed when the ParamsConfigDialo...
Definition: ParamsConfig.h:363
qulonglong IndexType
ID type of objects/items managed by DynExp.
Definition: ParamsConfig.h:87
bool ResetRequired
Becomes true if an existing DynExp::Object is edited and needs to be reset to apply changes after cli...
Definition: ParamsConfig.h:357
Ui::ParamsConfig ui
Bundles Qt widgets of the ParamsConfigDialog instance's user interface.
Definition: ParamsConfig.h:368
void Assign(ParamT &Param, typename ParamT::UnderlyingType Value)
When the settings dialog is closed by accepting it, assign a single value from the user interface to ...
Definition: ParamsConfig.h:405
std::function< void(void)> FunctionsToCallIfAcceptedType
Signature of a function which is called when the ParamsConfigDialog is closed by the user by clicking...
Definition: ParamsConfig.h:94
double NumberType
Number type used for numeric parameters (DynExp::ParamsBase::Param)
Definition: ParamsConfig.h:76
void OnOpenParam()
Called when clicking the 'Browse' button for a DynExp::TextUsageType::Path or DynExp::TextUsageType::...
void OnResetParam()
Called when resetting a parameter to its default value.
DynExp::Object * Object
DynExp::Object whose parameters are edited. nullptr if a new DynExp::Object is created and synchroniz...
Definition: ParamsConfig.h:351
const DynExp::DynExpCore & Core
Reference to DynExp's core.
Definition: ParamsConfig.h:334
Util::TextRefType TextRefType
Reference-to-string type of text-type parameters (DynExp::ParamsBase::Param)
Definition: ParamsConfig.h:78
Util::TextListType TextListType
List type of text-type parameters.
Definition: ParamsConfig.h:79
virtual void reject() override
Called when the settings dialog is rejected clicking its 'Cancel' button.
void AddParam(ParamInfo &&Info, const std::any Destiny, const NumberType Value, const NumberType MinValue, const NumberType MaxValue, const NumberType Precision, const NumberType Increment)
Appends a parameter to the configuration dialog.
void InsertWidget(ParamInfo &&Info, Param &&ParamData)
Creates Qt widgets for editing a parameter and inserts them into the configuration dialog.
virtual void accept() override
Called when the settings dialog is accepted clicking its 'OK' button.
ParamsConfigDialog(QWidget *parent, const DynExp::DynExpCore &Core, std::string Title)
Constructs a ParamsConfigDialog instance.
std::vector< Param > ParamList
List of all parameters assigned to this ParamsConfigDialog instance.
Definition: ParamsConfig.h:339
std::vector< FunctionsToCallIfAcceptedType > FunctionsToCallIfAccepted
List of functions to be called when acceptig the dialog. Refer to ParamsConfigDialog::FunctionsToCall...
Definition: ParamsConfig.h:345
void OnEditParam()
Called when opening a TextEditor for a DynExp::TextUsageType::Code parameter.
void HandleTextEditorDialogsOnClose()
Called when the settings dialog is closed. Removes the parent widget of text editors listed in TextEd...
bool Display(DynExp::Object *Object=nullptr)
Displays the configuration dialog.
Util::TextType TextType
String type of text-type parameters (DynExp::ParamsBase::Param)
Definition: ParamsConfig.h:77
Thrown when a list is expected to contain entries and when a query results in an empty answer or an e...
Definition: Exception.h:224
Data to operate on is invalid for a specific purpose. This indicates a corrupted data structure or fu...
Definition: Exception.h:163
An operation cannot be performed currently since the related object is in an invalid state like an er...
Definition: Exception.h:150
Thrown when an argument passed to a function exceeds the valid range.
Definition: Exception.h:211
Pointer to lock a class derived from ISynchronizedPointerLockable for synchronizing between threads....
Definition: Util.h:170
constexpr auto Undo
constexpr auto Edit
constexpr auto Info
constexpr auto DynExp
constexpr auto Open
TextUsageType
Specifies the usage of a text-type parameter. Setting the right usage allows the ParamsConfigDialog t...
Definition: ParamsConfig.h:28
size_t ItemIDType
ID type of objects/items managed by DynExp.
QString PromptOpenFilePath(QWidget *Parent, const QString &Title, const QString &DefaultSuffix, const QString &NameFilter, const QString &InitialDirectory)
Opens a file dialog to ask the user to select a single existing file.
Definition: QtUtil.cpp:113
std::vector< std::pair< TextType, ValueType > > TextValueListType
Type of a list containing key-value pairs where key is a text of type Util::TextType.
Definition: QtUtil.h:37
Accumulates include statements to provide a precompiled header.
Bundles a parameter's title and description texts.
Definition: ParamsConfig.h:48
Collection of data defining a single parameter to be managed by a ParamsConfigDialog instance....
Definition: ParamsConfig.h:101