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