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