DynExp
Highly flexible laboratory automation for dynamically changing experiments.
Loading...
Searching...
No Matches
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
13template <typename DestinyType>
15
21template <typename DestinyType>
22using ParamRefWrapperType = std::reference_wrapper<ParamWrapperType<DestinyType>>;
23
27using LinkParamRefWrapperType = std::reference_wrapper<DynExp::ParamsBase::LinkParamBase>;
28
32using LinkListParamRefWrapperType = std::reference_wrapper<DynExp::ParamsBase::LinkListParamBase>;
33
34ParamsConfigDialog::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
43void 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
57void 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
64 InsertWidget(std::move(Info), std::move(ParamData));
65}
66
67void 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
89void 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
108void 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
136void 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;
324 static_cast<QComboBox*>(ParamEntry.Widget)->currentData().value<DynExp::ParamsBase::EnumParamSignedIntegerType>());
325 break;
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.
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.
std::vector< TextEditor * > TextEditorDialogs
Keeps a list of text editor dialogs so that their ownership can be removed when the ParamsConfigDialo...
qulonglong IndexType
ID type of objects/items managed by DynExp.
bool ResetRequired
Becomes true if an existing DynExp::Object is edited and needs to be reset to apply changes after cli...
Ui::ParamsConfig ui
Bundles Qt widgets of the ParamsConfigDialog instance's user interface.
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 ...
std::function< void(void)> FunctionsToCallIfAcceptedType
Signature of a function which is called when the ParamsConfigDialog is closed by the user by clicking...
double NumberType
Number type used for numeric parameters (DynExp::ParamsBase::Param)
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...
const DynExp::DynExpCore & Core
Reference to DynExp's core.
Util::TextRefType TextRefType
Reference-to-string type of text-type parameters (DynExp::ParamsBase::Param)
Util::TextListType TextListType
List type of text-type parameters.
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.
std::vector< FunctionsToCallIfAcceptedType > FunctionsToCallIfAccepted
List of functions to be called when acceptig the dialog. Refer to ParamsConfigDialog::FunctionsToCall...
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)
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 DynExp
TextUsageType
Specifies the usage of a text-type parameter. Setting the right usage allows the ParamsConfigDialog t...
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.
Collection of data defining a single parameter to be managed by a ParamsConfigDialog instance....