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 "ui_DynExpManager.h"
6#include "DynExpManager.h"
7
9 : QMainWindow(parent), DynExpCore(DynExpCore),
10 ui(std::make_unique<Ui::DynExpManagerClass>()),
11 UpdateUITimer(new QTimer(this)),
12 AboutDialog(new DynExpAbout(this)), CircuitDiagramDlg(std::make_unique<CircuitDiagram>(nullptr)),
13 ModuleWindowsActionGroup(new QActionGroup(this)),
14 UIThemeActionGroup(new QActionGroup(this)), UIBrightThemeAction(nullptr), UIDarkThemeAction(nullptr),
15 LogContextMenu(new QMenu(this)),
16 ItemTreeContextMenu(new QMenu(this)), ClearWarningAction(nullptr),
17 ItemTreeHardwareAdapters(nullptr), ItemTreeInstruments(nullptr), ItemTreeModules(nullptr),
18 StatusBar(this), IsResetting(false), ShouldRedrawCircuitDiagram(true), ShouldUpdateCircuitDiagram(false)
19{
20 qApp->setStyle(QStyleFactory::create("Fusion"));
21
22 ui->setupUi(this);
23
24 // Item Libraries
25 RegisterItemsFromLibrary(DynExpCore.GetHardwareAdapterLib(), ui->menu_Add_Hardware_Adapter,
31
32 // Window menu
33 connect(ui->menu_Window, &QMenu::aboutToShow, this, &DynExpManager::OnWindowMenuOpened);
34 connect(ui->menu_Window, &QMenu::aboutToHide, this, &DynExpManager::OnWindowMenuClosed);
35
36 // Theme menu
37 UIBrightThemeAction = UIThemeActionGroup->addAction("&Bright");
38 UIBrightThemeAction->setCheckable(true);
39 UIBrightThemeAction->setChecked(true);
40 UIDarkThemeAction = UIThemeActionGroup->addAction("&Dark");
41 UIDarkThemeAction->setCheckable(true);
42 ui->menu_UI_Theme->addActions(UIThemeActionGroup->actions());
43 connect(UIThemeActionGroup, &QActionGroup::triggered, this, &DynExpManager::OnUIThemeChanged);
44
45 // Log table
46 ui->tableLog->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
47 ui->tableLog->verticalHeader()->setMinimumSectionSize(18);
48 ui->tableLog->verticalHeader()->setDefaultSectionSize(ui->tableLog->verticalHeader()->minimumSectionSize());
49 ui->tableLog->setColumnWidth(0, 140);
50
51 // Log table context menu
52 LogContextMenu->addAction("&Clear Log", this, &DynExpManager::OnClearLog);
53
54 // Status bar
55 ui->statusBarMain->addWidget(StatusBar.State, 8);
56 ui->statusBarMain->addPermanentWidget(StatusBar.NumRunningInstrGroup, 1);
57 ui->statusBarMain->addPermanentWidget(StatusBar.NumRunningModuleGroup, 1);
58 connect(StatusBar.State, &QPushButton::clicked, this, &DynExpManager::OnStatusBarStateClicked);
59
60 // Error list dialog
62
63 // Item tree
64 ItemTreeHardwareAdapters = new QTreeWidgetItem(ui->treeItems, { "Hardware Adapters", "", "" });
66 ItemTreeHardwareAdapters->setFirstColumnSpanned(true);
67 ItemTreeInstruments = new QTreeWidgetItem(ui->treeItems, { "Instruments", "", "" });
69 ItemTreeInstruments->setFirstColumnSpanned(true);
70 ItemTreeModules = new QTreeWidgetItem(ui->treeItems, { "Modules", "", "" });
71 ItemTreeModules->setIcon(0, QIcon(DynExpUI::Icons::Module));
72 ItemTreeModules->setFirstColumnSpanned(true);
73 ui->treeItems->header()->resizeSection(0, 120);
74 ui->treeItems->header()->resizeSection(1, 180);
75 ui->treeItems->header()->resizeSection(2, 100);
76 ui->splitterInstrListMain->setStretchFactor(0, 16);
77 ui->splitterInstrListMain->setStretchFactor(1, 5);
78
79 // Item tree context menu
80 ClearWarningAction = ItemTreeContextMenu->addAction("Clear &Warning", this, &DynExpManager::OnClearWarning);
81
82 // MDI area
83 connect(ui->mdiMain, &QMdiArea::subWindowActivated, this, &DynExpManager::OnModuleWindowActivated);
84
86
87 // If a project has been loaded due to a command line option, run it now.
90
91 // Start UI callback timer (63 fps UI update frequency should enable fluent motion)
92 connect(UpdateUITimer, &QTimer::timeout, this, &DynExpManager::OnUpdateUI);
93 UpdateUITimer->setInterval(std::chrono::milliseconds(16));
94 UpdateUITimer->start();
95}
96
100
102 : NumItemsInWarningState(0), NumItemsInErrorState(0),
103 State(new QPushButton(Owner)),
104 NumRunningInstrGroup(new QWidget(Owner)), NumRunningInstrLayout(new QHBoxLayout),
105 NumRunningInstrImage(new QLabel(Owner)), NumRunningInstr(new QLabel(Owner)),
106 NumRunningModuleGroup(new QWidget(Owner)), NumRunningModuleLayout(new QHBoxLayout),
107 NumRunningModuleImage(new QLabel(Owner)), NumRunningModule(new QLabel(Owner))
108{
109 constexpr int Height = 16;
110
111 State->setFlat(true);
112 State->setMinimumHeight(24);
113 State->setFocusPolicy(Qt::FocusPolicy::NoFocus);
114 State->setStyleSheet(QString::fromStdString(DynExpUI::PushButtonReadyStyleSheetBright));
115
116 NumRunningInstrImage->setFixedHeight(Height);
117 NumRunningInstrImage->setSizePolicy({ QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum });
118 NumRunningInstrImage->setToolTip("Running instruments");
119 NumRunningInstrImage->setPixmap(QPixmap(DynExpUI::Icons::Instrument).scaledToHeight(Height));
120 NumRunningInstr->setSizePolicy({ QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding });
121 NumRunningInstr->setToolTip("Running instruments");
122
123 NumRunningModuleImage->setFixedHeight(Height);
124 NumRunningModuleImage->setSizePolicy({ QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum });
125 NumRunningModuleImage->setToolTip("Running modules");
126 NumRunningModuleImage->setPixmap(QPixmap(DynExpUI::Icons::Module).scaledToHeight(Height));
127 NumRunningModule->setSizePolicy({ QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding });
128 NumRunningModule->setToolTip("Running modules");
129
133
137}
138
140{
141 std::string ObjectName = "< Unknown >";
142
143 if (Object)
144 {
145 try
146 {
147 ObjectName = Object->GetObjectName();
148 }
149 catch (...)
150 {
151 ObjectName = "< Error while fetching name >";
152 }
153 }
154
155 return ObjectName;
156}
157
159{
160 if (!Object)
161 return;
162
163 std::string ErrorMessage = "";
164 try
165 {
166 Object->EnsureReadyState(false);
167 }
168 catch (const Util::Exception& e)
169 {
170 Util::EventLog().Log(e);
171 ErrorMessage = e.what();
172 }
173 catch (const std::exception& e)
174 {
175 ErrorMessage = e.what();
176 }
177 catch (...)
178 {
179 ErrorMessage = "Unknown Error";
180 }
181
182 if (!ErrorMessage.empty())
183 {
184 QMessageBox::warning(this, "DynExp - Error",
185 QString::fromStdString(
186 "Starting up the item " + GetObjectNameSafe(Object) + ", the following error occurred:\n\n"
187 + ErrorMessage
188 ));
189 }
190}
191
193{
194 if (!Object)
195 return;
196
197 std::string ErrorMessage = "";
198 try
199 {
200 Object->Reset();
201 }
202 catch (const Util::Exception& e)
203 {
204 Util::EventLog().Log(e);
205 ErrorMessage = e.what();
206 }
207 catch (const std::exception& e)
208 {
209 ErrorMessage = e.what();
210 }
211 catch (...)
212 {
213 ErrorMessage = "Unknown Error";
214 }
215
216 if (!ErrorMessage.empty())
217 {
218 std::string ObjectName = GetObjectNameSafe(Object);
219
220 QMessageBox::warning(this, "DynExp - Error",
221 QString::fromStdString(
222 "Resetting the item " + ObjectName + ", the following error occurred:\n\n"
223 + ErrorMessage
224 ));
225 }
226}
227
229{
230 auto EventLogSize = Util::EventLog().GetLogSize();
231 if (EventLogSize >= std::numeric_limits<int>::max())
232 return;
233
234 if (ui->tableLog->rowCount() < static_cast<int>(EventLogSize))
235 {
236 auto Log = Util::EventLog().GetLog(ui->tableLog->rowCount());
237 for (const auto& LogEntry : Log)
238 {
239 const auto Row = ui->tableLog->rowCount();
240
241 ui->tableLog->setRowCount(Row + 1);
242 ui->tableLog->setItem(Row, 0, new QTableWidgetItem(QString::fromStdString(Util::ToStr(LogEntry.TimePoint))));
243 ui->tableLog->setItem(Row, 1, new QTableWidgetItem(Util::Exception::GetErrorLabel(LogEntry.Type)));
244 ui->tableLog->setItem(Row, 2, new QTableWidgetItem(QString::fromStdString(LogEntry.Message)));
245
246 for (int i = 0; i < 3; ++i)
247 ui->tableLog->item(Row, i)->setForeground(HTMLColorStringToThemeColor(Util::Exception::GetErrorLabelColor(LogEntry.Type)));
248
249 ui->tableLog->resizeColumnToContents(2);
250 ui->tableLog->scrollToBottom();
251 }
252 }
253}
254
256{
257 for (auto i = ui->tableLog->rowCount() - 1; i >= 0; --i)
258 for (int j = 0; j < 3; ++j)
259 ui->tableLog->item(i, j)->setForeground(AdjustColorToThemeColor(ui->tableLog->item(i, j)->foreground().color()));
260}
261
263{
264 const auto& ModuleManager = DynExpCore.GetModuleManager();
265
266 for (auto ModuleIter = ModuleManager.cbegin(); ModuleIter != ModuleManager.cend(); ++ModuleIter)
267 {
268 try
269 {
270 auto Module = ModuleIter->second.ResourcePointer.get();
271 if (!Module)
272 continue;
273 if (!Module->HasUI())
274 continue;
275
276 // Conversion should always be fine. Throws if not.
277 auto& QModule = dynamic_cast<DynExp::QModuleBase&>(*Module);
278
279 if (!QModule.IsRunning())
280 QModule.HideUI();
281 else if (QModule.IsPaused())
282 QModule.DisableUI();
283 else
284 QModule.UpdateUI();
285 }
286 catch (const Util::Exception& e)
287 {
288#ifdef DYNEXP_DEBUG
289 Util::EventLog().Log("Updating a module's UI, the error listed below occurred.", Util::ErrorType::Error);
290 Util::EventLog().Log(e);
291#else
293 {
294 Util::EventLog().Log("Updating a module's UI, the error listed below occurred.", Util::ErrorType::Error);
295 Util::EventLog().Log(e);
296 }
297#endif // DYNEXP_DEBUG
298 }
299 catch (const std::exception& e)
300 {
301 Util::EventLog().Log("Updating a module's UI, the error listed below occurred.", Util::ErrorType::Error);
302 Util::EventLog().Log(e.what());
303 }
304 catch (...)
305 {
306 Util::EventLog().Log("Updating a module's UI, an unknown error occurred.", Util::ErrorType::Error);
307 }
308 }
309}
310
312{
314 {
315 auto ProjectFilename = DynExpCore.GetProjectFilename();
316 setWindowTitle(QString::fromStdString(std::string("DynExp Manager - ") + ProjectFilename.string()));
317 }
318 else
319 setWindowTitle("DynExp Manager");
320
321 ui->action_Restore_Windows_from_Settings->setEnabled(static_cast<const DynExp::DynExpCore&>(DynExpCore).GetParams()->StoreWindowStates ==
323}
324
326{
327 const auto NumRunningInstr = DynExpCore.GetInstrumentManager().GetNumRunningInstruments();
328 const auto NumRunningModule = DynExpCore.GetModuleManager().GetNumRunningModules();
329
330 StatusBar.NumRunningInstr->setText(QString::number(NumRunningInstr));
331 StatusBar.NumRunningModule->setText(QString::number(NumRunningModule));
332
334 {
335 StatusBar.State->setText(QString::number(StatusBar.NumItemsInErrorState) +
336 QString((StatusBar.NumItemsInErrorState != 1 ? " errors" : " error")) +
337 ", " + QString::number(StatusBar.NumItemsInWarningState) +
338 QString((StatusBar.NumItemsInWarningState != 1 ? " warnings" : " warning")) + " occurred.");
339 StatusBar.State->setStyleSheet(QString::fromStdString(DynExpUI::PushButtonErrorStyleSheet));
340 }
341 else
342 {
344 {
345 StatusBar.State->setText(QString::number(StatusBar.NumItemsInWarningState) +
346 QString((StatusBar.NumItemsInWarningState != 1 ? " warnings" : " warning")) + " occurred.");
347 StatusBar.State->setStyleSheet(QString::fromStdString(DynExpUI::PushButtonWarningStyleSheet));
348 }
349 else
350 {
351 if (NumRunningInstr + NumRunningModule > 0)
352 {
353 StatusBar.State->setText("Running");
354 StatusBar.State->setStyleSheet(QString::fromStdString(DynExpUI::PushButtonRunningStyleSheet));
355 }
356 else
357 {
358 StatusBar.State->setText("Ready");
359 StatusBar.State->setStyleSheet(QString::fromStdString(UIBrightThemeAction->isChecked() ?
361 }
362 }
363 }
364
365 // When item in ErrorListDlg has been double-clicked, select the respective entry in ui->treeItems.
368}
369
371{
373 return;
374
376 {
377 if (CircuitDiagramDlg->Redraw(DynExpCore))
378 {
381 }
382 else
383 return;
384 }
385
387 {
388 if (CircuitDiagramDlg->UpdateStates(DynExpCore))
390 else
391 return;
392 }
393
394 // When item in CircuitDiagramDlg has been double-clicked, select the respective entry in ui->treeItems.
395 SelectItemTreeItem(CircuitDiagramDlg->GetSelectedEntry());
396}
397
399{
402
403 ErrorEntries.clear();
404
405 // Update item tree and select item most recently added. This is required since editing an item
406 // causes it to be removed and readded to the tree. Its selection state is restored.
407 // Reverse order to show low level (hardware adapter) errors in status bar with highest priority.
410 if (LastAdded)
411 ItemToSelect = LastAdded;
413 if (LastAdded)
414 ItemToSelect = LastAdded;
415
416 if (ItemToSelect)
417 {
418 ui->treeItems->clearSelection();
419 ItemToSelect->setSelected(true);
420 }
421}
422
424{
425 const auto ItemData = Resource.TreeWidgetItem->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
426
427 try
428 {
429 const auto ExceptionPtr = Resource.ResourcePointer->GetException();
430 const auto Warning = Resource.ResourcePointer->GetWarning();
431 const auto IsConnected = Resource.ResourcePointer->IsConnected();
432
433 std::string ObjectName;
434 if (ExceptionPtr || Warning)
435 ObjectName = GetObjectNameSafe(Resource.ResourcePointer.get()) + " (" + Resource.ResourcePointer->GetCategoryAndName() + ")";
436
437 if (ExceptionPtr)
438 {
439 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Error)
440 ChangeItemTreeItemToErrorState(*Resource.TreeWidgetItem, ItemData, ExceptionPtr);
441
443 auto ErrorMessage = Resource.TreeWidgetItem->toolTip(2); // Contains error message.
444
445 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), ErrorMessage, Util::ErrorType::Error, Resource.TreeWidgetItem.get());
446 }
447 else if (Warning)
448 {
449 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Warning)
450 ChangeItemTreeItemToWarningState(*Resource.TreeWidgetItem, ItemData, QString::fromStdString(Warning.Description));
451
453 auto WarningMessage = Resource.TreeWidgetItem->toolTip(2); // Contains warning message.
454
455 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), WarningMessage, Util::ErrorType::Warning, Resource.TreeWidgetItem.get());
456 }
457 else if (!IsConnected)
458 {
459 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::NotConnected)
461 "This hardware adapter is not connected yet, and thus it cannot be used now.");
462 }
463 else
464 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Normal)
466 "This hardware adapter is ready to be used.", "Ready", DynExpUI::Icons::Ready);
467 }
468 catch ([[maybe_unused]] const Util::TimeoutException& e)
469 {
470 constexpr auto Msg = "This hardware adapter does not respond.";
471
472 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::NotResponding)
474
476 }
477}
478
480{
481 const auto ItemData = Resource.TreeWidgetItem->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
482
483 try
484 {
485 const auto ExceptionPtr = Resource.ResourcePointer->GetException();
486 const auto Warning = Resource.ResourcePointer->GetWarning();
487
488 std::string ObjectName;
489 if (ExceptionPtr || Warning)
490 ObjectName = GetObjectNameSafe(Resource.ResourcePointer.get()) + " (" + Resource.ResourcePointer->GetCategoryAndName() + ")";
491
492 if (ExceptionPtr)
493 {
494 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Error)
495 ChangeItemTreeItemToErrorState(*Resource.TreeWidgetItem, ItemData, ExceptionPtr);
496
498 auto ErrorMessage = Resource.TreeWidgetItem->toolTip(2); // Contains error message.
499
500 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), ErrorMessage, Util::ErrorType::Error, Resource.TreeWidgetItem.get());
501 }
502 else if (Warning)
503 {
504 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Warning)
505 ChangeItemTreeItemToWarningState(*Resource.TreeWidgetItem, ItemData, QString::fromStdString(Warning.Description));
506
508 auto WarningMessage = Resource.TreeWidgetItem->toolTip(2); // Contains warning message.
509
510 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), WarningMessage, Util::ErrorType::Warning, Resource.TreeWidgetItem.get());
511 }
512 else
513 {
514 if (Resource.ResourcePointer->IsRunning() &&
515 ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Running)
516 ChangeItemTreeItemToRunningState(*Resource.TreeWidgetItem, ItemData, "This instrument is running.");
517
518 if (!Resource.ResourcePointer->IsRunning() &&
519 ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Normal)
521 "This instrument is ready to be run.", "Stopped");
522 }
523 }
524 catch ([[maybe_unused]] const Util::TimeoutException& e)
525 {
526 constexpr auto Msg = "This instrument does not respond.";
527
528 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::NotResponding)
530
532 }
533}
534
536{
537 const auto ItemData = Resource.TreeWidgetItem->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
538
539 static constexpr auto PausedString = "This module is paused because one or more linked instruments have been terminated. Restart these instruments in order to continue.";
540 static bool DisplayWarning = false;
541 static auto DisplayWarningLastSwitchTime = std::chrono::system_clock::now();
542
543 if (DisplayWarningLastSwitchTime <= std::chrono::system_clock::now() - std::chrono::seconds(2))
544 {
545 DisplayWarning = !DisplayWarning;
546 DisplayWarningLastSwitchTime = std::chrono::system_clock::now();
547 }
548
549 try
550 {
551 const auto ExceptionPtr = Resource.ResourcePointer->GetException();
552 const auto Warning = Resource.ResourcePointer->GetWarning();
553
554 std::string ObjectName;
555 if (ExceptionPtr || Warning || Resource.ResourcePointer->IsPaused())
556 ObjectName = GetObjectNameSafe(Resource.ResourcePointer.get()) + " (" + Resource.ResourcePointer->GetCategoryAndName() + ")";
557
558 if (ExceptionPtr)
559 {
560 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Error)
561 ChangeItemTreeItemToErrorState(*Resource.TreeWidgetItem, ItemData, ExceptionPtr);
562
564 auto ErrorMessage = Resource.TreeWidgetItem->toolTip(2); // Contains error message.
565
566 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), ErrorMessage, Util::ErrorType::Error, Resource.TreeWidgetItem.get());
567 }
568 else if (Warning || Resource.ResourcePointer->IsPaused())
569 {
570 if (Warning)
571 {
573 ErrorEntries.emplace_back(QString::fromStdString(ObjectName), QString::fromStdString(Warning.Description), Util::ErrorType::Warning, Resource.TreeWidgetItem.get());
574 }
575 if (Resource.ResourcePointer->IsPaused())
576 {
578 ErrorEntries.emplace_back(QString::fromStdString(ObjectName),
579 QString(PausedString) + "\n\n" + QString::fromStdString(Resource.ResourcePointer->GetReasonWhyPaused().Description),
581 }
582
583 if ((Warning && !Resource.ResourcePointer->IsPaused()) || (Warning && DisplayWarning))
584 {
585 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Warning)
586 ChangeItemTreeItemToWarningState(*Resource.TreeWidgetItem, ItemData, QString::fromStdString(Warning.Description));
587 }
588 else
589 {
590 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Paused)
591 ChangeItemTreeItemToPausedState(*Resource.TreeWidgetItem, ItemData, PausedString);
592 }
593 }
594 else
595 {
596 if (Resource.ResourcePointer->IsRunning() &&
597 ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Running)
598 ChangeItemTreeItemToRunningState(*Resource.TreeWidgetItem, ItemData, "This module is running.");
599
600 if (!Resource.ResourcePointer->IsRunning() &&
601 ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::Normal)
603 "This module is ready to be run.", "Stopped");
604 }
605 }
606 catch ([[maybe_unused]] const Util::TimeoutException& e)
607 {
608 constexpr auto Msg = "This module does not respond.";
609
610 if (ItemData.ItemTreeItemState != ItemTreeItemDataType::ItemTreeItemStateType::NotResponding)
612
614 }
615}
616
617void DynExpManager::UpdateItemTreeItemObjectName(QTreeWidgetItem* Item, const DynExp::Object* Object)
618{
619 QString ObjectName;
620 bool FetchingObjectNameFailed = false;
621
622 try
623 {
624 ObjectName = QString::fromStdString(Object->GetObjectName());
625 }
626 catch (...)
627 {
628 ObjectName = "< Error >";
629 FetchingObjectNameFailed = true;
630 }
631
632 Item->setText(0, ObjectName);
633 Item->setToolTip(0, ObjectName);
634 Item->setData(0, Qt::ItemDataRole::UserRole, QVariant::fromValue(FetchingObjectNameFailed));
635}
636
637void DynExpManager::SelectItemTreeItem(QTreeWidgetItem* SelectedEntry)
638{
639 if (!SelectedEntry)
640 return;
641
642 for (int i = 0; i < ui->treeItems->topLevelItemCount(); ++i)
643 {
644 QTreeWidgetItem* ParentItem = ui->treeItems->topLevelItem(i);
645 for (int j = 0; j < ParentItem->childCount(); ++j)
646 {
647 QTreeWidgetItem* ChildItem = ParentItem->child(j);
648 if (ChildItem->data(1, Qt::ItemDataRole::UserRole).value<decltype(SelectedEntry)>() == SelectedEntry)
649 {
651
652 ui->treeItems->clearSelection();
653 ui->treeItems->setFocus();
654 ChildItem->setSelected(true);
655
656 return;
657 }
658 }
659 }
660}
661
662void DynExpManager::ChangeItemTreeItemToNormalState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
663 const QString& Description, const QString& StateTitle, const char* IconPath)
664{
665 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
666 QVariant::fromValue(ItemTreeItemDataType(ItemTreeItemData.ID, ItemTreeItemDataType::ItemTreeItemStateType::Normal)));
667 ItemTreeItem.setIcon(2, QIcon(IconPath));
668 ItemTreeItem.setText(2, StateTitle);
669 ItemTreeItem.setForeground(2, QApplication::palette().text().color());
670 ItemTreeItem.setToolTip(2, Description);
671
673
674 // Issued to update also in case an item's thread terminates regularly
676}
677
678void DynExpManager::ChangeItemTreeItemToRunningState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
679 const QString& Description)
680{
681 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
683 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::Running));
684 ItemTreeItem.setText(2, "Running");
685 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("green"));
686 ItemTreeItem.setToolTip(2, Description);
687
689
690 // Issued to update also in case an item's thread is started automatically.
692}
693
694void DynExpManager::ChangeItemTreeItemToPausedState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
695 const QString& Description)
696{
697 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
698 QVariant::fromValue(ItemTreeItemDataType(ItemTreeItemData.ID, ItemTreeItemDataType::ItemTreeItemStateType::Paused)));
699 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::Paused));
700 ItemTreeItem.setText(2, "Paused");
701 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("orange"));
702 ItemTreeItem.setToolTip(2, Description);
703
705
706 // Issued to update also in case an item's thread is started automatically causing a paused situation immediately.
708}
709
710void DynExpManager::ChangeItemTreeItemToWarningState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
711 const QString& Description)
712{
713 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
715 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::Warning));
716 ItemTreeItem.setText(2, "Warning");
717 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("orange"));
718 ItemTreeItem.setToolTip(2, Description);
719
721
722 // Issued to update also in case an item's thread is started automatically causing a warning immediately.
724}
725
726void DynExpManager::ChangeItemTreeItemToErrorState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
727 const std::exception_ptr& ExceptionPtr)
728{
729 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
730 QVariant::fromValue(ItemTreeItemDataType(ItemTreeItemData.ID, ItemTreeItemDataType::ItemTreeItemStateType::Error)));
731 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::Error));
732 ItemTreeItem.setText(2, "Error");
733 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("red"));
734 ItemTreeItem.setToolTip(2, QString::fromStdString(Util::ExceptionToStr(ExceptionPtr)));
735
737
738 // Issued to update also in case an item's thread terminates due to an error.
740}
741
742void DynExpManager::ChangeItemTreeItemToNotConnectedState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
743 const QString& Description)
744{
745 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
747 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::NotReady));
748 ItemTreeItem.setText(2, "Connecting...");
749 ItemTreeItem.setForeground(2, QApplication::palette().text().color());
750 ItemTreeItem.setToolTip(2, Description);
751
753}
754
755void DynExpManager::ChangeItemTreeItemToNotRespondingState(QTreeWidgetItem& ItemTreeItem, const ItemTreeItemDataType& ItemTreeItemData,
756 const QString& Description)
757{
758 ItemTreeItem.setData(2, Qt::ItemDataRole::UserRole,
760 ItemTreeItem.setIcon(2, QIcon(DynExpUI::Icons::NotResponding));
761 ItemTreeItem.setText(2, "Not responding");
762 ItemTreeItem.setForeground(2, HTMLColorStringToThemeColor("orange"));
763 ItemTreeItem.setToolTip(2, Description);
764
766
767 // Issued to update also in case an item's thread is started automatically causing a warning immediately.
769}
770
771QColor DynExpManager::HTMLColorStringToThemeColor(const std::string& HTMLColor) const
772{
773 QString HTMLColorQ = QString::fromStdString(HTMLColor);
774
775 if (UIThemeActionGroup->checkedAction() == UIDarkThemeAction)
776 {
777 if (HTMLColorQ.toLower() == "blue")
779 if (HTMLColorQ.toLower() == "green")
781 }
782
783 return QColor(HTMLColorQ);
784}
785
786QColor DynExpManager::AdjustColorToThemeColor(const QColor& Color) const
787{
788 if (UIThemeActionGroup->checkedAction() == UIBrightThemeAction)
789 {
790 if (Color == DynExpUI::DarkPalette::blue)
791 return QColor("blue");
792 }
793 else if (UIThemeActionGroup->checkedAction() == UIDarkThemeAction)
794 {
795 if (Color == QColor("blue"))
797 }
798
799 return Color;
800}
801
802void DynExpManager::Reset(bool Force)
803{
804 IsResetting = true;
805
806 try
807 {
808 DynExpCore.Reset(Force);
809 }
810 catch (...)
811 {
812 IsResetting = false;
813
814 throw;
815 }
816
817 IsResetting = false;
819}
820
822{
824 if (QMessageBox::question(this, "DynExp - Close project?",
825 "Really close the current project? All running items will be stopped and unsaved changes will be lost.",
826 QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No)
827 != QMessageBox::StandardButton::Yes)
828 return false;
829
830 std::string ErrorMessage = "";
831 try
832 {
833 Reset();
834 }
835 catch (const Util::Exception& e)
836 {
837 Util::EventLog().Log(e);
838 ErrorMessage = e.what();
839 }
840 catch (const std::exception& e)
841 {
842 ErrorMessage = e.what();
843 }
844 catch (...)
845 {
846 ErrorMessage = "Unknown Error";
847 }
848
849 if (!ErrorMessage.empty())
850 {
851 auto Result = QMessageBox::critical(this, "DynExp - Critical error",
852 QString::fromStdString(
853 "Closing a project and stopping all running items, the following error occurred:\n\n"
854 + ErrorMessage + "\n\n- Click 'Retry' to try again to close the project."
855 + "\n- Click 'Ignore' to force the project to be closed. This may cause DynExp to be terminated without properly shutting down."
856 + "\n- Click 'Cancel' to return to the project without closing it."
857 ), QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::Retry | QMessageBox::StandardButton::Ignore,
858 QMessageBox::StandardButton::Cancel);
859
860 switch (Result)
861 {
862 case QMessageBox::StandardButton::Cancel: return false;
863 case QMessageBox::StandardButton::Ignore: return true;
864 default: return CloseProject();
865 }
866 }
867
869
870 return true;
871}
872
874{
875 std::string ErrorMessage = "";
876 try
877 {
879 }
880 catch (const Util::Exception& e)
881 {
882 Util::EventLog().Log(e);
883 ErrorMessage = e.what();
884 }
885 catch (const std::exception& e)
886 {
887 ErrorMessage = e.what();
888 }
889 catch (...)
890 {
891 ErrorMessage = "Unknown Error";
892 }
893
894 if (!ErrorMessage.empty())
895 {
896 auto Result = QMessageBox::critical(this, "DynExp - Critical error",
897 QString::fromStdString(
898 "Stopping a worker thread, the following error occurred:\n\n"
899 + ErrorMessage + "\n\n- Click 'Retry' to try again to stop the thread."
900 + "\n- Click 'Ignore' to force DynExp to be terminated without properly shutting down."
901 + "\n- Click 'Cancel' to return to DynExp without closing it."
902 ), QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::Retry | QMessageBox::StandardButton::Ignore,
903 QMessageBox::StandardButton::Cancel);
904
905 switch (Result)
906 {
907 case QMessageBox::StandardButton::Cancel: return false;
908 case QMessageBox::StandardButton::Ignore: std::terminate();
909 default: return Shutdown();
910 }
911 }
912
913 return true;
914}
915
916void DynExpManager::SaveProject(std::string_view Filename) noexcept
917{
918 std::string ErrorMessage = "";
919 try
920 {
921 DynExpCore.SaveProject(Filename, *this, *CircuitDiagramDlg, *ui->splitterInstrListMain, *ui->splitterMDILog);
922 }
923 catch (const Util::Exception& e)
924 {
925 Util::EventLog().Log(e);
926 ErrorMessage = e.what();
927 }
928 catch (const std::exception& e)
929 {
930 ErrorMessage = e.what();
931 }
932 catch (...)
933 {
934 ErrorMessage = "Unknown Error";
935 }
936
937 if (!ErrorMessage.empty())
938 {
939 QMessageBox::warning(this, "DynExp - Error",
940 QString::fromStdString(
941 std::string("Saving the project to file ").append(Filename)
942 + ", the following error occurred:\n\n" + ErrorMessage
943 ));
944 }
945}
946
948{
949 ui->action_Run_Item->setEnabled(false);
950 ui->action_Stop_Item->setEnabled(false);
951 ui->action_Force_Stop_Item->setEnabled(false);
952 ui->action_Reset_Item->setEnabled(false);
953 ui->action_Configure_Item->setEnabled(false);
954 ui->action_Delete_Item->setEnabled(false);
955
956 ClearWarningAction->setEnabled(false);
957}
958
960{
961 const auto NumModules = ModuleWindowsActionGroup->actions().size();
962
963 // Shortcuts Ctrl + 1 to Ctrl + 9 (Ctrl + 0 activates main window)
964 for (std::remove_const_t<decltype(NumModules)> i = 0; i < NumModules; ++i)
965 {
966 if (i < 9)
967 ModuleWindowsActionGroup->actions()[i]->setShortcut(QKeySequence(Qt::CTRL | static_cast<Qt::Key>(Qt::Key_1 + i)));
968 else
969 ModuleWindowsActionGroup->actions()[i]->setShortcut(QKeySequence());
970 }
971}
972
974{
975 const auto& ModuleManager = DynExpCore.GetModuleManager();
976
977 for (auto ModuleIter = ModuleManager.cbegin(); ModuleIter != ModuleManager.cend(); ++ModuleIter)
978 {
979 try
980 {
981 auto Module = ModuleIter->second.ResourcePointer.get();
982 if (!Module)
983 continue;
984 if (!Module->IsRunning() || !Module->HasUI())
985 continue;
986
987 // Conversion should never fail. Throws if not.
988 auto& QModule = dynamic_cast<DynExp::QModuleBase&>(*Module);
990 }
991 catch (const Util::Exception& e)
992 {
993 Util::EventLog().Log("Enumerating active module windows, the error listed below occurred.", Util::ErrorType::Error);
994 Util::EventLog().Log(e);
995 }
996 catch (const std::exception& e)
997 {
998 Util::EventLog().Log("Enumerating active module windows, the error listed below occurred.", Util::ErrorType::Error);
999 Util::EventLog().Log(e.what());
1000 }
1001 catch (...)
1002 {
1003 Util::EventLog().Log("Enumerating active module windows, an unknown error occurred.", Util::ErrorType::Error);
1004 }
1005 }
1006}
1007
1009{
1010 const auto& ModuleManager = DynExpCore.GetModuleManager();
1011
1012 for (auto ModuleIter = ModuleManager.cbegin(); ModuleIter != ModuleManager.cend(); ++ModuleIter)
1013 {
1014 try
1015 {
1016 auto Module = ModuleIter->second.ResourcePointer.get();
1017 if (!Module)
1018 continue;
1019 if (!Module->IsRunning() || !Module->HasUI())
1020 continue;
1021
1022 // Conversion should never fail. Throws if not.
1023 auto& QModule = dynamic_cast<DynExp::QModuleBase&>(*Module);
1024 if (QModule.IsActiveWindow())
1025 return &QModule;
1026 }
1027 catch (const Util::Exception& e)
1028 {
1029 Util::EventLog().Log("Finding active module window, the error listed below occurred.", Util::ErrorType::Error);
1030 Util::EventLog().Log(e);
1031 }
1032 catch (const std::exception& e)
1033 {
1034 Util::EventLog().Log("Finding active module window, the error listed below occurred.", Util::ErrorType::Error);
1035 Util::EventLog().Log(e.what());
1036 }
1037 catch (...)
1038 {
1039 Util::EventLog().Log("Finding active module window, an unknown error occurred.", Util::ErrorType::Error);
1040 }
1041 }
1042
1043 return nullptr;
1044}
1045
1046void DynExpManager::closeEvent(QCloseEvent* event)
1047{
1048 UpdateUITimer->stop();
1049
1050 if (CloseProject() && Shutdown())
1051 {
1052 CircuitDiagramDlg->hide();
1053
1054 event->accept();
1055
1056 return;
1057 }
1058
1059 event->ignore();
1060 UpdateUITimer->start();
1061}
1062
1064{
1065 if (!Object)
1066 throw Util::InvalidArgException("Object cannot be nullptr.");
1067
1068 // If Object is a module which has an UI (a QModule), init and display its UI.
1069 auto Module = dynamic_cast<DynExp::QModuleBase*>(Object);
1070 if (!Module)
1071 return;
1072
1073 if (!Module->IsRunning())
1074 return;
1075
1076 try
1077 {
1078 auto const Action = &Module->InitUI(*this, ui->mdiMain);
1079 ModuleWindowsActionGroup->addAction(Action);
1081 ui->menu_Window->addAction(Action);
1082 }
1083 catch (...)
1084 {
1085 // In case of an error occurring setting up the module's UI, terminate the module.
1086 Module->Reset();
1087
1088 throw;
1089 }
1090}
1091
1092bool DynExpManager::StopItem(DynExp::RunnableObject* Object, bool Force) noexcept
1093{
1094 if (!Object)
1095 return false;
1096
1097 std::string ErrorMessage = "";
1098 std::string ObjectName = "< Unknown >";
1099 try
1100 {
1101 ObjectName = GetObjectNameSafe(Object);
1102 Object->Terminate(Force);
1103
1105
1106 return true;
1107 }
1109 {
1110 if (QMessageBox::warning(this, "DynExp - Force-terminate item?",
1111 QString("This runnable is currently being used by at least another item. Force-terminate the item?\n\n")
1112 + e.GetUserNames().data(),
1113 QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No)
1114 == QMessageBox::StandardButton::Yes)
1115 {
1116 return StopItem(Object, true);
1117 }
1118 }
1119 catch (const Util::Exception& e)
1120 {
1121 Util::EventLog().Log(e);
1122 ErrorMessage = e.what();
1123 }
1124 catch (const std::exception& e)
1125 {
1126 ErrorMessage = e.what();
1127 }
1128 catch (...)
1129 {
1130 ErrorMessage = "Unknown Error";
1131 }
1132
1133 if (!ErrorMessage.empty())
1134 {
1135 QMessageBox::warning(this, "DynExp - Error",
1136 QString::fromStdString(
1137 "Stopping the item " + ObjectName + ", the following error occurred:\n\n"
1138 + ErrorMessage
1139 ));
1140 }
1141
1143
1144 return false;
1145}
1146
1148{
1149 activateWindow();
1150 raise();
1151}
1152
1153void DynExpManager::PostKeyPressEvent(QKeyEvent* Event) noexcept
1154{
1155 // postEvent() will delete event after posting it into the event queue.
1156 QCoreApplication::postEvent(this, Event->clone());
1157}
1158
1160{
1161 // Necessary to prevent calling functions of destroyed objects while stack unwinding is in progress.
1162 if (std::uncaught_exceptions())
1163 return;
1164
1165 try
1166 {
1167 UpdateLog();
1170 UpdateStatusBar(); // Relies on item tree having been updated directly before.
1171 UpdateCircuitDiagram(); // Relies on item tree having been updated directly before.
1172 }
1173 catch (const Util::Exception& e)
1174 {
1175#ifdef DYNEXP_DEBUG
1176 Util::EventLog().Log("Updating the main window UI, the error listed below occurred.", Util::ErrorType::Error);
1177 Util::EventLog().Log(e);
1178#else
1180 {
1181 Util::EventLog().Log("Updating the main window UI, the error listed below occurred.", Util::ErrorType::Error);
1182 Util::EventLog().Log(e);
1183 }
1184#endif // DYNEXP_DEBUG
1185 }
1186 catch (const std::exception& e)
1187 {
1188 Util::EventLog().Log("Updating the main window UI, the error listed below occurred.", Util::ErrorType::Error);
1189 Util::EventLog().Log(e.what());
1190 }
1191 catch (...)
1192 {
1193 Util::EventLog().Log("Updating the main window UI, an unknown error occurred.", Util::ErrorType::Error);
1194 }
1195
1196 // The following functions do not throw.
1197 // ->
1199
1202 // <-
1203}
1204
1205void DynExpManager::OnUIThemeChanged(QAction* ThemeAction)
1206{
1207 if (ThemeAction == UIBrightThemeAction)
1208 {
1209 qApp->setPalette(QPalette());
1210 qApp->setStyleSheet("");
1211 }
1212 else if (ThemeAction == UIDarkThemeAction)
1213 {
1214 qApp->setPalette(DynExpUI::DarkPalette::GetPalette());
1215 qApp->setStyleSheet(QString::fromStdString(DynExpUI::DarkPalette::GetStyleSheet()));
1216 }
1217
1219 DynExpCore.GetHardwareAdapterManager().DeleteAllTreeWidgetItems();
1220 DynExpCore.GetInstrumentManager().DeleteAllTreeWidgetItems();
1221 DynExpCore.GetModuleManager().DeleteAllTreeWidgetItems();
1222
1224}
1225
1227{
1228 if (!CloseProject())
1229 return;
1230}
1231
1233{
1234 if (!CloseProject())
1235 return;
1236
1237 auto Filename = Util::PromptOpenFilePath(this, "Open project", ".dynp", "DynExp project files (*.dynp)").toStdString();
1238 if (Filename.empty())
1239 return;
1240
1241 std::string ErrorMessage = "";
1242 try
1243 {
1244 DynExpCore.OpenProject(Filename);
1245 }
1246 catch (const Util::Exception& e)
1247 {
1248 Util::EventLog().Log(e);
1249 ErrorMessage = e.what();
1250 }
1251 catch (const std::exception& e)
1252 {
1253 ErrorMessage = e.what();
1254 }
1255 catch (...)
1256 {
1257 ErrorMessage = "Unknown Error";
1258 }
1259
1260 if (!ErrorMessage.empty())
1261 {
1262 QMessageBox::warning(this, "DynExp - Error",
1263 QString::fromStdString(
1264 "Opening a project from file " + Filename + ", the following error occurred:\n\n"
1265 + ErrorMessage
1266 ));
1267
1268 try
1269 {
1270 // If this throws, something went terribly wrong. Terminate application.
1271 Reset(true);
1272 }
1273 catch (...)
1274 {
1275 Util::EventLog().Log("Error occurred while resetting project. Execution cannot continue.",
1277
1278 std::terminate();
1279 }
1280 }
1281 else
1282 {
1283 // First, only restore window states and sizes in order not to confuse the user
1284 // by windows moving away suddenly after some time of starting up the project...
1285 try
1286 {
1287 DynExpCore.RestoreWindowStatesFromParams(*this, *CircuitDiagramDlg, *ui->splitterInstrListMain, *ui->splitterMDILog, true);
1288 }
1289 catch (...)
1290 {
1291 // Swallow any exception here since we try again in a moment.
1292 }
1293
1294 // ...second, run the project and restore all the module windows' states (and again the o
1295 OnRunProject();
1297 }
1298}
1299
1307
1309{
1310 auto Filename = Util::PromptSaveFilePath(this, "Save project as", ".dynp", "DynExp project files (*.dynp)").toStdString();
1311 if (Filename.empty())
1312 return;
1313
1314 SaveProject(Filename);
1315}
1316
1317// Firstly, start hardware adapters. Then, wait until they are connected before starting
1318// instruments and modules.
1320{
1321 std::string ErrorMessage = "";
1322 try
1323 {
1324 // Connect all hardware adapters asynchronously
1325 auto ConnectingHardwareAdaptersFuture = DynExpCore.ConnectHardwareAdapters();
1326
1327 auto BusyDlg = std::make_unique<BusyDialog>(this);
1328 BusyDlg->SetDescriptionText("Connecting hardware adapters...");
1329 BusyDlg->SetCheckFinishedFunction(std::bind_front(&DynExp::DynExpCore::AllHardwareAdaptersConnected, &DynExpCore));
1330
1331 // BusyDlg->exec() blocks and starts new event loop for the modal dialog.
1332 auto Result = BusyDlg->exec();
1333 ConnectingHardwareAdaptersFuture.wait();
1334
1335 if (Result == QDialog::Accepted)
1336 {
1337 BusyDlg->SetDescriptionText("Starting instruments...");
1338 BusyDlg->SetCheckFinishedFunction(std::bind_front(&DynExp::DynExpCore::AllInstrumentsInitialized, &DynExpCore));
1339
1341 Result = BusyDlg->exec();
1342
1343 if (Result == QDialog::Accepted)
1344 DynExpCore.RunModules(std::bind_front(&DynExpManager::RegisterModuleUI, this));
1345 else if (BusyDlg->GetException())
1346 std::rethrow_exception(BusyDlg->GetException());
1347 }
1348 else if (BusyDlg->GetException())
1349 std::rethrow_exception(BusyDlg->GetException());
1350 }
1351 catch (const Util::Exception& e)
1352 {
1353 Util::EventLog().Log(e);
1354 ErrorMessage = e.what();
1355 }
1356 catch (const std::exception& e)
1357 {
1358 ErrorMessage = e.what();
1359 }
1360 catch (...)
1361 {
1362 ErrorMessage = "Unknown Error";
1363 }
1364
1365 if (!ErrorMessage.empty())
1366 {
1367 QMessageBox::warning(this, "DynExp - Error",
1368 QString::fromStdString(
1369 "Starting up the project, the following error occurred:\n\n"
1370 + ErrorMessage
1371 ));
1372 }
1373
1375}
1376
1378{
1379 std::string ErrorMessage = "";
1380 try
1381 {
1383 }
1384 catch (const Util::Exception& e)
1385 {
1386 Util::EventLog().Log(e);
1387 ErrorMessage = e.what();
1388 }
1389 catch (const std::exception& e)
1390 {
1391 ErrorMessage = e.what();
1392 }
1393 catch (...)
1394 {
1395 ErrorMessage = "Unknown Error";
1396 }
1397
1398 if (!ErrorMessage.empty())
1399 {
1400 QMessageBox::warning(this, "DynExp - Error",
1401 QString::fromStdString(
1402 "Shutting down the project, the following error occurred:\n\n"
1403 + ErrorMessage
1404 ));
1405 }
1406
1408}
1409
1411{
1412 std::string ErrorMessage = "";
1413 try
1414 {
1416 }
1417 catch (const Util::Exception& e)
1418 {
1419 Util::EventLog().Log(e);
1420 ErrorMessage = e.what();
1421 }
1422 catch (const std::exception& e)
1423 {
1424 ErrorMessage = e.what();
1425 }
1426 catch (...)
1427 {
1428 ErrorMessage = "Unknown Error";
1429 }
1430
1431 if (!ErrorMessage.empty())
1432 {
1433 QMessageBox::warning(this, "DynExp - Error",
1434 QString::fromStdString(
1435 "Resetting the project's failed items, the following error occurred:\n\n"
1436 + ErrorMessage
1437 ));
1438 }
1439
1441}
1442
1444{
1445 std::string ErrorMessage = "";
1446 try
1447 {
1448 DynExpCore.RestoreWindowStatesFromParams(*this, *CircuitDiagramDlg, *ui->splitterInstrListMain, *ui->splitterMDILog);
1449 }
1450 catch (const Util::Exception& e)
1451 {
1452 Util::EventLog().Log(e);
1453 ErrorMessage = e.what();
1454 }
1455 catch (const std::exception& e)
1456 {
1457 ErrorMessage = e.what();
1458 }
1459 catch (...)
1460 {
1461 ErrorMessage = "Unknown Error";
1462 }
1463
1464 if (!ErrorMessage.empty())
1465 {
1466 QMessageBox::warning(this, "DynExp - Error",
1467 QString::fromStdString(
1468 "Restoring the main and module windows' geometries and states from the project file, the following error occurred:\n\n"
1469 + ErrorMessage
1470 ));
1471 }
1472}
1473
1475{
1476 std::string ErrorMessage = "";
1477 try
1478 {
1480 }
1481 catch (const Util::Exception& e)
1482 {
1483 Util::EventLog().Log(e);
1484 ErrorMessage = e.what();
1485 }
1486 catch (const std::exception& e)
1487 {
1488 ErrorMessage = e.what();
1489 }
1490 catch (...)
1491 {
1492 ErrorMessage = "Unknown Error";
1493 }
1494
1495 if (!ErrorMessage.empty())
1496 {
1497 QMessageBox::warning(this, "DynExp - Error",
1498 QString::fromStdString(
1499 "Editing the project settings, the following error occurred:\n\n"
1500 + ErrorMessage
1501 ));
1502 }
1503}
1504
1506{
1507 if (ui->treeItems->selectedItems().length() != 1)
1508 return;
1509
1510 auto Item = ui->treeItems->selectedItems()[0];
1511 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1512
1513 std::string ErrorMessage = "";
1514 std::string ObjectName = "< Unknown >";
1515 try
1516 {
1517 DynExp::RunnableObject* Object = nullptr;
1518
1519 if (Item->parent() == ItemTreeInstruments)
1520 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1521 if (Item->parent() == ItemTreeModules)
1522 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1523
1524 if (Object)
1525 {
1526 ObjectName = GetObjectNameSafe(Object);
1527
1528 Object->Run(this);
1529 RegisterModuleUI(Object);
1530 }
1531 }
1532 catch (const Util::Exception& e)
1533 {
1534 Util::EventLog().Log(e);
1535 ErrorMessage = e.what();
1536 }
1537 catch (const std::exception& e)
1538 {
1539 ErrorMessage = e.what();
1540 }
1541 catch (...)
1542 {
1543 ErrorMessage = "Unknown Error";
1544 }
1545
1546 if (!ErrorMessage.empty())
1547 {
1548 QMessageBox::warning(this, "DynExp - Error",
1549 QString::fromStdString(
1550 "Starting the item " + ObjectName + ", the following error occurred:\n\n"
1551 + ErrorMessage
1552 ));
1553 }
1554
1556}
1557
1559{
1560 if (ui->treeItems->selectedItems().length() != 1)
1561 return;
1562
1563 auto Item = ui->treeItems->selectedItems()[0];
1564 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1565
1566 DynExp::RunnableObject* Object = nullptr;
1567 std::string ErrorMessage = "";
1568 try
1569 {
1570 if (Item->parent() == ItemTreeInstruments)
1571 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1572 if (Item->parent() == ItemTreeModules)
1573 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1574 }
1575 catch (const Util::Exception& e)
1576 {
1577 Util::EventLog().Log(e);
1578 ErrorMessage = e.what();
1579 }
1580 catch (const std::exception& e)
1581 {
1582 ErrorMessage = e.what();
1583 }
1584 catch (...)
1585 {
1586 ErrorMessage = "Unknown Error";
1587 }
1588
1589 if (!ErrorMessage.empty())
1590 {
1591 QMessageBox::warning(this, "DynExp - Error",
1592 QString::fromStdString(
1593 "Determining the object to be stopped, the following error occurred:\n\n"
1594 + ErrorMessage
1595 ));
1596 }
1597
1598 StopItem(Object, Force); // noexcept, checks whether Object is nullptr.
1599}
1600
1602{
1603 OnStopItem(true);
1604}
1605
1607{
1608 if (ui->treeItems->selectedItems().length() != 1)
1609 return;
1610
1611 auto Item = ui->treeItems->selectedItems()[0];
1612 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1613 DynExp::Object* Object = nullptr;
1614
1615 std::string ErrorMessage = "";
1616 try
1617 {
1618 if (Item->parent() == ItemTreeHardwareAdapters)
1619 Object = DynExpCore.GetHardwareAdapterManager().GetResource(ItemData.ID);
1620 if (Item->parent() == ItemTreeInstruments)
1621 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1622 if (Item->parent() == ItemTreeModules)
1623 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1624 }
1625 catch (const Util::Exception& e)
1626 {
1627 Util::EventLog().Log(e);
1628 ErrorMessage = e.what();
1629 }
1630 catch (const std::exception& e)
1631 {
1632 ErrorMessage = e.what();
1633 }
1634 catch (...)
1635 {
1636 ErrorMessage = "Unknown Error";
1637 }
1638
1639 if (!ErrorMessage.empty())
1640 {
1641 std::string ObjectName = GetObjectNameSafe(Object);
1642
1643 QMessageBox::warning(this, "DynExp - Error",
1644 QString::fromStdString(
1645 "Resetting the item " + ObjectName + ", the following error occurred:\n\n"
1646 + ErrorMessage
1647 ));
1648 }
1649 else
1650 ResetItem(Object); // ResetItem() itself checks if nullptr and handles exceptions.
1651
1653}
1654
1656{
1657 if (ui->treeItems->selectedItems().length() != 1)
1658 return;
1659
1660 auto Item = ui->treeItems->selectedItems()[0];
1661 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1662 DynExp::Object* Object = nullptr;
1663 DynExp::RunnableObject* RunnableObject = nullptr;
1664 bool ResetRequired = false;
1665
1666 std::string ErrorMessage = "";
1667 try
1668 {
1669 if (Item->parent() == ItemTreeHardwareAdapters)
1670 Object = DynExpCore.GetHardwareAdapterManager().GetResource(ItemData.ID);
1671 if (Item->parent() == ItemTreeInstruments)
1672 {
1673 RunnableObject = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1674 Object = RunnableObject;
1675 }
1676 if (Item->parent() == ItemTreeModules)
1677 {
1678 RunnableObject = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1679 Object = RunnableObject;
1680 }
1681
1682 if (!Object)
1683 return;
1684
1685 if (Item->parent() == ItemTreeHardwareAdapters)
1687 if (Item->parent() == ItemTreeInstruments)
1689 if (Item->parent() == ItemTreeModules)
1691 }
1692 catch (const Util::Exception& e)
1693 {
1694 Util::EventLog().Log(e);
1695 ErrorMessage = e.what();
1696 }
1697 catch (const std::exception& e)
1698 {
1699 ErrorMessage = e.what();
1700 }
1701 catch (...)
1702 {
1703 ErrorMessage = "Unknown Error";
1704 }
1705
1706 std::string ObjectName = GetObjectNameSafe(Object);
1707
1708 if (!ErrorMessage.empty())
1709 QMessageBox::warning(this, "DynExp - Error",
1710 QString::fromStdString(
1711 "Configuring the item " + ObjectName + ", the following error occurred:\n\n"
1712 + ErrorMessage
1713 ));
1714 else
1715 {
1716 if (ResetRequired)
1717 {
1718 // Only ask whether to reset item for hardware adapters and running instruments/modules.
1719 if ((Object && !RunnableObject) /* hardware adapter */ || (RunnableObject && RunnableObject->IsRunning()) /* instrument or module */)
1720 {
1721 if (QMessageBox::question(this, "DynExp - Reset item?",
1722 QString::fromStdString("The item " + ObjectName + " needs to be reset in order to apply the changes. Should it be reset now?"),
1723 QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No)
1724 == QMessageBox::StandardButton::Yes)
1725 ResetItem(Object); // See below.
1726 }
1727 else
1728 ResetItem(Object); // ResetItem() itself checks if nullptr and handles exceptions.
1729 }
1730 }
1731
1733}
1734
1736{
1737 if (ui->treeItems->selectedItems().length() != 1)
1738 return;
1739
1740 auto Item = ui->treeItems->selectedItems()[0];
1741 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1742
1743 std::string ErrorMessage = "";
1744 std::string ObjectName = "< Unknown >";
1745 try
1746 {
1747 if (Item->parent() == ItemTreeHardwareAdapters)
1748 {
1749 auto Object = DynExpCore.GetHardwareAdapterManager().GetResource(ItemData.ID);
1750
1751 if (Object)
1752 {
1753 ObjectName = GetObjectNameSafe(Object);
1754 DynExpCore.GetHardwareAdapterManager().RemoveResource(ItemData.ID);
1755 }
1756 }
1757 else
1758 {
1759 DynExp::RunnableObject* Object = nullptr;
1760
1761 if (Item->parent() == ItemTreeInstruments)
1762 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1763 if (Item->parent() == ItemTreeModules)
1764 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1765
1766 if (Object)
1767 {
1768 ObjectName = GetObjectNameSafe(Object);
1769
1770 if (Object->IsRunning())
1771 {
1772 if (QMessageBox::question(this, "DynExp - Stop item?",
1773 QString::fromStdString("The item " + ObjectName + " is currently running. Should it be stopped in order to be deleted?"),
1774 QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No)
1775 != QMessageBox::StandardButton::Yes)
1776 return;
1777
1778 Object->Terminate();
1779 }
1780
1781 if (Item->parent() == ItemTreeInstruments)
1782 DynExpCore.GetInstrumentManager().RemoveResource(ItemData.ID);
1783 if (Item->parent() == ItemTreeModules)
1784 DynExpCore.GetModuleManager().RemoveResource(ItemData.ID);
1785 }
1786 }
1787 }
1789 {
1790 ErrorMessage = e.GetErrorMessage();
1791 }
1792 catch (const Util::Exception& e)
1793 {
1794 Util::EventLog().Log(e);
1795 ErrorMessage = e.what();
1796 }
1797 catch (const std::exception& e)
1798 {
1799 ErrorMessage = e.what();
1800 }
1801 catch (...)
1802 {
1803 ErrorMessage = "Unknown Error";
1804 }
1805
1807
1808 if (!ErrorMessage.empty())
1809 {
1810 QMessageBox::warning(this, "DynExp - Error",
1811 QString::fromStdString(
1812 "Deleting the item " + ObjectName + ", the following error occurred:\n\n"
1813 + ErrorMessage
1814 ));
1815 }
1816
1818}
1819
1821{
1822 ui->action_Dock_Undock_Window->setEnabled(GetModuleByActiveUIWindow() != nullptr);
1823 ui->action_Dummy_NoWindow->setVisible(ModuleWindowsActionGroup->actions().empty());
1825}
1826
1828{
1829 // To listen on shortcut.
1830 ui->action_Dock_Undock_Window->setEnabled(true);
1831}
1832
1834{
1835 auto const QModule = GetModuleByActiveUIWindow();
1836
1837 if (QModule)
1838 QModule->DockUndockWindow();
1839}
1840
1846
1848{
1849 AboutDialog->exec();
1850}
1851
1853{
1854 // Avoid direct reopening after close (refer to ErrorListDialog::focusOutEvent()).
1856 {
1857 ErrorListDlg->show();
1858 ErrorListDlg->resize(StatusBar.State->width() - 4, 250);
1859 ErrorListDlg->move(StatusBar.State->mapToGlobal(StatusBar.State->pos()) - QPoint(0, 6 + ErrorListDlg->height()));
1860 ErrorListDlg->setFocus();
1861 }
1862 else
1864}
1865
1867{
1868 LogContextMenu->exec(ui->tableLog->mapToGlobal(Position));
1869}
1870
1872{
1873 ItemTreeContextMenu->exec(ui->treeItems->mapToGlobal(Position));
1874}
1875
1877{
1878 std::string ErrorMessage = "";
1879 try
1880 {
1882 ui->tableLog->setRowCount(0);
1883 }
1884 catch (const Util::Exception& e)
1885 {
1886 Util::EventLog().Log(e);
1887 ErrorMessage = e.what();
1888 }
1889 catch (const std::exception& e)
1890 {
1891 ErrorMessage = e.what();
1892 }
1893 catch (...)
1894 {
1895 ErrorMessage = "Unknown Error";
1896 }
1897
1898 if (!ErrorMessage.empty())
1899 {
1900 QMessageBox::warning(this, "DynExp - Error",
1901 QString::fromStdString(
1902 "Clearing the event log, the following error occurred:\n\n"
1903 + ErrorMessage
1904 ));
1905 }
1906}
1907
1909{
1910 if (ui->treeItems->selectedItems().length() != 1)
1911 return;
1912
1913 auto Item = ui->treeItems->selectedItems()[0];
1914 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1915 DynExp::Object* Object = nullptr;
1916
1917 try
1918 {
1919 if (Item->parent() == ItemTreeHardwareAdapters)
1920 Object = DynExpCore.GetHardwareAdapterManager().GetResource(ItemData.ID);
1921 if (Item->parent() == ItemTreeInstruments)
1922 Object = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID);
1923 if (Item->parent() == ItemTreeModules)
1924 Object = DynExpCore.GetModuleManager().GetResource(ItemData.ID);
1925
1926 if (!Object)
1927 return;
1928
1929 Object->ClearWarning();
1930 }
1931 catch (...)
1932 {
1933 return;
1934 }
1935}
1936
1941
1943{
1944 MakeItem(qobject_cast<QAction*>(sender()), DynExpCore.GetInstrumentLib(), DynExpCore.GetInstrumentManager());
1945}
1946
1948{
1949 MakeItem(qobject_cast<QAction*>(sender()), DynExpCore.GetModuleLib(), DynExpCore.GetModuleManager());
1950}
1951
1953{
1954 if (IsResetting)
1955 return;
1956
1957 if (ui->treeItems->selectedItems().length() != 1)
1958 {
1960 return;
1961 }
1962
1963 auto Item = ui->treeItems->selectedItems()[0];
1964
1965 if (Item->parent() == ItemTreeHardwareAdapters || Item->parent() == ItemTreeInstruments ||
1966 Item->parent() == ItemTreeModules)
1967 {
1968 ui->action_Reset_Item->setEnabled(true);
1969 ui->action_Configure_Item->setEnabled(true);
1970 ui->action_Delete_Item->setEnabled(true);
1971
1972 ClearWarningAction->setEnabled(true);
1973 }
1974
1975 if (Item->parent() == ItemTreeHardwareAdapters)
1976 {
1977 ui->action_Run_Item->setEnabled(false);
1978 ui->action_Stop_Item->setEnabled(false);
1979 ui->action_Force_Stop_Item->setEnabled(false);
1980
1981 return;
1982 }
1983
1984 if (Item->parent() == ItemTreeInstruments || Item->parent() == ItemTreeModules)
1985 {
1986 const auto ItemData = Item->data(2, Qt::ItemDataRole::UserRole).value<ItemTreeItemDataType>();
1987
1988 bool IsRunning = false;
1989 try
1990 {
1991 if (Item->parent() == ItemTreeInstruments)
1992 IsRunning = DynExpCore.GetInstrumentManager().GetResource(ItemData.ID)->IsRunning();
1993 if (Item->parent() == ItemTreeModules)
1994 IsRunning = DynExpCore.GetModuleManager().GetResource(ItemData.ID)->IsRunning();
1995 }
1996 catch (...)
1997 {
1998 // Swallow especially Util::NotFoundException.
1999 }
2000
2001 ui->action_Run_Item->setEnabled(!IsRunning);
2002 ui->action_Stop_Item->setEnabled(IsRunning);
2003 ui->action_Force_Stop_Item->setEnabled(IsRunning);
2004
2005 return;
2006 }
2007
2009}
2010
2011void DynExpManager::OnItemDoubleClicked(QTreeWidgetItem* Item, int Column)
2012{
2014}
2015
2017{
2018 if (IsResetting)
2019 return;
2020
2021 auto ActiveQModule = GetModuleByActiveUIWindow();
2022
2023 if (ActiveQModule)
2024 {
2025 ui->treeItems->clearSelection();
2026 DynExpCore.GetModuleManager().FocusTreeWidgetItem(ActiveQModule->GetID());
2027 }
2028}
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.
QMenu * LogContextMenu
Context menu appearing when right-clicking on the message log widget.
std::unique_ptr< Ui::DynExpManagerClass > ui
Qt widgets belonging to the main window's user interface.
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:1660
void HideUI()
Removes MdiSubWindow, DockWidget, and ModuleWindowFocusAction setting them to nullptr.
Definition Module.cpp:638
void UpdateModuleWindowFocusAction()
Updates the icon assigned to ModuleWindowFocusAction depending on whether Widget is docked to or undo...
Definition Module.cpp:663
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:317
auto GetLogSize() const
Determines the number of entries in the internal event log.
Definition Util.h:1250
void ClearLog()
Clears the internal event log.
Definition Util.h:1236
std::vector< LogEntry > GetLog(size_t FirstElement=0) const
Returns the internal event log starting from the n-th stored element.
Definition Util.cpp:494
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:100
const ErrorType Type
DynExp error type from Util::ErrorType
Definition Exception.h:106
constexpr const char * GetErrorLabel() const
Definition Exception.h:99
An invalid argument like a null pointer has been passed to a function.
Definition Exception.h:138
Thrown when an operation timed out before it could be completed, especially used for locking shared d...
Definition Exception.h:262
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:688
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:197
EventLogger & EventLog()
This function holds a static EventLogger instance and returns a reference to it. DynExp uses only one...
Definition Util.cpp:517
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