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 "ui_ParamsConfig.h"
5#include "moc_ParamsConfig.cpp"
6#include "ParamsConfig.h"
7#include "Object.h"
8#include "DynExpCore.h"
9
14template <typename DestinyType>
16
22template <typename DestinyType>
23using ParamRefWrapperType = std::reference_wrapper<ParamWrapperType<DestinyType>>;
24
28using LinkParamRefWrapperType = std::reference_wrapper<DynExp::ParamsBase::LinkParamBase>;
29
33using LinkListParamRefWrapperType = std::reference_wrapper<DynExp::ParamsBase::LinkListParamBase>;
34
35ParamsConfigDialog::ParamsConfigDialog(QWidget* parent, const DynExp::DynExpCore& Core, std::string Title)
36 : QDialog(parent, Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
37 ui(std::make_unique<Ui::ParamsConfig>()),
38 Core(Core), Object(nullptr), ResetRequired(false)
39{
40 ui->setupUi(this);
41
42 setWindowTitle(QString::fromStdString(Title));
43}
44
48
49void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const NumberType Value,
50 const NumberType MinValue, const NumberType MaxValue, const NumberType Precision, const NumberType Increment)
51{
52 auto SpinBox = new QDoubleSpinBox(this);
53 SpinBox->setDecimals(Precision);
54 SpinBox->setMinimum(MinValue);
55 SpinBox->setMaximum(MaxValue);
56 SpinBox->setSingleStep(Increment);
57 SpinBox->setValue(Value);
58
59 Param ParamData(Param::ParamType::Number, SpinBox, Destiny);
60 InsertWidget(std::move(Info), std::move(ParamData));
61}
62
63void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const TextType Value, const DynExp::TextUsageType TextUsage)
64{
65 auto LineEdit = new QLineEdit(this);
66 LineEdit->setText(QString::fromStdString(Value));
67
70 InsertWidget(std::move(Info), std::move(ParamData));
71}
72
73void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const TextRefType Value,
74 const TextListType& TextList, const bool AllowResetToDefault)
75{
76 if (TextList.empty())
77 throw Util::EmptyException("A parameter's text list must not be empty.");
78
79 auto ComboBox = new QComboBox(this);
80 int SelectedIndex = 0;
81
82 for (const auto& Entry : TextList)
83 {
84 if (Value == Entry)
85 SelectedIndex = ComboBox->count();
86
87 ComboBox->addItem(QString::fromStdString(Entry));
88 }
89 ComboBox->setCurrentIndex(SelectedIndex);
90
91 Param ParamData(Param::ParamType::TextList, ComboBox, Destiny, 0, AllowResetToDefault);
92 InsertWidget(std::move(Info), std::move(ParamData));
93}
94
95void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const TextListIndexType Value,
96 const TextListIndexType DefaultValue, const TextListType& TextList)
97{
98 if (TextList.empty())
99 throw Util::EmptyException("A parameter's text list must not be empty.");
100 if (Value >= TextList.size())
101 throw Util::OutOfRangeException("A parameter's text list does not contain enough entries to select the specified initial value.");
102 if (DefaultValue >= TextList.size())
103 throw Util::OutOfRangeException("A parameter's text list does not contain enough entries to define a default value.");
104
105 auto ComboBox = new QComboBox(this);
106 for (const auto& Entry : TextList)
107 ComboBox->addItem(QString::fromStdString(Entry));
108 ComboBox->setCurrentIndex(Util::NumToT<int>(Value));
109
110 Param ParamData(Param::ParamType::IndexedTextList, ComboBox, Destiny, DefaultValue);
111 InsertWidget(std::move(Info), std::move(ParamData));
112}
113
114void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const DynExp::ItemIDType Value,
115 bool IsOptional, std::string_view IconResourcePath, FunctionsToCallIfAcceptedType FunctionToCallIfAccepted,
116 Util::TextValueListType<IndexType>&& ItemIDsWithLabels)
117{
118 auto ComboBox = new QComboBox(this);
119 int SelectedIndex = 0;
120
121 if (IsOptional)
122 ComboBox->addItem("< None >", QVariant(static_cast<IndexType>(DynExp::ItemIDNotSet)));
123
124 FunctionsToCallIfAccepted.push_back(FunctionToCallIfAccepted);
125
126 for (const auto& TextValuePair : ItemIDsWithLabels)
127 {
128 if (TextValuePair.second == Value)
129 SelectedIndex = ComboBox->count();
130
131 if (!IconResourcePath.empty())
132 ComboBox->addItem(QIcon(IconResourcePath.data()), QString::fromStdString(TextValuePair.first), QVariant(static_cast<IndexType>(TextValuePair.second)));
133 else
134 ComboBox->addItem(QString::fromStdString(TextValuePair.first), QVariant(static_cast<IndexType>(TextValuePair.second)));
135 }
136 ComboBox->setCurrentIndex(SelectedIndex);
137
138 Param ParamData(Param::ParamType::Index, ComboBox, Destiny, false);
139 InsertWidget(std::move(Info), std::move(ParamData));
140}
141
142void ParamsConfigDialog::AddParam(ParamInfo&& Info, const std::any Destiny, const std::vector<DynExp::ItemIDType>& Values,
143 bool IsOptional, std::string_view IconResourcePath, FunctionsToCallIfAcceptedType FunctionToCallIfAccepted,
144 Util::TextValueListType<IndexType>&& ItemIDsWithLabels)
145{
146 auto ChoiceListDlg = new ChoiceListDialog(this, std::move(ItemIDsWithLabels), Info.Title, IsOptional, IconResourcePath, Values);
147
148 FunctionsToCallIfAccepted.push_back(FunctionToCallIfAccepted);
149
150 auto SetButton = new QPushButton(this);
151 SetButton->setProperty("ChoiceListDlg", QVariant::fromValue(ChoiceListDlg));
152 SetButton->setText("Select...");
153 if (!IconResourcePath.empty())
154 SetButton->setIcon(QIcon(IconResourcePath.data()));
155 connect(SetButton, &QPushButton::clicked, ChoiceListDlg, &QDialog::open);
156
157 Param ParamData(Param::ParamType::IndexList, SetButton, Destiny, false);
158 InsertWidget(std::move(Info), std::move(ParamData));
159}
160
162{
163 this->Object = Object;
164 ResetRequired = false;
165
166 TextEditorDialogs.clear();
167
168 adjustSize();
169 layout()->setSizeConstraint(QLayout::SetFixedSize);
170
171 return exec() == DialogCode::Accepted;
172}
173
175{
176 auto Label = new QLabel(QString::fromStdString(Info.Title), this);
177 Label->setToolTip(QString::fromStdString(Info.Description));
178 ParamData.Widget->setToolTip(QString::fromStdString(Info.Description));
179
180 auto SubLayout = std::make_unique<QHBoxLayout>();
181 SubLayout->addWidget(ParamData.Widget);
182
183 ParamList.push_back(std::move(ParamData));
184
185 if (ParamData.Type == Param::ParamType::Path || ParamData.Type == Param::ParamType::Code)
186 {
187 auto OpenButton = new QPushButton(QIcon(DynExpUI::Icons::Open), "", this);
188 OpenButton->setToolTip("Browse...");
189 OpenButton->setMaximumWidth(24);
190 OpenButton->setProperty("ParamID", QVariant::fromValue(ParamList.size() - 1));
191
192 // functor-based syntax does not work with inheriting QDialog privately, so use string-based syntax
193 connect(OpenButton, SIGNAL(clicked()), this, SLOT(OnOpenParam()));
194
195 SubLayout->addWidget(OpenButton);
196
197 if (ParamData.Type == Param::ParamType::Code)
198 {
199 auto EditButton = new QPushButton(QIcon(DynExpUI::Icons::Edit), "", this);
200 EditButton->setToolTip("Edit...");
201 EditButton->setMaximumWidth(24);
202 EditButton->setProperty("ParamID", QVariant::fromValue(ParamList.size() - 1));
203
204 // functor-based syntax does not work with inheriting QDialog privately, so use string-based syntax
205 connect(EditButton, SIGNAL(clicked()), this, SLOT(OnEditParam()));
206
207 SubLayout->addWidget(EditButton);
208 }
209 }
210
211 if (ParamData.Type != Param::ParamType::Index && ParamData.Type != Param::ParamType::IndexList && ParamData.AllowResetToDefault)
212 {
213 auto ResetButton = new QPushButton(QIcon(DynExpUI::Icons::Undo), "", this);
214 ResetButton->setToolTip("Reset to default value");
215 ResetButton->setMaximumWidth(24);
216 ResetButton->setProperty("ParamID", QVariant::fromValue(ParamList.size() - 1));
217
218 // functor-based syntax does not work with inheriting QDialog privately, so use string-based syntax
219 connect(ResetButton, SIGNAL(clicked()), this, SLOT(OnResetParam()));
220
221 SubLayout->addWidget(ResetButton);
222 }
223
224 ui->MainLayout->addRow(Label, SubLayout.release());
225}
226
228{
229 for (auto EditorDlg : TextEditorDialogs)
230 {
231 if (EditorDlg->isVisible())
232 {
233 // setParent() hides the dialog and resets flags, so show it again and restore flags.
234 auto Flags = EditorDlg->windowFlags();
235 EditorDlg->setParent(nullptr);
236 EditorDlg->setWindowFlags(Flags);
237 EditorDlg->setWindowIcon(QIcon(DynExpUI::Icons::DynExp));
238 EditorDlg->showNormal();
239 EditorDlg->setAttribute(Qt::WA_DeleteOnClose);
240 }
241 }
242}
243
244// Thread-safe since it only reads constants
246{
247 auto& ParamToOpen = ParamList.at(qobject_cast<QPushButton*>(sender())->property("ParamID").value<size_t>());
248
249 if (ParamToOpen.Type != Param::ParamType::Path && ParamToOpen.Type != Param::ParamType::Code)
250 throw Util::InvalidStateException("A parameter's type is corrupted.");
251
252 QString Filename = Util::PromptOpenFilePath(this, "Select file for parameter", "", "", "");
253 if (!Filename.isEmpty())
254 static_cast<QLineEdit*>(ParamToOpen.Widget)->setText(Filename);
255}
256
257// Thread-safe since it only reads constants
259{
260 auto& ParamToEdit = ParamList.at(qobject_cast<QPushButton*>(sender())->property("ParamID").value<size_t>());
261
262 if (ParamToEdit.Type != Param::ParamType::Code)
263 throw Util::InvalidStateException("A parameter's type is corrupted.");
264
265 auto Filename = static_cast<QLineEdit*>(ParamToEdit.Widget)->text();
266 if (!Filename.isEmpty())
267 {
268 // ParamsConfigDialog is parent to allow editing despite ParamsConfigDialog being a modal dialog.
269 TextEditorDialogs.emplace_back(new TextEditor(this, Core.ToAbsolutePath(Filename.toStdString())));
270 TextEditorDialogs.back()->show();
271 }
272}
273
274// Thread-safe since it only reads constants
276{
277 auto& ParamToReset = ParamList.at(qobject_cast<QPushButton*>(sender())->property("ParamID").value<size_t>());
278
279 switch (ParamToReset.Type)
280 {
282 static_cast<QDoubleSpinBox*>(ParamToReset.Widget)->setValue(
283 std::any_cast<ParamRefWrapperType<NumberType>>(ParamToReset.Destiny).get().GetDefaultValue());
284 break;
285 case Param::ParamType::SignedInteger: [[fallthrough]];
287 static_cast<QComboBox*>(ParamToReset.Widget)->setCurrentIndex(Util::NumToT<int>(ParamToReset.DefaultIndex));
288 break;
292 static_cast<QLineEdit*>(ParamToReset.Widget)->setText(
293 QString::fromStdString(std::any_cast<ParamRefWrapperType<TextType>>(ParamToReset.Destiny).get().GetDefaultValue()));
294 break;
296 static_cast<QComboBox*>(ParamToReset.Widget)->setCurrentText(
297 QString::fromStdString(std::any_cast<ParamRefWrapperType<TextType>>(ParamToReset.Destiny).get().GetDefaultValue()));
298 break;
300 static_cast<QComboBox*>(ParamToReset.Widget)->setCurrentIndex(Util::NumToT<int>(
301 std::any_cast<ParamRefWrapperType<IndexType>>(ParamToReset.Destiny).get().GetDefaultValue()));
302 break;
303 case Param::ParamType::Index: [[fallthrough]];
304 case Param::ParamType::IndexList: [[fallthrough]];
305 default:
306 throw Util::InvalidStateException("A parameter's type is corrupted.");
307 }
308}
309
311{
312 try
313 {
314 // Ensure thread-safety in case of editing an existing Object. See e.g. class ObjectLink<T>!
316 if (Object)
317 lock = Object->GetParams();
318
319 ChoiceListDialog* ChoiceListDlg = nullptr;
320
321 for (const auto& ParamEntry : ParamList)
322 switch (ParamEntry.Type)
323 {
325 Assign(std::any_cast<ParamRefWrapperType<NumberType>>(ParamEntry.Destiny).get(),
326 static_cast<QDoubleSpinBox*>(ParamEntry.Widget)->value());
327 break;
330 static_cast<QComboBox*>(ParamEntry.Widget)->currentData().value<DynExp::ParamsBase::EnumParamSignedIntegerType>());
331 break;
334 static_cast<QComboBox*>(ParamEntry.Widget)->currentData().value<DynExp::ParamsBase::EnumParamUnsignedIntegerType>());
335 break;
339 Assign(std::any_cast<ParamRefWrapperType<TextType>>(ParamEntry.Destiny).get(),
340 static_cast<QLineEdit*>(ParamEntry.Widget)->text().toStdString());
341 break;
343 Assign(std::any_cast<ParamRefWrapperType<TextType>>(ParamEntry.Destiny).get(),
344 static_cast<QComboBox*>(ParamEntry.Widget)->currentText().toStdString());
345 break;
347 Assign(std::any_cast<ParamRefWrapperType<IndexType>>(ParamEntry.Destiny).get(),
348 static_cast<QComboBox*>(ParamEntry.Widget)->currentIndex());
349 break;
351 Assign(std::any_cast<LinkParamRefWrapperType>(ParamEntry.Destiny).get(),
352 static_cast<QComboBox*>(ParamEntry.Widget)->currentData().value<IndexType>());
353 break;
355 ChoiceListDlg = static_cast<QPushButton*>(ParamEntry.Widget)->property("ChoiceListDlg").value<decltype(ChoiceListDlg)>();
356 if (!ChoiceListDlg)
357 throw Util::InvalidDataException("A parameter's \"ChoiceListDlg\" property is corrupted.");
358 if (!ChoiceListDlg->IsOptional() && ChoiceListDlg->GetSelection().empty())
359 {
360 QMessageBox::warning(this, "DynExp - Parameter empty",
361 QString("The parameter \"") + ChoiceListDlg->GetParamName().data() + "\" must not be empty. Please assign at least one item.");
362 return;
363 }
364 Assign(std::any_cast<LinkListParamRefWrapperType>(ParamEntry.Destiny).get(), ChoiceListDlg->GetSelection());
365 break;
366 default:
367 throw Util::InvalidStateException("A parameter's type is corrupted.");
368 }
369
370 for (const auto& Function : FunctionsToCallIfAccepted)
371 Function();
372 }
373 catch (...)
374 {
375 reject();
376 }
377
379
380 QDialog::accept();
381}
382
384{
386
387 QDialog::reject();
388}
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...
std::unique_ptr< 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:225
Data to operate on is invalid for a specific purpose. This indicates a corrupted data structure or fu...
Definition Exception.h:164
An operation cannot be performed currently since the related object is in an invalid state like an er...
Definition Exception.h:151
Thrown when an argument passed to a function exceeds the valid range.
Definition Exception.h:212
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....