DynExp
Highly flexible laboratory automation for dynamically changing experiments.
Loading...
Searching...
No Matches
DynExpManager.cpp
Go to the documentation of this file.
1// This file is part of DynExp.
2
3#include "stdafx.h"
4#include "moc_DynExpManager.cpp"
5#include "DynExpManager.h"
6
8 : QMainWindow(parent), DynExpCore(DynExpCore),
9 UpdateUITimer(new QTimer(this)),
10 AboutDialog(new DynExpAbout(this)), CircuitDiagramDlg(std::make_unique<CircuitDiagram>(nullptr)),
11 ModuleWindowsActionGroup(new QActionGroup(this)),
12 UIThemeActionGroup(new QActionGroup(this)), UIBrightThemeAction(nullptr), UIDarkThemeAction(nullptr),
13 LogContextMenu(new QMenu(this)),
14 ItemTreeContextMenu(new QMenu(this)), ClearWarningAction(nullptr),
15 ItemTreeHardwareAdapters(nullptr), ItemTreeInstruments(nullptr), ItemTreeModules(nullptr),
16 StatusBar(this), IsResetting(false), ShouldRedrawCircuitDiagram(true), ShouldUpdateCircuitDiagram(false)
17{
18 qApp->setStyle(QStyleFactory::create("Fusion"));
19
20 ui.setupUi(this);
21
22 // Item Libraries
29
30 // Window menu
31 connect(ui.menu_Window, &QMenu::aboutToShow, this, &DynExpManager::OnWindowMenuOpened);
32 connect(ui.menu_Window, &QMenu::aboutToHide, this, &DynExpManager::OnWindowMenuClosed);
33
34 // Theme menu
35 UIBrightThemeAction = UIThemeActionGroup->addAction("&Bright");
36 UIBrightThemeAction->setCheckable(true);
37 UIBrightThemeAction->setChecked(true);
38 UIDarkThemeAction = UIThemeActionGroup->addAction("&Dark");
39 UIDarkThemeAction->setCheckable(true);
40 ui.menu_UI_Theme->addActions(UIThemeActionGroup->actions());
41 connect(UIThemeActionGroup, &QActionGroup::triggered, this, &DynExpManager::OnUIThemeChanged);
42
43 // Log table
44 ui.tableLog->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
45 ui.tableLog->verticalHeader()->setMinimumSectionSize(18);
46 ui.tableLog->verticalHeader()->setDefaultSectionSize(ui.tableLog->verticalHeader()->minimumSectionSize());
47 ui.tableLog->setColumnWidth(0, 140);
48
49 // Log table context menu
50 LogContextMenu->addAction("&Clear Log", this, &DynExpManager::OnClearLog);
51
52 // Status bar
53 ui.statusBarMain->addWidget(StatusBar.State, 8);
54 ui.statusBarMain->addPermanentWidget(StatusBar.NumRunningInstrGroup, 1);
55 ui.statusBarMain->addPermanentWidget(StatusBar.NumRunningModuleGroup, 1);
56 connect(StatusBar.State, &QPushButton::clicked, this, &DynExpManager::OnStatusBarStateClicked);
57
58 // Error list dialog
60
61 // Item tree
62 ItemTreeHardwareAdapters = new QTreeWidgetItem(ui.treeItems, { "Hardware Adapters", "", "" });
64 ItemTreeHardwareAdapters->setFirstColumnSpanned(true);
65 ItemTreeInstruments = new QTreeWidgetItem(ui.treeItems, { "Instruments", "", "" });
67 ItemTreeInstruments->setFirstColumnSpanned(true);
68 ItemTreeModules = new QTreeWidgetItem(ui.treeItems, { "Modules", "", "" });
69 ItemTreeModules->setIcon(0, QIcon(DynExpUI::Icons::Module));
70 ItemTreeModules->setFirstColumnSpanned(true);
71 ui.treeItems->header()->resizeSection(0, 120);
72 ui.treeItems->header()->resizeSection(1, 180);
73 ui.treeItems->header()->resizeSection(2, 100);
74 ui.splitterInstrListMain->setStretchFactor(0, 16);
75 ui.splitterInstrListMain->setStretchFactor(1, 5);
76
77 // Item tree context menu
78 ClearWarningAction = ItemTreeContextMenu->addAction("Clear &Warning", this, &DynExpManager::OnClearWarning);
79
80 // MDI area
81 connect(ui.mdiMain, &QMdiArea::subWindowActivated, this, &DynExpManager::OnModuleWindowActivated);
82
84
85 // If a project has been loaded due to a command line option, run it now.
88
89 // Start UI callback timer (63 fps UI update frequency should enable fluent motion)
90 connect(UpdateUITimer, &QTimer::timeout, this, &DynExpManager::OnUpdateUI);
91 UpdateUITimer->setInterval(std::chrono::milliseconds(16));
92 UpdateUITimer->start();
93}
94
96 : NumItemsInWarningState(0), NumItemsInErrorState(0),
97 State(new QPushButton(Owner)),
98 NumRunningInstrGroup(new QWidget(Owner)), NumRunningInstrLayout(new QHBoxLayout),
99 NumRunningInstrImage(new QLabel(Owner)), NumRunningInstr(new QLabel(Owner)),
100 NumRunningModuleGroup(new QWidget(Owner)), NumRunningModuleLayout(new QHBoxLayout),
101 NumRunningModuleImage(new QLabel(Owner)), NumRunningModule(new QLabel(Owner))
102{
103 constexpr int Height = 16;
104
105 State->setFlat(true);
106 State->setMinimumHeight(24);
107 State->setFocusPolicy(Qt::FocusPolicy::NoFocus);
108 State->setStyleSheet(QString::fromStdString(DynExpUI::PushButtonReadyStyleSheetBright));
109
110 NumRunningInstrImage->setFixedHeight(Height);
111 NumRunningInstrImage->setSizePolicy({ QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum });
112 NumRunningInstrImage->setToolTip("Running instruments");
113 NumRunningInstrImage->setPixmap(QPixmap(DynExpUI::Icons::Instrument).scaledToHeight(Height));
114 NumRunningInstr->setSizePolicy({ QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding });
115 NumRunningInstr->setToolTip("Running instruments");
116
117 NumRunningModuleImage->setFixedHeight(Height);
118 NumRunningModuleImage->setSizePolicy({ QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum });
119 NumRunningModuleImage->setToolTip("Running modules");
120 NumRunningModuleImage->setPixmap(QPixmap(DynExpUI::Icons::Module).scaledToHeight(Height));
121 NumRunningModule->setSizePolicy({ QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding });
122 NumRunningModule->setToolTip("Running modules");
123
127
131}
132
134{
135 std::string ObjectName = "< Unknown >";
136
137 if (Object)
138 {
139 try
140 {
141 ObjectName = Object->GetObjectName();
142 }
143 catch (...)
144 {
145 ObjectName = "< Error while fetching name >";
146 }
147 }
148
149 return ObjectName;
150}
151
153{
154 if (!Object)
155 return;
156
157 std::string ErrorMessage = "";
158 try
159 {
160 Object->EnsureReadyState(false);
161 }
162 catch (const Util::Exception& e)
163 {
164 Util::EventLog().Log(e);
165 ErrorMessage = e.what();
166 }
167 catch (const std::exception& e)
168 {
169 ErrorMessage = e.what();
170 }
171 catch (...)
172 {
173 ErrorMessage = "Unknown Error";
174 }
175
176 if (!ErrorMessage.empty())
177 {
178 QMessageBox::warning(this, "DynExp - Error",
179 QString::fromStdString(
180 "Starting up the item " + GetObjectNameSafe(Object) + ", the following error occurred:\n\n"
181 + ErrorMessage
182 ));
183 }
184}
185
187{
188 if (!Object)
189 return;
190
191 std::string ErrorMessage = "";
192 try
193 {
194 Object->Reset();
195 }
196 catch (const Util::Exception& e)
197 {
198 Util::EventLog().Log(e);
199 ErrorMessage = e.what();
200 }
201 catch (const std::exception& e)
202 {
203 ErrorMessage = e.what();
204 }
205 catch (...)
206 {
207 ErrorMessage = "Unknown Error";
208 }
209
210 if (!ErrorMessage.empty())
211 {
212 std::string ObjectName = GetObjectNameSafe(Object);
213
214 QMessageBox::warning(this, "DynExp - Error",
215 QString::fromStdString(
216 "Resetting the item " + ObjectName + ", the following error occurred:\n\n"
217 + ErrorMessage
218 ));
219 }
220}
221
223{
224 auto EventLogSize = Util::EventLog().GetLogSize();
225 if (EventLogSize >= std::numeric_limits<int>::max())
226 return;
227
228 if (ui.tableLog->rowCount() < static_cast<int>(EventLogSize))
229 {
230 auto Log = Util::EventLog().GetLog(ui.tableLog->rowCount());
231 for (const auto& LogEntry : Log)
232 {
233 const auto Row = ui.tableLog->rowCount();
234
235 ui.tableLog->setRowCount(Row + 1);
236 ui.tableLog->setItem(Row, 0, new QTableWidgetItem(QString::fromStdString(Util::ToStr(LogEntry.TimePoint))));
237 ui.tableLog->setItem(Row, 1, new QTableWidgetItem(Util::Exception::GetErrorLabel(LogEntry.Type)));
238 ui.tableLog->setItem(Row, 2, new QTableWidgetItem(QString::fromStdString(LogEntry.Message)));
239
240 for (int i = 0; i < 3; ++i)
241 ui.tableLog->item(Row, i)->setForeground(HTMLColorStringToThemeColor(Util::Exception::GetErrorLabelColor(LogEntry.Type)));
242
243 ui.tableLog->resizeColumnToContents(2);
244 ui.tableLog->scrollToBottom();
245 }
246 }
247}
248
250{
251 for (auto i = ui.tableLog->rowCount() - 1; i >= 0; --i)
252 for (int j = 0; j < 3; ++j)
253 ui.tableLog->item(i, j)->setForeground(AdjustColorToThemeColor(ui.tableLog->item(i, j)->foreground().color()));
254}
255
257{
258 const auto& ModuleManager = DynExpCore.GetModuleManager();
259
260 for (auto ModuleIter = ModuleManager.cbegin(); ModuleIter != ModuleManager.cend(); ++ModuleIter)
261 {
262 try
263 {
264 auto Module = ModuleIter->second.ResourcePointer.get();
265 if (!Module)
266 continue;
267 if (!Module->HasUI())
268 continue;
269
270 // Conversion should always be fine. Throws if not.
271 auto& QModule = dynamic_cast<DynExp::QModuleBase&>(*Module);
272
273 if (!QModule.IsRunning())
274 QModule.HideUI();
275 else if (QModule.IsPaused())
276 QModule.DisableUI();
277 else
278 QModule.UpdateUI();
279 }
280 catch (const Util::Exception& e)
281 {
282#ifdef DYNEXP_DEBUG
283 Util::EventLog().Log("Updating a module's UI, the error listed below occurred.", Util::ErrorType::Error);
284 Util::EventLog().Log(e);
285#else
287 {
288 Util::EventLog().Log("Updating a module's UI, the error listed below occurred.", Util::ErrorType::Error);
289 Util::EventLog().Log(e);
290 }
291#endif // DYNEXP_DEBUG
292 }
293 catch (const std::exception& e)
294 {
295 Util::EventLog().Log("Updating a module's UI, the error listed below occurred.", Util::ErrorType::Error);
296 Util::EventLog().Log(e.what());
297 }
298 catch (...)
299 {
300 Util::EventLog().Log("Updating a module's UI, an unknown error occurred.", Util::ErrorType::Error);
301 }
302 }
303}
304
306{
308 {
309 auto ProjectFilename = DynExpCore.GetProjectFilename();
310 setWindowTitle(QString::fromStdString(std::string("DynExp Manager - ") + ProjectFilename.string()));
311 }
312 else
313 setWindowTitle("DynExp Manager");
314
315 ui.action_Restore_Windows_from_Settings->setEnabled(static_cast<const DynExp::DynExpCore&>(DynExpCore).GetParams()->StoreWindowStates ==
317}
318
320{
321 const auto NumRunningInstr = DynExpCore.GetInstrumentManager().GetNumRunningInstruments();
322 const auto NumRunningModule = DynExpCore.GetModuleManager().GetNumRunningModules();
323
324 StatusBar.NumRunningInstr->setText(QString::number(NumRunningInstr));
325 StatusBar.NumRunningModule->setText(QString::number(NumRunningModule));
326
328 {
329 StatusBar.State->setText(QString::number(StatusBar.NumItemsInErrorState) +
330 QString((StatusBar.NumItemsInErrorState != 1 ? " errors" : " error")) +
331 ", " + QString::number(StatusBar.NumItemsInWarningState) +
332 QString((StatusBar.NumItemsInWarningState != 1 ? " warnings" : " warning")) + " occurred.");
333 StatusBar.State->setStyleSheet(QString::fromStdString(DynExpUI::PushButtonErrorStyleSheet));
334 }
335 else
336 {
338 {
339 StatusBar.State->setText(QString::number(StatusBar.NumItemsInWarningState) +
340 QString((StatusBar.NumItemsInWarningState != 1 ? " warnings" : " warning")) + " occurred.");
341 StatusBar.State->setStyleSheet(QString::fromStdString(DynExpUI::PushButtonWarningStyleSheet));
342 }
343 else
344 {
345 if (NumRunningInstr + NumRunningModule > 0)
346 {
347 StatusBar.State->setText("Running");
348 StatusBar.State->setStyleSheet(QString::fromStdString(DynExpUI::PushButtonRunningStyleSheet));
349 }
350 else
351 {
352 StatusBar.State->setText("Ready");
353 StatusBar.State->setStyleSheet(QString::fromStdString(UIBrightThemeAction->isChecked() ?
355 }
356 }
357 }
358
359 // When item in ErrorListDlg has been double-clicked, select the respective entry in ui.treeItems.
362}
363
365{
367 return;
368
370 {
371 if (CircuitDiagramDlg->Redraw(DynExpCore))
372 {
375 }
376 else
377 return;
378 }
379
381 {
382 if (CircuitDiagramDlg->UpdateStates(DynExpCore))
384 else
385 return;
386 }
387
388 // When item in CircuitDiagramDlg has been double-clicked, select the respective entry in ui.treeItems.
389 SelectItemTreeItem(CircuitDiagramDlg->GetSelectedEntry());
390}
391
393{
396
397 ErrorEntries.clear();
398
399 // Update item tree and select item most recently added. This is required since editing an item
400 // causes it to be removed and readded to the tree. Its selection state is restored.
401 // Reverse order to show low level (hardware adapter) errors in status bar with highest priority.
404 if (LastAdded)
405 ItemToSelect = LastAdded;
407 if (LastAdded)
408 ItemToSelect = LastAdded;
409
410 if (ItemToSelect)
411 {
412 ui.treeItems->clearSelection();
413 ItemToSelect->setSelected(true);
414 }
415}
416
418{
419 const auto ItemData = Resource.TreeWidgetItem->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
420
421 try
422 {
423 const auto ExceptionPtr = Resource.ResourcePointer->GetException();
424 const auto Warning = Resource.ResourcePointer->GetWarning();
425 const auto IsConnected = Resource.ResourcePointer->IsConnected();
426
427 std::string ObjectName;
428 if (ExceptionPtr || Warning)
429 ObjectName = GetObjectNameSafe(Resource.ResourcePointer.get()) + " (" + Resource.ResourcePointer->GetCategoryAndName() + ")";
430
431 if (ExceptionPtr)
432 {
433 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Error)
434 ChangeItemTreeItemToErrorState(*Resource.TreeWidgetItem, ItemData, ExceptionPtr);
435
437 auto ErrorMessage = Resource.TreeWidgetItem->toolTip(2); // Contains error message.
438
439 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), ErrorMessage, Util::ErrorType::Error, Resource.TreeWidgetItem.get());
440 }
441 else if (Warning)
442 {
443 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Warning)
444 ChangeItemTreeItemToWarningState(*Resource.TreeWidgetItem, ItemData, QString::fromStdString(Warning.Description));
445
447 auto WarningMessage = Resource.TreeWidgetItem->toolTip(2); // Contains warning message.
448
449 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), WarningMessage, Util::ErrorType::Warning, Resource.TreeWidgetItem.get());
450 }
451 else if (!IsConnected)
452 {
453 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::NotConnected)
455 "This hardware adapter is not connected yet, and thus it cannot be used now.");
456 }
457 else
458 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Normal)
460 "This hardware adapter is ready to be used.", "Ready", DynExpUI::Icons::Ready);
461 }
462 catch ([[maybe_unused]] const Util::TimeoutException& e)
463 {
464 constexpr auto Msg = "This hardware adapter does not respond.";
465
466 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::NotResponding)
468
470 }
471}
472
474{
475 const auto ItemData = Resource.TreeWidgetItem->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
476
477 try
478 {
479 const auto ExceptionPtr = Resource.ResourcePointer->GetException();
480 const auto Warning = Resource.ResourcePointer->GetWarning();
481
482 std::string ObjectName;
483 if (ExceptionPtr || Warning)
484 ObjectName = GetObjectNameSafe(Resource.ResourcePointer.get()) + " (" + Resource.ResourcePointer->GetCategoryAndName() + ")";
485
486 if (ExceptionPtr)
487 {
488 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Error)
489 ChangeItemTreeItemToErrorState(*Resource.TreeWidgetItem, ItemData, ExceptionPtr);
490
492 auto ErrorMessage = Resource.TreeWidgetItem->toolTip(2); // Contains error message.
493
494 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), ErrorMessage, Util::ErrorType::Error, Resource.TreeWidgetItem.get());
495 }
496 else if (Warning)
497 {
498 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Warning)
499 ChangeItemTreeItemToWarningState(*Resource.TreeWidgetItem, ItemData, QString::fromStdString(Warning.Description));
500
502 auto WarningMessage = Resource.TreeWidgetItem->toolTip(2); // Contains warning message.
503
504 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), WarningMessage, Util::ErrorType::Warning, Resource.TreeWidgetItem.get());
505 }
506 else
507 {
508 if (Resource.ResourcePointer->IsRunning() &&
509 ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Running)
510 ChangeItemTreeItemToRunningState(*Resource.TreeWidgetItem, ItemData, "This instrument is running.");
511
512 if (!Resource.ResourcePointer->IsRunning() &&
513 ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Normal)
515 "This instrument is ready to be run.", "Stopped");
516 }
517 }
518 catch ([[maybe_unused]] const Util::TimeoutException& e)
519 {
520 constexpr auto Msg = "This instrument does not respond.";
521
522 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::NotResponding)
524
526 }
527}
528
530{
531 const auto ItemData = Resource.TreeWidgetItem->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
532
533 static constexpr auto PausedString = "This module is paused because one or more linked instruments have been terminated. Restart these instruments in order to continue.";
534 static bool DisplayWarning = false;
535 static auto DisplayWarningLastSwitchTime = std::chrono::system_clock::now();
536
537 if (DisplayWarningLastSwitchTime <= std::chrono::system_clock::now() - std::chrono::seconds(2))
538 {
539 DisplayWarning = !DisplayWarning;
540 DisplayWarningLastSwitchTime = std::chrono::system_clock::now();
541 }
542
543 try
544 {
545 const auto ExceptionPtr = Resource.ResourcePointer->GetException();
546 const auto Warning = Resource.ResourcePointer->GetWarning();
547
548 std::string ObjectName;
549 if (ExceptionPtr || Warning || Resource.ResourcePointer->IsPaused())
550 ObjectName = GetObjectNameSafe(Resource.ResourcePointer.get()) + " (" + Resource.ResourcePointer->GetCategoryAndName() + ")";
551
552 if (ExceptionPtr)
553 {
554 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Error)
555 ChangeItemTreeItemToErrorState(*Resource.TreeWidgetItem, ItemData, ExceptionPtr);
556
558 auto ErrorMessage = Resource.TreeWidgetItem->toolTip(2); // Contains error message.
559
560 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), ErrorMessage, Util::ErrorType::Error, Resource.TreeWidgetItem.get());
561 }
562 else if (Warning || Resource.ResourcePointer->IsPaused())
563 {
564 if (Warning)
565 {
567 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), QString::fromStdString(Warning.Description), Util::ErrorType::Warning, Resource.TreeWidgetItem.get());
568 }
569 if (Resource.ResourcePointer->IsPaused())
570 {
572 ErrorEntries.emplace_back(QString::fromStdString(ObjectName),
573 QString(PausedString) + "\n\n" + QString::fromStdString(Resource.ResourcePointer->GetReasonWhyPaused().Description),
575 }
576
577 if ((Warning && !Resource.ResourcePointer->IsPaused()) || (Warning && DisplayWarning))
578 {
579 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Warning)
580 ChangeItemTreeItemToWarningState(*Resource.TreeWidgetItem, ItemData, QString::fromStdString(Warning.Description));
581 }
582 else
583 {
584 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Paused)
585 ChangeItemTreeItemToPausedState(*Resource.TreeWidgetItem, ItemData, PausedString);
586 }
587 }
588 else
589 {
590 if (Resource.ResourcePointer->IsRunning() &&
591 ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Running)
592 ChangeItemTreeItemToRunningState(*Resource.TreeWidgetItem, ItemData, "This module is running.");
593
594 if (!Resource.ResourcePointer->IsRunning() &&
595 ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Normal)
597 "This module is ready to be run.", "Stopped");
598 }
599 }
600 catch ([[maybe_unused]] const Util::TimeoutException& e)
601 {
602 constexpr auto Msg = "This module does not respond.";
603
604 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::NotResponding)
606
608 }
609}
610
611void DynExpManager::UpdateItemTreeItemObjectName(QTreeWidgetItem* Item, const DynExp::Object* Object)
612{
613 QString ObjectName;
614 bool FetchingObjectNameFailed = false;
615
616 try
617 {
618 ObjectName = QString::fromStdString(Object->GetObjectName());
619 }
620 catch (...)
621 {
622 ObjectName = "< Error >";
623 FetchingObjectNameFailed = true;
624 }
625
626 Item->setText(0, ObjectName);
627 Item->setToolTip(0, ObjectName);
628 Item->setData(0, Qt::ItemDataRole::UserRole, QVariant::fromValue(FetchingObjectNameFailed));
629}
630
631void DynExpManager::SelectItemTreeItem(QTreeWidgetItem* SelectedEntry)
632{
633 if (!SelectedEntry)
634 return;
635
636 for (int i = 0; i < ui.treeItems->topLevelItemCount(); ++i)
637 {
638 QTreeWidgetItem* ParentItem = ui.treeItems->topLevelItem(i);
639 for (int j = 0; j < ParentItem->childCount(); ++j)
640 {
641 QTreeWidgetItem* ChildItem = ParentItem->child(j);
642 if (ChildItem->data(1, Qt::ItemDataRole::UserRole).value<decltype(SelectedEntry)>() == SelectedEntry)
643 {
645
646 ui.treeItems->clearSelection();
647 ui.treeItems->setFocus();
648 ChildItem->setSelected(true);
649
650 return;
651 }
652 }
653 }
654}
655
656void DynExpManager::ChangeItemTreeItemToNormalState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
657 const QString& Description, const QString& StateTitle, const char* IconPath)
658{
659 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
660 QVariant::fromValue(ItemTreeItemDataType(ItemTreeItemData.ID, ItemTreeItemDataType::ItemTreeItemStateType::Normal)));
661 ItemTreeItem.setIcon(2, QIcon(IconPath));
662 ItemTreeItem.setText(2, StateTitle);
663 ItemTreeItem.setForeground(2, QApplication::palette().text().color());
664 ItemTreeItem.setToolTip(2, Description);
665
667
668 // Issued to update also in case an item's thread terminates regularly
670}
671
672void DynExpManager::ChangeItemTreeItemToRunningState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
673 const QString& Description)
674{
675 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
677 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::Running));
678 ItemTreeItem.setText(2, "Running");
679 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("green"));
680 ItemTreeItem.setToolTip(2, Description);
681
683
684 // Issued to update also in case an item's thread is started automatically.
686}
687
688void DynExpManager::ChangeItemTreeItemToPausedState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
689 const QString& Description)
690{
691 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
692 QVariant::fromValue(ItemTreeItemDataType(ItemTreeItemData.ID, ItemTreeItemDataType::ItemTreeItemStateType::Paused)));
693 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::Paused));
694 ItemTreeItem.setText(2, "Paused");
695 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("orange"));
696 ItemTreeItem.setToolTip(2, Description);
697
699
700 // Issued to update also in case an item's thread is started automatically causing a paused situation immediately.
702}
703
704void DynExpManager::ChangeItemTreeItemToWarningState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
705 const QString& Description)
706{
707 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
709 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::Warning));
710 ItemTreeItem.setText(2, "Warning");
711 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("orange"));
712 ItemTreeItem.setToolTip(2, Description);
713
715
716 // Issued to update also in case an item's thread is started automatically causing a warning immediately.
718}
719
720void DynExpManager::ChangeItemTreeItemToErrorState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
721 const std::exception_ptr& ExceptionPtr)
722{
723 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
724 QVariant::fromValue(ItemTreeItemDataType(ItemTreeItemData.ID, ItemTreeItemDataType::ItemTreeItemStateType::Error)));
725 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::Error));
726 ItemTreeItem.setText(2, "Error");
727 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("red"));
728 ItemTreeItem.setToolTip(2, QString::fromStdString(Util::ExceptionToStr(ExceptionPtr)));
729
731
732 // Issued to update also in case an item's thread terminates due to an error.
734}
735
736void DynExpManager::ChangeItemTreeItemToNotConnectedState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
737 const QString& Description)
738{
739 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
741 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::NotReady));
742 ItemTreeItem.setText(2, "Connecting...");
743 ItemTreeItem.setForeground(2, QApplication::palette().text().color());
744 ItemTreeItem.setToolTip(2, Description);
745
747}
748
749void DynExpManager::ChangeItemTreeItemToNotRespondingState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
750 const QString& Description)
751{
752 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
754 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::NotResponding));
755 ItemTreeItem.setText(2, "Not responding");
756 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("orange"));
757 ItemTreeItem.setToolTip(2, Description);
758
760
761 // Issued to update also in case an item's thread is started automatically causing a warning immediately.
763}
764
765QColor DynExpManager::HTMLColorStringToThemeColor(const std::string& HTMLColor) const
766{
767 QString HTMLColorQ = QString::fromStdString(HTMLColor);
768
769 if (UIThemeActionGroup->checkedAction() == UIDarkThemeAction)
770 {
771 if (HTMLColorQ.toLower() == "blue")
773 if (HTMLColorQ.toLower() == "green")
775 }
776
777 return QColor(HTMLColorQ);
778}
779
780QColor DynExpManager::AdjustColorToThemeColor(const QColor& Color) const
781{
782 if (UIThemeActionGroup->checkedAction() == UIBrightThemeAction)
783 {
784 if (Color == DynExpUI::DarkPalette::blue)
785 return QColor("blue");
786 }
787 else if (UIThemeActionGroup->checkedAction() == UIDarkThemeAction)
788 {
789 if (Color == QColor("blue"))
791 }
792
793 return Color;
794}
795
796void DynExpManager::Reset(bool Force)
797{
798 IsResetting = true;
799
800 try
801 {
802 DynExpCore.Reset(Force);
803 }
804 catch (...)
805 {
806 IsResetting = false;
807
808 throw;
809 }
810
811 IsResetting = false;
813}
814
816{
818 if (QMessageBox::question(this, "DynExp - Close project?",
819 "Really close the current project? All running items will be stopped and unsaved changes will be lost.",
820 QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No)
821 != QMessageBox::StandardButton::Yes)
822 return false;
823
824 std::string ErrorMessage = "";
825 try
826 {
827 Reset();
828 }
829 catch (const Util::Exception& e)
830 {
831 Util::EventLog().Log(e);
832 ErrorMessage = e.what();
833 }
834 catch (const std::exception& e)
835 {
836 ErrorMessage = e.what();
837 }
838 catch (...)
839 {
840 ErrorMessage = "Unknown Error";
841 }
842
843 if (!ErrorMessage.empty())
844 {
845 auto Result = QMessageBox::critical(this, "DynExp - Critical error",
846 QString::fromStdString(
847 "Closing a project and stopping all running items, the following error occurred:\n\n"
848 + ErrorMessage + "\n\n- Click 'Retry' to try again to close the project."
849 + "\n- Click 'Ignore' to force the project to be closed. This may cause DynExp to be terminated without properly shutting down."
850 + "\n- Click 'Cancel' to return to the project without closing it."
851 ), QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::Retry | QMessageBox::StandardButton::Ignore,
852 QMessageBox::StandardButton::Cancel);
853
854 switch (Result)
855 {
856 case QMessageBox::StandardButton::Cancel: return false;
857 case QMessageBox::StandardButton::Ignore: return true;
858 default: return CloseProject();
859 }
860 }
861
863
864 return true;
865}
866
868{
869 std::string ErrorMessage = "";
870 try
871 {
873 }
874 catch (const Util::Exception& e)
875 {
876 Util::EventLog().Log(e);
877 ErrorMessage = e.what();
878 }
879 catch (const std::exception& e)
880 {
881 ErrorMessage = e.what();
882 }
883 catch (...)
884 {
885 ErrorMessage = "Unknown Error";
886 }
887
888 if (!ErrorMessage.empty())
889 {
890 auto Result = QMessageBox::critical(this, "DynExp - Critical error",
891 QString::fromStdString(
892 "Stopping a worker thread, the following error occurred:\n\n"
893 + ErrorMessage + "\n\n- Click 'Retry' to try again to stop the thread."
894 + "\n- Click 'Ignore' to force DynExp to be terminated without properly shutting down."
895 + "\n- Click 'Cancel' to return to DynExp without closing it."
896 ), QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::Retry | QMessageBox::StandardButton::Ignore,
897 QMessageBox::StandardButton::Cancel);
898
899 switch (Result)
900 {
901 case QMessageBox::StandardButton::Cancel: return false;
902 case QMessageBox::StandardButton::Ignore: std::terminate();
903 default: return Shutdown();
904 }
905 }
906
907 return true;
908}
909
910void DynExpManager::SaveProject(std::string_view Filename) noexcept
911{
912 std::string ErrorMessage = "";
913 try
914 {
915 DynExpCore.SaveProject(Filename, *this, *CircuitDiagramDlg, *ui.splitterInstrListMain, *ui.splitterMDILog);
916 }
917 catch (const Util::Exception& e)
918 {
919 Util::EventLog().Log(e);
920 ErrorMessage = e.what();
921 }
922 catch (const std::exception& e)
923 {
924 ErrorMessage = e.what();
925 }
926 catch (...)
927 {
928 ErrorMessage = "Unknown Error";
929 }
930
931 if (!ErrorMessage.empty())
932 {
933 QMessageBox::warning(this, "DynExp - Error",
934 QString::fromStdString(
935 std::string("Saving the project to file ").append(Filename)
936 + ", the following error occurred:\n\n" + ErrorMessage
937 ));
938 }
939}
940
942{
943 ui.action_Run_Item->setEnabled(false);
944 ui.action_Stop_Item->setEnabled(false);
945 ui.action_Force_Stop_Item->setEnabled(false);
946 ui.action_Reset_Item->setEnabled(false);
947 ui.action_Configure_Item->setEnabled(false);
948 ui.action_Delete_Item->setEnabled(false);
949
950 ClearWarningAction->setEnabled(false);
951}
952
954{
955 const auto NumModules = ModuleWindowsActionGroup->actions().size();
956
957 // Shortcuts Ctrl + 1 to Ctrl + 9 (Ctrl + 0 activates main window)
958 for (std::remove_const_t<decltype(NumModules)> i = 0; i < NumModules; ++i)
959 {
960 if (i < 9)
961 ModuleWindowsActionGroup->actions()[i]->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_1 + i));
962 else
963 ModuleWindowsActionGroup->actions()[i]->setShortcut(QKeySequence());
964 }
965}
966
968{
969 const auto& ModuleManager = DynExpCore.GetModuleManager();
970
971 for (auto ModuleIter = ModuleManager.cbegin(); ModuleIter != ModuleManager.cend(); ++ModuleIter)
972 {
973 try
974 {
975 auto Module = ModuleIter->second.ResourcePointer.get();
976 if (!Module)
977 continue;
978 if (!Module->IsRunning() || !Module->HasUI())
979 continue;
980
981 // Conversion should never fail. Throws if not.
982 auto& QModule = dynamic_cast<DynExp::QModuleBase&>(*Module);
984 }
985 catch (const Util::Exception& e)
986 {
987 Util::EventLog().Log("Enumerating active module windows, the error listed below occurred.", Util::ErrorType::Error);
988 Util::EventLog().Log(e);
989 }
990 catch (const std::exception& e)
991 {
992 Util::EventLog().Log("Enumerating active module windows, the error listed below occurred.", Util::ErrorType::Error);
993 Util::EventLog().Log(e.what());
994 }
995 catch (...)
996 {
997 Util::EventLog().Log("Enumerating active module windows, an unknown error occurred.", Util::ErrorType::Error);
998 }
999 }
1000}
1001
1003{
1004 const auto& ModuleManager = DynExpCore.GetModuleManager();
1005
1006 for (auto ModuleIter = ModuleManager.cbegin(); ModuleIter != ModuleManager.cend(); ++ModuleIter)
1007 {
1008 try
1009 {
1010 auto Module = ModuleIter->second.ResourcePointer.get();
1011 if (!Module)
1012 continue;
1013 if (!Module->IsRunning() || !Module->HasUI())
1014 continue;
1015
1016 // Conversion should never fail. Throws if not.
1017 auto& QModule = dynamic_cast<DynExp::QModuleBase&>(*Module);
1018 if (QModule.IsActiveWindow())
1019 return &QModule;
1020 }
1021 catch (const Util::Exception& e)
1022 {
1023 Util::EventLog().Log("Finding active module window, the error listed below occurred.", Util::ErrorType::Error);
1024 Util::EventLog().Log(e);
1025 }
1026 catch (const std::exception& e)
1027 {
1028 Util::EventLog().Log("Finding active module window, the error listed below occurred.", Util::ErrorType::Error);
1029 Util::EventLog().Log(e.what());
1030 }
1031 catch (...)
1032 {
1033 Util::EventLog().Log("Finding active module window, an unknown error occurred.", Util::ErrorType::Error);
1034 }
1035 }
1036
1037 return nullptr;
1038}
1039
1040void DynExpManager::closeEvent(QCloseEvent* event)
1041{
1042 UpdateUITimer->stop();
1043
1044 if (CloseProject() && Shutdown())
1045 {
1046 CircuitDiagramDlg->hide();
1047
1048 event->accept();
1049
1050 return;
1051 }
1052
1053 event->ignore();
1054 UpdateUITimer->start();
1055}
1056
1058{
1059 if (!Object)
1060 throw Util::InvalidArgException("Object cannot be nullptr.");
1061
1062 // If Object is a module which has an UI (a QModule), init and display its UI.
1063 auto Module = dynamic_cast<DynExp::QModuleBase*>(Object);
1064 if (!Module)
1065 return;
1066
1067 if (!Module->IsRunning())
1068 return;
1069
1070 try
1071 {
1072 auto const Action = &Module->InitUI(*this, ui.mdiMain);
1073 ModuleWindowsActionGroup->addAction(Action);
1075 ui.menu_Window->addAction(Action);
1076 }
1077 catch (...)
1078 {
1079 // In case of an error occurring setting up the module's UI, terminate the module.
1080 Module->Reset();
1081
1082 throw;
1083 }
1084}
1085
1086bool DynExpManager::StopItem(DynExp::RunnableObject* Object, bool Force) noexcept
1087{
1088 if (!Object)
1089 return false;
1090
1091 std::string ErrorMessage = "";
1092 std::string ObjectName = "< Unknown >";
1093 try
1094 {
1095 ObjectName = GetObjectNameSafe(Object);
1096 Object->Terminate(Force);
1097
1099
1100 return true;
1101 }
1103 {
1104 if (QMessageBox::warning(this, "DynExp - Force-terminate item?",
1105 QString("This runnable is currently being used by at least another item. Force-terminate the item?\n\n")
1106 + e.GetUserNames().data(),
1107 QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No)
1108 == QMessageBox::StandardButton::Yes)
1109 {
1110 return StopItem(Object, true);
1111 }
1112 }
1113 catch (const Util::Exception& e)
1114 {
1115 Util::EventLog().Log(e);
1116 ErrorMessage = e.what();
1117 }
1118 catch (const std::exception& e)
1119 {
1120 ErrorMessage = e.what();
1121 }
1122 catch (...)
1123 {
1124 ErrorMessage = "Unknown Error";
1125 }
1126
1127 if (!ErrorMessage.empty())
1128 {
1129 QMessageBox::warning(this, "DynExp - Error",
1130 QString::fromStdString(
1131 "Stopping the item " + ObjectName + ", the following error occurred:\n\n"
1132 + ErrorMessage
1133 ));
1134 }
1135
1137
1138 return false;
1139}
1140
1142{
1143 activateWindow();
1144 raise();
1145}
1146
1147void DynExpManager::PostKeyPressEvent(QKeyEvent* Event) noexcept
1148{
1149 // postEvent() will delete event after posting it into the event queue.
1150 QCoreApplication::postEvent(this, Event->clone());
1151}
1152
1154{
1155 // Necessary to prevent calling functions of destroyed objects while stack unwinding is in progress.
1156 if (std::uncaught_exceptions())
1157 return;
1158
1159 try
1160 {
1161 UpdateLog();
1164 UpdateStatusBar(); // Relies on item tree having been updated directly before.
1165 UpdateCircuitDiagram(); // Relies on item tree having been updated directly before.
1166 }
1167 catch (const Util::Exception& e)
1168 {
1169#ifdef DYNEXP_DEBUG
1170 Util::EventLog().Log("Updating the main window UI, the error listed below occurred.", Util::ErrorType::Error);
1171 Util::EventLog().Log(e);
1172#else
1174 {
1175 Util::EventLog().Log("Updating the main window UI, the error listed below occurred.", Util::ErrorType::Error);
1176 Util::EventLog().Log(e);
1177 }
1178#endif // DYNEXP_DEBUG
1179 }
1180 catch (const std::exception& e)
1181 {
1182 Util::EventLog().Log("Updating the main window UI, the error listed below occurred.", Util::ErrorType::Error);
1183 Util::EventLog().Log(e.what());
1184 }
1185 catch (...)
1186 {
1187 Util::EventLog().Log("Updating the main window UI, an unknown error occurred.", Util::ErrorType::Error);
1188 }
1189
1190 // The following functions do not throw.
1191 // ->
1193
1196 // <-
1197}
1198
1199void DynExpManager::OnUIThemeChanged(QAction* ThemeAction)
1200{
1201 if (ThemeAction == UIBrightThemeAction)
1202 {
1203 qApp->setPalette(QPalette());
1204 qApp->setStyleSheet("");
1205 }
1206 else if (ThemeAction == UIDarkThemeAction)
1207 {
1208 qApp->setPalette(DynExpUI::DarkPalette::GetPalette());
1209 qApp->setStyleSheet(QString::fromStdString(DynExpUI::DarkPalette::GetStyleSheet()));
1210 }
1211
1213 DynExpCore.GetHardwareAdapterManager().DeleteAllTreeWidgetItems();
1214 DynExpCore.GetInstrumentManager().DeleteAllTreeWidgetItems();
1215 DynExpCore.GetModuleManager().DeleteAllTreeWidgetItems();
1216
1218}
1219
1221{
1222 if (!CloseProject())
1223 return;
1224}
1225
1227{
1228 if (!CloseProject())
1229 return;
1230
1231 auto Filename = Util::PromptOpenFilePath(this, "Open project", ".dynp", "DynExp project files (*.dynp)").toStdString();
1232 if (Filename.empty())
1233 return;
1234
1235 std::string ErrorMessage = "";
1236 try
1237 {
1238 DynExpCore.OpenProject(Filename);
1239 }
1240 catch (const Util::Exception& e)
1241 {
1242 Util::EventLog().Log(e);
1243 ErrorMessage = e.what();
1244 }
1245 catch (const std::exception& e)
1246 {
1247 ErrorMessage = e.what();
1248 }
1249 catch (...)
1250 {
1251 ErrorMessage = "Unknown Error";
1252 }
1253
1254 if (!ErrorMessage.empty())
1255 {
1256 QMessageBox::warning(this, "DynExp - Error",
1257 QString::fromStdString(
1258 "Opening a project from file " + Filename + ", the following error occurred:\n\n"
1259 + ErrorMessage
1260 ));
1261
1262 try
1263 {
1264 // If this throws, something went terribly wrong. Terminate application.
1265 Reset(true);
1266 }
1267 catch (...)
1268 {
1269 Util::EventLog().Log("Error occurred while resetting project. Execution cannot continue.",
1271
1272 std::terminate();
1273 }
1274 }
1275 else
1276 {
1277 // First, only restore window states and sizes in order not to confuse the user
1278 // by windows moving away suddenly after some time of starting up the project...
1279 try
1280 {
1281 DynExpCore.RestoreWindowStatesFromParams(*this, *CircuitDiagramDlg, *ui.splitterInstrListMain, *ui.splitterMDILog, true);
1282 }
1283 catch (...)
1284 {
1285 // Swallow any exception here since we try again in a moment.
1286 }
1287
1288 // ...second, run the project and restore all the module windows' states (and again the o
1289 OnRunProject();
1291 }
1292}
1293
1301
1303{
1304 auto Filename = Util::PromptSaveFilePath(this, "Save project as", ".dynp", "DynExp project files (*.dynp)").toStdString();
1305 if (Filename.empty())
1306 return;
1307
1308 SaveProject(Filename);
1309}
1310
1311// Firstly, start hardware adapters. Then, wait until they are connected before starting
1312// instruments and modules.
1314{
1315 std::string ErrorMessage = "";
1316 try
1317 {
1318 // Connect all hardware adapters asynchronously
1319 auto ConnectingHardwareAdaptersFuture = DynExpCore.ConnectHardwareAdapters();
1320
1321 auto BusyDlg = std::make_unique<BusyDialog>(this);
1322 BusyDlg->SetDescriptionText("Connecting hardware adapters...");
1323 BusyDlg->SetCheckFinishedFunction(std::bind_front(&DynExp::DynExpCore::AllHardwareAdaptersConnected, &DynExpCore));
1324
1325 // BusyDlg->exec() blocks and starts new event loop for the modal dialog.
1326 auto Result = BusyDlg->exec();
1327 ConnectingHardwareAdaptersFuture.wait();
1328
1329 if (Result == QDialog::Accepted)
1330 {
1331 BusyDlg->SetDescriptionText("Starting instruments...");
1332 BusyDlg->SetCheckFinishedFunction(std::bind_front(&DynExp::DynExpCore::AllInstrumentsInitialized, &DynExpCore));
1333
1335 Result = BusyDlg->exec();
1336
1337 if (Result == QDialog::Accepted)
1338 DynExpCore.RunModules(std::bind_front(&DynExpManager::RegisterModuleUI, this));
1339 else if (BusyDlg->GetException())
1340 std::rethrow_exception(BusyDlg->GetException());
1341 }
1342 else if (BusyDlg->GetException())
1343 std::rethrow_exception(BusyDlg->GetException());
1344 }
1345 catch (const Util::Exception& e)
1346 {
1347 Util::EventLog().Log(e);
1348 ErrorMessage = e.what();
1349 }
1350 catch (const std::exception& e)
1351 {
1352 ErrorMessage = e.what();
1353 }
1354 catch (...)
1355 {
1356 ErrorMessage = "Unknown Error";
1357 }
1358
1359 if (!ErrorMessage.empty())
1360 {
1361 QMessageBox::warning(this, "DynExp - Error",
1362 QString::fromStdString(
1363 "Starting up the project, the following error occurred:\n\n"
1364 + ErrorMessage
1365 ));
1366 }
1367
1369}
1370
1372{
1373 std::string ErrorMessage = "";
1374 try
1375 {
1377 }
1378 catch (const Util::Exception& e)
1379 {
1380 Util::EventLog().Log(e);
1381 ErrorMessage = e.what();
1382 }
1383 catch (const std::exception& e)
1384 {
1385 ErrorMessage = e.what();
1386 }
1387 catch (...)
1388 {
1389 ErrorMessage = "Unknown Error";
1390 }
1391
1392 if (!ErrorMessage.empty())
1393 {
1394 QMessageBox::warning(this, "DynExp - Error",
1395 QString::fromStdString(
1396 "Shutting down the project, the following error occurred:\n\n"
1397 + ErrorMessage
1398 ));
1399 }
1400
1402}
1403
1405{
1406 std::string ErrorMessage = "";
1407 try
1408 {
1410 }
1411 catch (const Util::Exception& e)
1412 {
1413 Util::EventLog().Log(e);
1414 ErrorMessage = e.what();
1415 }
1416 catch (const std::exception& e)
1417 {
1418 ErrorMessage = e.what();
1419 }
1420 catch (...)
1421 {
1422 ErrorMessage = "Unknown Error";
1423 }
1424
1425 if (!ErrorMessage.empty())
1426 {
1427 QMessageBox::warning(this, "DynExp - Error",
1428 QString::fromStdString(
1429 "Resetting the project's failed items, the following error occurred:\n\n"
1430 + ErrorMessage
1431 ));
1432 }
1433
1435}
1436
1438{
1439 std::string ErrorMessage = "";
1440 try
1441 {
1442 DynExpCore.RestoreWindowStatesFromParams(*this, *CircuitDiagramDlg, *ui.splitterInstrListMain, *ui.splitterMDILog);
1443 }
1444 catch (const Util::Exception& e)
1445 {
1446 Util::EventLog().Log(e);
1447 ErrorMessage = e.what();
1448 }
1449 catch (const std::exception& e)
1450 {
1451 ErrorMessage = e.what();
1452 }
1453 catch (...)
1454 {
1455 ErrorMessage = "Unknown Error";
1456 }
1457
1458 if (!ErrorMessage.empty())
1459 {
1460 QMessageBox::warning(this, "DynExp - Error",
1461 QString::fromStdString(
1462 "Restoring the main and module windows' geometries and states from the project file, the following error occurred:\n\n"
1463 + ErrorMessage
1464 ));
1465 }
1466}
1467
1469{
1470 std::string ErrorMessage = "";
1471 try
1472 {
1474 }
1475 catch (const Util::Exception& e)
1476 {
1477 Util::EventLog().Log(e);
1478 ErrorMessage = e.what();
1479 }
1480 catch (const std::exception& e)
1481 {
1482 ErrorMessage = e.what();
1483 }
1484 catch (...)
1485 {
1486 ErrorMessage = "Unknown Error";
1487 }
1488
1489 if (!ErrorMessage.empty())
1490 {
1491 QMessageBox::warning(this, "DynExp - Error",
1492 QString::fromStdString(
1493 "Editing the project settings, the following error occurred:\n\n"
1494 + ErrorMessage
1495 ));
1496 }
1497}
1498
1500{
1501 if (ui.treeItems->selectedItems().length() != 1)
1502 return;
1503
1504 auto Item = ui.treeItems->selectedItems()[0];
1505 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1506
1507 std::string ErrorMessage = "";
1508 std::string ObjectName = "< Unknown >";
1509 try
1510 {
1511 DynExp::RunnableObject* Object = nullptr;
1512
1513 if (Item->parent() == ItemTreeInstruments)
1514 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1515 if (Item->parent() == ItemTreeModules)
1516 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1517
1518 if (Object)
1519 {
1520 ObjectName = GetObjectNameSafe(Object);
1521
1522 Object->Run(this);
1523 RegisterModuleUI(Object);
1524 }
1525 }
1526 catch (const Util::Exception& e)
1527 {
1528 Util::EventLog().Log(e);
1529 ErrorMessage = e.what();
1530 }
1531 catch (const std::exception& e)
1532 {
1533 ErrorMessage = e.what();
1534 }
1535 catch (...)
1536 {
1537 ErrorMessage = "Unknown Error";
1538 }
1539
1540 if (!ErrorMessage.empty())
1541 {
1542 QMessageBox::warning(this, "DynExp - Error",
1543 QString::fromStdString(
1544 "Starting the item " + ObjectName + ", the following error occurred:\n\n"
1545 + ErrorMessage
1546 ));
1547 }
1548
1550}
1551
1553{
1554 if (ui.treeItems->selectedItems().length() != 1)
1555 return;
1556
1557 auto Item = ui.treeItems->selectedItems()[0];
1558 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1559
1560 DynExp::RunnableObject* Object = nullptr;
1561 std::string ErrorMessage = "";
1562 try
1563 {
1564 if (Item->parent() == ItemTreeInstruments)
1565 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1566 if (Item->parent() == ItemTreeModules)
1567 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1568 }
1569 catch (const Util::Exception& e)
1570 {
1571 Util::EventLog().Log(e);
1572 ErrorMessage = e.what();
1573 }
1574 catch (const std::exception& e)
1575 {
1576 ErrorMessage = e.what();
1577 }
1578 catch (...)
1579 {
1580 ErrorMessage = "Unknown Error";
1581 }
1582
1583 if (!ErrorMessage.empty())
1584 {
1585 QMessageBox::warning(this, "DynExp - Error",
1586 QString::fromStdString(
1587 "Determining the object to be stopped, the following error occurred:\n\n"
1588 + ErrorMessage
1589 ));
1590 }
1591
1592 StopItem(Object, Force); // noexcept, checks whether Object is nullptr.
1593}
1594
1596{
1597 OnStopItem(true);
1598}
1599
1601{
1602 if (ui.treeItems->selectedItems().length() != 1)
1603 return;
1604
1605 auto Item = ui.treeItems->selectedItems()[0];
1606 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1607 DynExp::Object* Object = nullptr;
1608
1609 std::string ErrorMessage = "";
1610 try
1611 {
1612 if (Item->parent() == ItemTreeHardwareAdapters)
1613 Object = DynExpCore.GetHardwareAdapterManager().GetResource(ItemData.ID);
1614 if (Item->parent() == ItemTreeInstruments)
1615 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1616 if (Item->parent() == ItemTreeModules)
1617 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1618 }
1619 catch (const Util::Exception& e)
1620 {
1621 Util::EventLog().Log(e);
1622 ErrorMessage = e.what();
1623 }
1624 catch (const std::exception& e)
1625 {
1626 ErrorMessage = e.what();
1627 }
1628 catch (...)
1629 {
1630 ErrorMessage = "Unknown Error";
1631 }
1632
1633 if (!ErrorMessage.empty())
1634 {
1635 std::string ObjectName = GetObjectNameSafe(Object);
1636
1637 QMessageBox::warning(this, "DynExp - Error",
1638 QString::fromStdString(
1639 "Resetting the item " + ObjectName + ", the following error occurred:\n\n"
1640 + ErrorMessage
1641 ));
1642 }
1643 else
1644 ResetItem(Object); // ResetItem() itself checks if nullptr and handles exceptions.
1645
1647}
1648
1650{
1651 if (ui.treeItems->selectedItems().length() != 1)
1652 return;
1653
1654 auto Item = ui.treeItems->selectedItems()[0];
1655 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1656 DynExp::Object* Object = nullptr;
1657 DynExp::RunnableObject* RunnableObject = nullptr;
1658 bool ResetRequired = false;
1659
1660 std::string ErrorMessage = "";
1661 try
1662 {
1663 if (Item->parent() == ItemTreeHardwareAdapters)
1664 Object = DynExpCore.GetHardwareAdapterManager().GetResource(ItemData.ID);
1665 if (Item->parent() == ItemTreeInstruments)
1666 {
1667 RunnableObject = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1668 Object = RunnableObject;
1669 }
1670 if (Item->parent() == ItemTreeModules)
1671 {
1672 RunnableObject = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1673 Object = RunnableObject;
1674 }
1675
1676 if (!Object)
1677 return;
1678
1679 if (Item->parent() == ItemTreeHardwareAdapters)
1681 if (Item->parent() == ItemTreeInstruments)
1683 if (Item->parent() == ItemTreeModules)
1685 }
1686 catch (const Util::Exception& e)
1687 {
1688 Util::EventLog().Log(e);
1689 ErrorMessage = e.what();
1690 }
1691 catch (const std::exception& e)
1692 {
1693 ErrorMessage = e.what();
1694 }
1695 catch (...)
1696 {
1697 ErrorMessage = "Unknown Error";
1698 }
1699
1700 std::string ObjectName = GetObjectNameSafe(Object);
1701
1702 if (!ErrorMessage.empty())
1703 QMessageBox::warning(this, "DynExp - Error",
1704 QString::fromStdString(
1705 "Configuring the item " + ObjectName + ", the following error occurred:\n\n"
1706 + ErrorMessage
1707 ));
1708 else
1709 {
1710 if (ResetRequired)
1711 {
1712 // Only ask whether to reset item for hardware adapters and running instruments/modules.
1713 if ((Object && !RunnableObject) /* hardware adapter */ || (RunnableObject && RunnableObject->IsRunning()) /* instrument or module */)
1714 {
1715 if (QMessageBox::question(this, "DynExp - Reset item?",
1716 QString::fromStdString("The item " + ObjectName + " needs to be reset in order to apply the changes. Should it be reset now?"),
1717 QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No)
1718 == QMessageBox::StandardButton::Yes)
1719 ResetItem(Object); // See below.
1720 }
1721 else
1722 ResetItem(Object); // ResetItem() itself checks if nullptr and handles exceptions.
1723 }
1724 }
1725
1727}
1728
1730{
1731 if (ui.treeItems->selectedItems().length() != 1)
1732 return;
1733
1734 auto Item = ui.treeItems->selectedItems()[0];
1735 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1736
1737 std::string ErrorMessage = "";
1738 std::string ObjectName = "< Unknown >";
1739 try
1740 {
1741 if (Item->parent() == ItemTreeHardwareAdapters)
1742 {
1743 auto Object = DynExpCore.GetHardwareAdapterManager().GetResource(ItemData.ID);
1744
1745 if (Object)
1746 {
1747 ObjectName = GetObjectNameSafe(Object);
1748 DynExpCore.GetHardwareAdapterManager().RemoveResource(ItemData.ID);
1749 }
1750 }
1751 else
1752 {
1753 DynExp::RunnableObject* Object = nullptr;
1754
1755 if (Item->parent() == ItemTreeInstruments)
1756 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1757 if (Item->parent() == ItemTreeModules)
1758 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1759
1760 if (Object)
1761 {
1762 ObjectName = GetObjectNameSafe(Object);
1763
1764 if (Object->IsRunning())
1765 {
1766 if (QMessageBox::question(this, "DynExp - Stop item?",
1767 QString::fromStdString("The item " + ObjectName + " is currently running. Should it be stopped in order to be deleted?"),
1768 QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No)
1769 != QMessageBox::StandardButton::Yes)
1770 return;
1771
1772 Object->Terminate();
1773 }
1774
1775 if (Item->parent() == ItemTreeInstruments)
1776 DynExpCore.GetInstrumentManager().RemoveResource(ItemData.ID);
1777 if (Item->parent() == ItemTreeModules)
1778 DynExpCore.GetModuleManager().RemoveResource(ItemData.ID);
1779 }
1780 }
1781 }
1783 {
1784 ErrorMessage = e.GetErrorMessage();
1785 }
1786 catch (const Util::Exception& e)
1787 {
1788 Util::EventLog().Log(e);
1789 ErrorMessage = e.what();
1790 }
1791 catch (const std::exception& e)
1792 {
1793 ErrorMessage = e.what();
1794 }
1795 catch (...)
1796 {
1797 ErrorMessage = "Unknown Error";
1798 }
1799
1801
1802 if (!ErrorMessage.empty())
1803 {
1804 QMessageBox::warning(this, "DynExp - Error",
1805 QString::fromStdString(
1806 "Deleting the item " + ObjectName + ", the following error occurred:\n\n"
1807 + ErrorMessage
1808 ));
1809 }
1810
1812}
1813
1815{
1816 ui.action_Dock_Undock_Window->setEnabled(GetModuleByActiveUIWindow() != nullptr);
1817 ui.action_Dummy_NoWindow->setVisible(ModuleWindowsActionGroup->actions().empty());
1819}
1820
1822{
1823 // To listen on shortcut.
1824 ui.action_Dock_Undock_Window->setEnabled(true);
1825}
1826
1828{
1829 auto const QModule = GetModuleByActiveUIWindow();
1830
1831 if (QModule)
1832 QModule->DockUndockWindow();
1833}
1834
1840
1842{
1843 AboutDialog->exec();
1844}
1845
1847{
1848 // Avoid direct reopening after close (refer to ErrorListDialog::focusOutEvent()).
1850 {
1851 ErrorListDlg->show();
1852 ErrorListDlg->resize(StatusBar.State->width() - 4, 250);
1853 ErrorListDlg->move(StatusBar.State->mapToGlobal(StatusBar.State->pos()) - QPoint(0, 6 + ErrorListDlg->height()));
1854 ErrorListDlg->setFocus();
1855 }
1856 else
1858}
1859
1861{
1862 LogContextMenu->exec(ui.tableLog->mapToGlobal(Position));
1863}
1864
1866{
1867 ItemTreeContextMenu->exec(ui.treeItems->mapToGlobal(Position));
1868}
1869
1871{
1872 std::string ErrorMessage = "";
1873 try
1874 {
1876 ui.tableLog->setRowCount(0);
1877 }
1878 catch (const Util::Exception& e)
1879 {
1880 Util::EventLog().Log(e);
1881 ErrorMessage = e.what();
1882 }
1883 catch (const std::exception& e)
1884 {
1885 ErrorMessage = e.what();
1886 }
1887 catch (...)
1888 {
1889 ErrorMessage = "Unknown Error";
1890 }
1891
1892 if (!ErrorMessage.empty())
1893 {
1894 QMessageBox::warning(this, "DynExp - Error",
1895 QString::fromStdString(
1896 "Clearing the event log, the following error occurred:\n\n"
1897 + ErrorMessage
1898 ));
1899 }
1900}
1901
1903{
1904 if (ui.treeItems->selectedItems().length() != 1)
1905 return;
1906
1907 auto Item = ui.treeItems->selectedItems()[0];
1908 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1909 DynExp::Object* Object = nullptr;
1910
1911 try
1912 {
1913 if (Item->parent() == ItemTreeHardwareAdapters)
1914 Object = DynExpCore.GetHardwareAdapterManager().GetResource(ItemData.ID);
1915 if (Item->parent() == ItemTreeInstruments)
1916 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1917 if (Item->parent() == ItemTreeModules)
1918 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1919
1920 if (!Object)
1921 return;
1922
1923 Object->ClearWarning();
1924 }
1925 catch (...)
1926 {
1927 return;
1928 }
1929}
1930
1935
1937{
1938 MakeItem(qobject_cast<QAction*>(sender()), DynExpCore.GetInstrumentLib(), DynExpCore.GetInstrumentManager());
1939}
1940
1942{
1943 MakeItem(qobject_cast<QAction*>(sender()), DynExpCore.GetModuleLib(), DynExpCore.GetModuleManager());
1944}
1945
1947{
1948 if (IsResetting)
1949 return;
1950
1951 if (ui.treeItems->selectedItems().length() != 1)
1952 {
1954 return;
1955 }
1956
1957 auto Item = ui.treeItems->selectedItems()[0];
1958
1959 if (Item->parent() == ItemTreeHardwareAdapters || Item->parent() == ItemTreeInstruments ||
1960 Item->parent() == ItemTreeModules)
1961 {
1962 ui.action_Reset_Item->setEnabled(true);
1963 ui.action_Configure_Item->setEnabled(true);
1964 ui.action_Delete_Item->setEnabled(true);
1965
1966 ClearWarningAction->setEnabled(true);
1967 }
1968
1969 if (Item->parent() == ItemTreeHardwareAdapters)
1970 {
1971 ui.action_Run_Item->setEnabled(false);
1972 ui.action_Stop_Item->setEnabled(false);
1973 ui.action_Force_Stop_Item->setEnabled(false);
1974
1975 return;
1976 }
1977
1978 if (Item->parent() == ItemTreeInstruments || Item->parent() == ItemTreeModules)
1979 {
1980 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1981
1982 bool IsRunning = false;
1983 try
1984 {
1985 if (Item->parent() == ItemTreeInstruments)
1986 IsRunning = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID)->IsRunning();
1987 if (Item->parent() == ItemTreeModules)
1988 IsRunning = DynExpCore.GetModuleManager().GetResource(ItemData.ID)->IsRunning();
1989 }
1990 catch (...)
1991 {
1992 // Swallow especially Util::NotFoundException.
1993 }
1994
1995 ui.action_Run_Item->setEnabled(!IsRunning);
1996 ui.action_Stop_Item->setEnabled(IsRunning);
1997 ui.action_Force_Stop_Item->setEnabled(IsRunning);
1998
1999 return;
2000 }
2001
2003}
2004
2005void DynExpManager::OnItemDoubleClicked(QTreeWidgetItem* Item, int Column)
2006{
2008}
2009
2011{
2012 if (IsResetting)
2013 return;
2014
2015 auto ActiveQModule = GetModuleByActiveUIWindow();
2016
2017 if (ActiveQModule)
2018 {
2019 ui.treeItems->clearSelection();
2020 DynExpCore.GetModuleManager().FocusTreeWidgetItem(ActiveQModule->GetID());
2021 }
2022}
Implements DynExp's main UI window called DynExpManager.
Implements DynExp's main window as a Qt-based user interface (UI).
void SelectItemTreeItem(QTreeWidgetItem *SelectedEntry)
Selects the given QTreeWidgetItem instance and brings this DynExpManager window to the front.
DynExpAbout * AboutDialog
Dialog showing license and copyright information.
void ChangeItemTreeItemToRunningState(QTreeWidgetItem &ItemTreeItem, const ItemTreeItemDataType &ItemTreeItemData, const QString &Description)
void OnProjectSettingsClicked()
QColor HTMLColorStringToThemeColor(const std::string &HTMLColor) const
Constructs a QColor instance from an HTML color string depending on the currently active UI theme.
void Reset(bool Force=false)
Resets the currently loaded project removing all resources from the resource managers....
void UpdateCircuitDiagram()
void OnAddHardwareAdapter()
void OnStatusBarStateClicked()
void OnItemDoubleClicked(QTreeWidgetItem *Item, int Column)
QMenu * ItemTreeContextMenu
Context menu appearing when right-clicking on the main tree widget showing DynExp::Object instances.
ErrorListDialog * ErrorListDlg
Dialog showing the list of currently active warnings and errors (ErrorEntries)
void MakeItem(QAction *SenderAction, LibraryVectorT &Lib, ManagerT &ResourceManager)
Creates a DynExp::Object instance according to a QAction instance SenderAction, which contains the in...
DynExp::DynExpCore & DynExpCore
Handle to DynExp's internal data.
QTreeWidgetItem * ItemTreeHardwareAdapters
Item for the hardware adapters section within the main tree widget showing DynExp::Object instances.
QColor AdjustColorToThemeColor(const QColor &Color) const
Transforms a QColor instance depending on the currently active UI theme.
void OnItemSelectionChanged()
bool Shutdown() noexcept
Terminates DynExpCore::WorkerThread and waits until the thread has ended.
static std::string GetObjectNameSafe(DynExp::Object *Object)
Retrieves the name of a DynExp::Object instance from its parameter class instance.
void OnStopItem(bool Force=false)
void ChangeItemTreeItemToNotRespondingState(QTreeWidgetItem &ItemTreeItem, const ItemTreeItemDataType &ItemTreeItemData, const QString &Description)
void ResetItem(DynExp::Object *Object)
Calls DynExp::Object::Reset() on a DynExp::Object instance.
void OnRestoreWindowStatesFromParams()
void RegisterItemsFromLibrary(const LibraryVectorT &Lib, QMenu *const MenuBase, const QString IconPath, void(DynExpManager::*Slot)())
Adds the library entries from Lib to submenus below MenuBase and assigns actions to them invoking Slo...
void UpdateModuleWindowsActionShortcuts() noexcept
Establishes shortcuts to switch between module windows. Refer to ModuleWindowsActionGroup.
QTreeWidgetItem * ItemTreeModules
Item for the modules section within the main tree widget showing DynExp::Object instances.
virtual void closeEvent(QCloseEvent *event) override
bool UpdateItemConfig(DynExp::Object *Object, LibraryVectorT &Lib, ManagerT &ResourceManager)
Invokes a configuration dialog to let the user adjust the parameters of a DynExp::Object instance.
void OnLogContextMenuRequested(const QPoint &Position)
void ChangeItemTreeItemToNotConnectedState(QTreeWidgetItem &ItemTreeItem, const ItemTreeItemDataType &ItemTreeItemData, const QString &Description)
void OnModuleWindowActivated(QMdiSubWindow *Window)
void PostKeyPressEvent(QKeyEvent *Event) noexcept
Delegates a Qt key press event (e.g. the key sequences Ctrl+1 - Ctrl+9) from module widgets to the ma...
void FocusMainWindow() noexcept
Focuses/activates DynExp's main window and moves it on top of other windows if possible.
void DisableAllActions() noexcept
Disables all user interface actions such that item-specific ones can be reenabled upon a selection ch...
void OnShowCircuitDiagram()
struct DynExpManager::StatusBarType StatusBar
DynExp::QModuleBase * GetModuleByActiveUIWindow() noexcept
Determines the DynExp::QModuleBase instance of the current project whose UI window is active (has the...
void UpdateItemTreeItemObjectName(QTreeWidgetItem *Item, const DynExp::Object *Object)
Updates the text of a QTreeWidgetItem instance to match it to the related DynExp::Object instance.
Ui::DynExpManagerClass ui
Qt widgets belonging to the main window's user interface.
QMenu * LogContextMenu
Context menu appearing when right-clicking on the message log widget.
void UpdateModuleWindowsActionIcons() noexcept
Invokes DynExp::QModuleBase::UpdateModuleWindowFocusAction() on each module.
QActionGroup * UIThemeActionGroup
Actions to switch in between different UI themes.
std::unique_ptr< CircuitDiagram > CircuitDiagramDlg
Dialog showing the circuit diagram of the current project. It has no parent to not stay on top of the...
bool IsResetting
Indicates whether DynExpManager is currently deleting all resources to empty the project.
QActionGroup * ModuleWindowsActionGroup
Actions to switch between module windows with CTRL + < number key > shortcuts.
QAction * UIBrightThemeAction
Action for a bright UI theme.
void ChangeItemTreeItemToPausedState(QTreeWidgetItem &ItemTreeItem, const ItemTreeItemDataType &ItemTreeItemData, const QString &Description)
QTreeWidgetItem * ItemTreeInstruments
Item for the instruments section within the main tree widget showing DynExp::Object instances.
void UpdateModulesUI() noexcept
void OnUIThemeChanged(QAction *ThemeAction)
void UpdateItemTreeItem(const DynExp::HardwareAdapterManager::ResourceType &Resource)
void RegisterModuleUI(DynExp::Object *const Object)
Initializes the UI of the module Object (expecting that Object is a DynExp::QModuleBase instance....
void SaveProject(std::string_view Filename) noexcept
Saves the current DynExp project to an XML project file.
QAction * ClearWarningAction
Action to clear the warning of an item within the main tree widget showing DynExp::Object instances.
QTreeWidgetItem * UpdateItemTreeSection(QTreeWidgetItem *Section, ManagerT &ResourceManager)
Loops through resources managed by ResourceManager and adds respective QTreeWidgetItem instances as c...
void ChangeItemTreeItemToErrorState(QTreeWidgetItem &ItemTreeItem, const ItemTreeItemDataType &ItemTreeItemData, const std::exception_ptr &ExceptionPtr)
bool StopItem(DynExp::RunnableObject *Object, bool Force=false) noexcept
Calls DynExp::RunnableObject::Terminate() on a DynExp::RunnableObject instance.
void ChangeItemTreeItemToNormalState(QTreeWidgetItem &ItemTreeItem, const ItemTreeItemDataType &ItemTreeItemData, const QString &Description, const QString &StateTitle, const char *IconPath=DynExpUI::Icons::Stopped)
bool ShouldRedrawCircuitDiagram
Indicates whether the circuit diagram has to be rebuilt. Particularly, this must be set to true direc...
void EnsureItemReadyState(DynExp::Object *Object)
Calls DynExp::Object::EnsureReadyState() on a DynExp::Object instance.
void OnItemTreeContextMenuRequested(const QPoint &Position)
void ChangeItemTreeItemToWarningState(QTreeWidgetItem &ItemTreeItem, const ItemTreeItemDataType &ItemTreeItemData, const QString &Description)
ErrorListDialog::ErrorEntriesType ErrorEntries
List of all currently active warnings and errors.
bool CloseProject() noexcept
Closes the current project and opens a new, empty one.
DynExpManager(DynExp::DynExpCore &DynExpCore, QWidget *parent=Q_NULLPTR)
Constructs a DynExpManager instance.
bool ShouldUpdateCircuitDiagram
Indicates whether any item state has changed. Set to true to update the circuit diagram....
QAction * UIDarkThemeAction
Action for a dark UI theme.
QTimer * UpdateUITimer
Timer for periodically updating the user interface.
DynExp's core class acts as the interface between the user interface and DynExp's internal data like ...
Definition DynExpCore.h:127
void SaveProject(std::string_view Filename, const QMainWindow &MainWindow, const QDialog &CircuitDiagramDlg, QSplitter &HSplitter, QSplitter &VSplitter)
Saves the current DynExp project to an XML project file.
auto & GetHardwareAdapterLib() const noexcept
Getter for the hardware adapter library.
Definition DynExpCore.h:263
auto ConnectHardwareAdapters(CommonResourceManagerBase::FunctionToCallWhenObjectStartedType FunctionToCallWhenHardwareAdapterConnecting=nullptr)
Connects all hardware adapters contained in HardwareAdapterMgr asynchronously calling ResourceManager...
Definition DynExpCore.h:204
void ShutdownProject()
Terminates all running instruments and modules.
bool AllInstrumentsInitialized() const
Checks whether all instruments contained in InstrumentMgr have been initialized successfully.
auto & GetInstrumentLib() const noexcept
Getter for the instrument library.
Definition DynExpCore.h:269
void OpenProject(std::string_view Filename)
Loads a DynExp project from an XML project file.
auto & GetInstrumentManager() noexcept
Getter for the instrument manager.
Definition DynExpCore.h:287
bool IsProjectOpened(const std::chrono::milliseconds Timeout=GetParamsTimeoutDefault) const noexcept
Locks Params and determines whether a project has been openend from a project file.
Definition DynExpCore.h:354
auto & GetModuleManager() noexcept
Getter for the module manager.
Definition DynExpCore.h:293
void EditProjectSettings(QWidget *const DialogParent)
Opens a settings dialog (ParamsConfigDialog) to let the user configure the parameter values of the cu...
void RestoreWindowStatesFromParams(QMainWindow &MainWindow, QDialog &CircuitDiagramDlg, QSplitter &HSplitter, QSplitter &VSplitter, bool OnlyMainWindow=false)
Sets module and main windows' positions, sizes and styles according to parameters stored in the curre...
void Reset(bool Force=false)
Resets the currently loaded project removing all resources from the resource managers....
bool HasLoadedProjectFromCommandlineParams() noexcept
Indicates whether a DynExp project has been loaded from a path specified as a command line argument w...
auto & GetHardwareAdapterManager() noexcept
Getter for the hardware adapter manager.
Definition DynExpCore.h:281
auto GetProjectFilename(const std::chrono::milliseconds Timeout=GetParamsTimeoutDefault) const
Locks Params and returns the current DynExp project's filename.
Definition DynExpCore.h:340
void RunModules(CommonResourceManagerBase::FunctionToCallWhenObjectStartedType FunctionToCallWhenModuleStarted=nullptr)
Runs all modules contained in ModuleMgr with RunnableObjectParams::StartupType::OnCreation startup se...
void RunInstruments(CommonResourceManagerBase::FunctionToCallWhenObjectStartedType FunctionToCallWhenInstrumentStarted=nullptr)
Runs all instruments contained in InstrumentMgr with RunnableObjectParams::StartupType::OnCreation st...
bool AllHardwareAdaptersConnected() const
Checks whether all hardware adapters contained in HardwareAdapterMgr have been connected successfully...
void ResetFailedItems(QWidget &ParentWindow)
Calls Object::Reset() and Object::ClearWarning() on all owned DynExp::Object instances which are in a...
void Shutdown()
Terminates DynExpCore::WorkerThread and waits until the thread has ended.
auto & GetModuleLib() const noexcept
Getter for the module library.
Definition DynExpCore.h:275
Base class for all DynExp Objects like hardware adapters (DynExp::HardwareAdapterBase),...
Definition Object.h:1971
void Reset()
Resets this Object instance (including all its derived classes) by calling ResetImpl()....
Definition Object.cpp:403
void EnsureReadyState(bool IsAutomaticStartup)
Ensures that this Object instance is ready by possibly starting its worker thread or by opening conne...
Definition Object.cpp:446
auto GetObjectName(const std::chrono::milliseconds Timeout=GetParamsTimeoutDefault) const
Returns the name of this Object instance.
Definition Object.h:2128
Base class for modules with a Qt-based user interface. Derive from this class to implement modules wi...
Definition Module.h:1344
void HideUI()
Removes MdiSubWindow, DockWidget, and ModuleWindowFocusAction setting them to nullptr.
Definition Module.cpp:604
void UpdateModuleWindowFocusAction()
Updates the icon assigned to ModuleWindowFocusAction depending on whether Widget is docked to or undo...
Definition Module.cpp:629
Exception type thrown by TerminateImpl() if the RunnableObject cannot be terminated for being used by...
Definition Object.h:2454
std::string_view GetUserNames() const
Getter for UserNames.
Definition Object.h:2467
std::string GetErrorMessage() const
Genereates a user-readable error message containing the content of UserNames.
Definition Object.cpp:512
Defines an Object which possesses a thread it runs in. The RunnableObject can be started and stopped ...
Definition Object.h:2426
bool Run(QWidget *ParentWidget=nullptr)
Starts the RunnableObject instance's thread and ensures that all Object instances linked to this inst...
Definition Object.cpp:550
bool IsRunning() const noexcept
Returns Running.
Definition Object.h:2550
void Terminate(bool Force=false, const std::chrono::milliseconds Timeout=TerminateTimeoutDefault)
Notifies the RunnableObject instance's thread to terminate and waits until it has ended....
Definition Object.cpp:617
QTreeWidgetItem * GetSelectedEntry()
void SetErrorEntries(const ErrorEntriesType &ErrorEntries)
void ResetClosedByClickingOpenWidget() noexcept
bool HasBeenClosedByClickingOpenWidget() const noexcept
void Log(const std::string &Message, const ErrorType Type=ErrorType::Info, const size_t Line=0, const std::string &Function="", const std::string &File="", const int ErrorCode=0, const std::stacktrace &Trace={}) noexcept
Logs an event from information specified manually.
Definition Util.cpp:309
auto GetLogSize() const
Determines the number of entries in the internal event log.
Definition Util.h:1188
void ClearLog()
Clears the internal event log.
Definition Util.h:1174
std::vector< LogEntry > GetLog(size_t FirstElement=0) const
Returns the internal event log starting from the n-th stored element.
Definition Util.cpp:486
DynExp exceptions are derived from this class. It contains basic information about the cause of the e...
Definition Exception.h:51
constexpr const char * GetErrorLabelColor() const
Definition Exception.h:99
const ErrorType Type
DynExp error type from Util::ErrorType
Definition Exception.h:105
constexpr const char * GetErrorLabel() const
Definition Exception.h:98
An invalid argument like a null pointer has been passed to a function.
Definition Exception.h:137
Thrown when an operation timed out before it could be completed, especially used for locking shared d...
Definition Exception.h:261
const QColor green("lime")
const QColor blue(42, 130, 218)
constexpr auto Warning
constexpr auto Instrument
constexpr auto NotReady
constexpr auto HardwareAdapter
constexpr auto Paused
constexpr auto Module
constexpr auto NotResponding
constexpr auto Running
static const auto PushButtonReadyStyleSheetBright
static const auto PushButtonRunningStyleSheet
static const auto PushButtonErrorStyleSheet
static const auto PushButtonReadyStyleSheetDark
static const auto PushButtonWarningStyleSheet
std::string ToStr(const T &Value, int Precision=-1)
Converts a (numeric) value of type T to a std::string using operator<< of std::stringstream.
Definition Util.h:625
std::string ExceptionToStr(const std::exception_ptr ExceptionPtr)
Returns the what() information of an exception derived from std::exception and stored in an exception...
Definition Util.cpp:189
EventLogger & EventLog()
This function holds a static EventLogger instance and returns a reference to it. DynExp uses only one...
Definition Util.cpp:509
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
void ActivateWindow(QWidget &Widget)
Renders a window active and brings it to the front.
Definition QtUtil.cpp:229
QString PromptSaveFilePath(QWidget *Parent, const QString &Title, const QString &DefaultSuffix, const QString &NameFilter, const QString &InitialDirectory)
Works as PromptOpenFilePath() but asks the user to select a single file which does not need to exist.
Definition QtUtil.cpp:128
Accumulates include statements to provide a precompiled header.
Bundles data which is assigned to items of the main QTreeWidget. Each item refers to a DynExp::Object...
const DynExp::ItemIDType ID
ID of the DynExp::Object instance assigned to this item.
StatusBarType(DynExpManager *Owner)
Constructs a StatusBarType instance.
Defines a DynExp resource, which mainly owns a DynExp::Object instance wrapping a pointer to it.
Definition Managers.h:24
std::unique_ptr< QTreeWidgetItem > TreeWidgetItem
For visualization of the resource and its state in DynExp's main window (DynExpManager)....
Definition Managers.h:43
PointerType ResourcePointer
Pointer to the DynExp::Object instance owned by this resource.
Definition Managers.h:36