signal_collection_widget.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement
4  *
5  * Copyright (c) 2020,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  *
22  * Authors: Christoph Rösmann
23  *********************************************************************/
24 
25 #include <corbo-core/corbo_core.h>
27 #include <corbo-gui/utilities.h>
28 #include <QStack>
29 
30 #include <QLabel>
31 #include <QMessageBox>
32 #include <QMouseEvent>
33 #include <QTreeWidget>
34 
35 #include <tuple>
36 
37 namespace corbo {
38 namespace gui {
39 
40 SignalCollectionWidget::SignalCollectionWidget(SignalHelper::Ptr signal_helper, QWidget* parent) : QWidget(parent)
41 {
42  setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
43 
44  _signal_helper = signal_helper;
45 
46  _main_layout = new QVBoxLayout(this);
47  _main_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
48  _main_layout->setContentsMargins(0, 0, 0, 0);
49 
50  QHBoxLayout* title_layout = new QHBoxLayout;
51  title_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
52  title_layout->setContentsMargins(5, 5, 5, 5);
53  title_layout->addWidget(new QLabel(tr("Available Signals")));
54  title_layout->addWidget(util::create_horizontal_line());
55  _main_layout->addLayout(title_layout);
56 
57  _signal_tree = createSignalTree();
58  _main_layout->addWidget(_signal_tree);
59 
60  _main_layout->addWidget(util::create_horizontal_line());
61  _main_layout->addSpacing(10);
62 }
63 
64 SignalCollectionWidget::~SignalCollectionWidget() {}
65 
66 QSize SignalCollectionWidget::sizeHint() const { return QSize(200, 800); }
67 
68 ExtendedTreeWidget* SignalCollectionWidget::createSignalTree()
69 {
70  ExtendedTreeWidget* signal_tree = new ExtendedTreeWidget;
71  signal_tree->viewport()->setBackgroundRole(QPalette::Background);
72  signal_tree->setAttribute(Qt::WA_MacShowFocusRect, false);
73  // signal_tree->setRootIsDecorated(false);
74  // signal_tree->setColumnCount(1);
75  // signal_tree->setIconSize(QSize(20, 20));
76  signal_tree->setHeaderHidden(true);
77  signal_tree->setIndentation(10);
78  signal_tree->setStyleSheet("QTreeView { border: none; }");
79  signal_tree->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
80  signal_tree->setContextMenuPolicy(Qt::CustomContextMenu);
81  signal_tree->connect(signal_tree, &QTreeWidget::customContextMenuRequested,
82  [this, signal_tree](const QPoint& point) { this->namespaceContextMenu(point, signal_tree); });
83  return signal_tree;
84 }
85 
86 void SignalCollectionWidget::addSignal(const QString& key, SignalHelper::SignalData& signal_data)
87 {
88  _current_task_id = _signal_helper->currentSeriesId();
89 
90  QStringList namespaces = signal_data.namespaces;
91  namespaces.pop_back(); // remove own name
92 
93  QVector<QTreeWidgetItem*> namespace_items;
94 
95  for (const QString& group : namespaces)
96  {
97  // check if we have the current namespace
98  QTreeWidgetItem* current_item = nullptr;
99  if (namespace_items.empty()) // check top-level
100  {
101  QList<QTreeWidgetItem*> items = _signal_tree->findItems(group, Qt::MatchExactly);
102  if (!items.empty()) current_item = items.front();
103  }
104  else
105  {
106  // check children of last group
107  for (int i = 0; i < namespace_items.back()->childCount(); ++i)
108  {
109  if (namespace_items.back()->child(i)->text(0) == group)
110  {
111  current_item = namespace_items.back()->child(i);
112  }
113  }
114  }
115 
116  if (!current_item)
117  {
118  // namespace not yet created
119  QTreeWidgetItem* item = new QTreeWidgetItem;
120  if (namespace_items.empty())
121  _signal_tree->addTopLevelItem(item); // new top-level item
122  else
123  namespace_items.back()->addChild(item); // add as child to previous item
124  item->setText(0, group);
125  namespace_items.push_back(item);
126  }
127  else
128  {
129  // append item to the current item list
130  namespace_items.push_back(current_item);
131  }
132  }
133 
134  // now add signal
135  // first check if we have multiple dimensions
136  int dimension = signal_data.signal->header.value_dimension;
137 
138  if (dimension == 0) return;
139 
140  QTreeWidgetItem* item = new QTreeWidgetItem;
141  if (namespace_items.empty()) // new top-level item
142  _signal_tree->addTopLevelItem(item);
143  else
144  namespace_items.back()->addChild(item);
145 
146  if (dimension == 1)
147  {
148  // add signal name directly
149  SignalWidget* signal_widget = new SignalWidget(signal_data.short_name, key, 0);
150  signal_data.active_indices.insert(0);
151  _signal_tree->setItemWidget(item, 0, signal_widget);
152  }
153  else
154  {
155  // treat signal name as namespace and add 1D-subsignals
156  item->setText(0, signal_data.namespaces.back());
157 
158  for (int i = 0; i < dimension; ++i)
159  {
160  // create 1D-subsignals
161  QTreeWidgetItem* subitem = new QTreeWidgetItem;
162  item->addChild(subitem);
163 
164  // get custom names if available
165  QString sublabel = (i < signal_data.value_labels.size()) ? signal_data.value_labels[i] : signal_data.short_name + QString::number(i);
166  SignalWidget* signal_widget = new SignalWidget(sublabel, key, i);
167  signal_data.active_indices.insert(i);
168  _signal_tree->setItemWidget(subitem, 0, signal_widget);
169  }
170  }
171 
172  _signal_tree->updateSizeHint();
173 }
174 
175 void SignalCollectionWidget::moveToRecent(const QDateTime& dtime)
176 {
177  ExtendedTreeWidget* new_tree = createSignalTree();
178  QLayoutItem* current_tree = _main_layout->replaceWidget(_signal_tree, new_tree, Qt::FindDirectChildrenOnly);
179  _signal_tree = new_tree;
180 
181  // update name in common signal target
182  if (_signal_helper)
183  {
184  QString groupname = "data_" + dtime.toString("yyyy_MM_dd_hh_mm_ss"); // prepend text, e.g., 'data', because otherwise some yaml importer fail
185  _signal_helper->getSignalTreeRef()[_current_task_id].setTopLevelGroupName(groupname.toStdString());
186  }
187 
188  if (current_tree)
189  {
190  QVBoxLayout* layout = new QVBoxLayout;
191  layout->setContentsMargins(15, 0, 0, 0);
192  layout->addItem(current_tree);
193 
194  QString title = dtime.toString("dd.MM.yyyy hh:mm:ss");
195  CollapsableGroupBox* group = new CollapsableGroupBox(title);
196 
197  // add context menu
198  group->setContextMenuPolicy(Qt::CustomContextMenu);
199  int task_id = _current_task_id;
200  group->connect(group, &QTreeWidget::customContextMenuRequested,
201  [this, group, task_id](const QPoint& point) { this->recentGroupContextMenu(point, group, task_id); });
202 
203  group->groupBox()->setLayout(layout);
204 
205  _recent_signals.push_back(std::make_tuple(group, current_tree->widget(), _current_task_id));
206  _main_layout->addWidget(group);
207 
208  ++_current_task_id; // will also be update next time signals are added
209  }
210 }
211 
212 void SignalCollectionWidget::removeTreeItem(const QString& key, int value_idx)
213 {
214  QString name;
215  int id = 0;
216  SignalHelper::key2Name(key, name, id);
217 
218  CollapsableGroupBox* group_box = nullptr;
219  QTreeWidget* tree = nullptr;
220  auto group_it = _recent_signals.end();
221 
222  // search tree and group for current id;
223  if (id == _current_task_id)
224  {
225  tree = _signal_tree;
226  }
227  else
228  {
229  group_it = std::find_if(_recent_signals.begin(), _recent_signals.end(),
230  [id](const std::tuple<CollapsableGroupBox*, QWidget*, int> item) { return id == std::get<2>(item); });
231  if (group_it != _recent_signals.end())
232  {
233  group_box = std::get<0>(*group_it);
234  tree = static_cast<QTreeWidget*>(std::get<1>(*group_it));
235  }
236  }
237 
238  if (!tree) return;
239 
240  // search for current item:
241  QStringList namespaces;
242  SignalHelper::extractNamespace(name, namespaces);
243 
244  // find top-level child;
245  QTreeWidgetItem* item = nullptr;
246  QTreeWidgetItem* candidate = nullptr;
247  bool found = false;
248 
249  // n -> size+1 : also expend last namespace (signal name) in case of multi-value signals
250  for (int n = 0; n < namespaces.size() + 1; ++n)
251  {
252  if (found) break;
253 
254  int no_items;
255  if (n == 0)
256  no_items = tree->topLevelItemCount();
257  else if (item)
258  no_items = item->childCount();
259  else
260  break;
261 
262  for (int i = 0; i < no_items; ++i)
263  {
264  QTreeWidgetItem* child = (n == 0) ? tree->topLevelItem(i) : item->child(i);
265 
266  // check leaf widget
267  SignalWidget* signal_widget = dynamic_cast<SignalWidget*>(tree->itemWidget(child, 0));
268  if (signal_widget)
269  {
270  if (signal_widget->key().compare(key) == 0)
271  {
272  if (signal_widget->valueIdx() == value_idx || value_idx == SignalHelper::ALL_VALUES)
273  {
274  found = true;
275  candidate = child;
276  break;
277  }
278  }
279  }
280 
281  // check namespace
282  if (n < namespaces.size() && child->text(0).compare(namespaces[n]) == 0)
283  {
284  if (namespaces.size() == n + 1)
285  {
286  if (value_idx == SignalHelper::ALL_VALUES)
287  {
288  found = true;
289  candidate = child;
290  break;
291  }
292  }
293  item = child; // expand
294  break;
295  }
296  }
297  }
298 
299  if (!candidate) return;
300 
301  // check if parent has no other children
302  QTreeWidgetItem* parent = candidate->parent();
303  QTreeWidgetItem* parent_candidate = nullptr;
304  while (parent != nullptr)
305  {
306  if (parent->childCount() > 1) break;
307  parent_candidate = parent;
308  parent = parent->parent();
309  }
310  // delete highest parent that does not contain any further child
311  if (parent_candidate)
312  delete parent_candidate;
313  else
314  delete candidate;
315 
316  // check if our tree is empty, in that case erase group box as well
317  if (group_box && group_it != _recent_signals.end() && tree->topLevelItemCount() == 0)
318  {
319  int task_id = std::get<2>(*group_it);
320  group_box->deleteLater();
321  _recent_signals.erase(group_it);
322  emit taskRemoved(task_id);
323  }
324 }
325 
326 void SignalCollectionWidget::getGroupInfo(const QTreeWidget* tree, const QTreeWidgetItem* item, QString* name_out, int* id_out)
327 {
328  // search if member of a "recent" group and extract task_id and group_box pointer
329  int task_id = _current_task_id; // default construct with current_task_id
330  auto group_it = std::find_if(_recent_signals.begin(), _recent_signals.end(),
331  [tree](const std::tuple<CollapsableGroupBox*, QWidget*, int> item) { return tree == std::get<1>(item); });
332  if (group_it != _recent_signals.end()) task_id = std::get<2>(*group_it);
333 
334  if (id_out) *id_out = task_id;
335 
336  // get namespace:
337  if (name_out)
338  {
339  const QTreeWidgetItem* parent = item->parent();
340  QString name = item->text(0);
341  while (parent != nullptr)
342  {
343  name = parent->text(0) + corbo::SIGNAL_NAMESPACE_DELIMITER + name;
344  parent = parent->parent();
345  }
346  *name_out = name;
347  }
348 }
349 
350 void SignalCollectionWidget::removeSignal(QTreeWidget* tree, QTreeWidgetItem* item)
351 {
352  // search if member of a "recent" group and extract task_id and group_box pointer
353  int task_id = _current_task_id; // default construct with current_task_id
354  auto group_it = std::find_if(_recent_signals.begin(), _recent_signals.end(),
355  [tree](const std::tuple<CollapsableGroupBox*, QWidget*, int> item) { return tree == std::get<1>(item); });
356  if (group_it != _recent_signals.end()) task_id = std::get<2>(*group_it);
357 
358  // first remove signals in the signal_helper
359  SignalWidget* signal_widget = dynamic_cast<SignalWidget*>(tree->itemWidget(item, 0));
360  if (signal_widget)
361  {
362  _signal_helper->removeSignal(signal_widget->key(), signal_widget->valueIdx());
363  }
364  else
365  {
366  // get namespace:
367  QTreeWidgetItem* parent = item->parent();
368  QString name = item->text(0);
369  while (parent != nullptr)
370  {
371  name = parent->text(0) + corbo::SIGNAL_NAMESPACE_DELIMITER + name;
372  parent = parent->parent();
373  }
374  _signal_helper->clearNamespace(name, task_id);
375  }
376 }
377 
378 void SignalCollectionWidget::recentGroupContextMenu(const QPoint& point, CollapsableGroupBox* group, int task_id)
379 {
380  if (!group) return;
381 
382  QMenu menu(this);
383 
384  QAction* restore_param_action = new QAction(tr("Restore Parameters"), this);
385  connect(restore_param_action, &QAction::triggered, [this, task_id]() {
386  if (QMessageBox::Yes ==
387  QMessageBox(QMessageBox::Information, "Restore Parameters", "Do you really want to restore parameters?",
388  QMessageBox::Yes | QMessageBox::No)
389  .exec())
390  {
391  emit requestTaskParameterBackup(task_id);
392  }
393  });
394  menu.addAction(restore_param_action);
395 
396  menu.addSeparator();
397 
398  // Export whole group
399  QMenu* menu_export = menu.addMenu(tr("Export to ..."));
400 
401  auto it = _signal_helper->getSignalTreeRef().find(task_id);
402  if (it != _signal_helper->getSignalTreeRef().end())
403  {
404  const CommonSignalTarget::SignalGroup* signal_group = &it->second.getSignals();
405 
406  // add all exporters that support 'SignalGroup export'
407  for (const auto& item : DataExporterInterface::getFactory().getObjectMap())
408  {
409  DataExporterInterface::Ptr exporter_in_factory = item.second;
410  if (exporter_in_factory->isSupportingSignalGroup())
411  {
412  QAction* export_group_action = new QAction(QString::fromStdString(exporter_in_factory->getFormatName()), this);
413  connect(export_group_action, &QAction::triggered, [this, signal_group, exporter_in_factory]() {
414  DataExporterInterface::Ptr exporter =
415  exporter_in_factory->getInstance(); // create new exporter instance since we do not want to modify the one in the factory
416  QString format_name = QString::fromStdString(exporter->getFormatName());
417  QString file_suffix = QString::fromStdString(exporter->getFileSuffix());
418  std::string filename =
419  QFileDialog::getSaveFileName(this, "Save " + format_name, "data" + file_suffix, format_name + " File (* " + file_suffix + ")")
420  .toStdString();
421  exporter->exportSignalGroup(filename, (*signal_group));
422  });
423  menu_export->addAction(export_group_action);
424  }
425  }
426  }
427 
428  menu.addSeparator();
429 
430  QAction* remove_action = new QAction(tr("Remove Signals"), this);
431  connect(remove_action, &QAction::triggered, [this, task_id]() { _signal_helper->clearSeries(task_id); });
432  menu.addAction(remove_action);
433 
434  menu.exec(group->mapToGlobal(point));
435 }
436 
437 void SignalCollectionWidget::namespaceContextMenu(const QPoint& point, QTreeWidget* tree)
438 {
439  if (!tree) return;
440 
441  // skip context menu for available signals, allow only for recent measurements!
442  if (tree == _signal_tree) return;
443 
444  QTreeWidgetItem* item = tree->itemAt(point);
445 
446  if (!item) return;
447 
448  QMenu menu(this);
449 
450  SignalWidget* signal_widget = dynamic_cast<SignalWidget*>(tree->itemWidget(item, 0));
451  if (signal_widget)
452  {
453  // this is a single signal (AND single value of a signal, e.g. a single component of the time-series object)
454  addContextActionsSingleValueSignal(*signal_widget, menu);
455 
456  // add further actions
457  QAction* remove_action = new QAction(tr("Remove Signal"), this);
458  connect(remove_action, &QAction::triggered, [this, tree, item]() { removeSignal(tree, item); });
459  menu.addAction(remove_action);
460  }
461  else
462  {
463  // get namespace:
464  QString name;
465  int id = 0;
466  getGroupInfo(tree, item, &name, &id);
467 
468  // Export whole group
469  QMenu* menu_export = menu.addMenu(tr("Export to ..."));
470 
471  auto it = _signal_helper->getSignalTreeRef().find(id);
472  if (it != _signal_helper->getSignalTreeRef().end())
473  {
474  CommonSignalTarget::SignalGroup* signal_group = it->second.getGroup(name.toStdString());
475  if (signal_group)
476  {
477  // add all exporters that support 'SignalGroup export'
478  for (const auto& item : DataExporterInterface::getFactory().getObjectMap())
479  {
480  DataExporterInterface::Ptr exporter_in_factory = item.second;
481  if (exporter_in_factory->isSupportingSignalGroup())
482  {
483  QAction* export_group_action = new QAction(QString::fromStdString(exporter_in_factory->getFormatName()), this);
484  connect(export_group_action, &QAction::triggered, [this, signal_group, exporter_in_factory]() {
485  DataExporterInterface::Ptr exporter =
486  exporter_in_factory
487  ->getInstance(); // create new exporter instance since we do not want to modify the one in the factory
488  QString format_name = QString::fromStdString(exporter->getFormatName());
489  QString file_suffix = QString::fromStdString(exporter->getFileSuffix());
490  std::string filename = QFileDialog::getSaveFileName(this, "Save " + format_name, "data" + file_suffix,
491  format_name + " File (* " + file_suffix + ")")
492  .toStdString();
493  exporter->exportSignalGroup(filename, (*signal_group));
494  });
495  menu_export->addAction(export_group_action);
496  }
497  }
498  }
499  else
500  {
501  // we might have found a multi-valued signal which is not a group in the signal target
502  SignalInterface::Ptr signal = it->second.getSignal(name.toStdString());
503  if (signal)
504  {
505  addContextActionsMultiValueSignal(signal, menu, *menu_export);
506  }
507  else
508  {
509  PRINT_ERROR_NAMED("Cannot retrieve signal group " << name.toStdString());
510  }
511  }
512  }
513 
514  menu.addSeparator();
515 
516  QAction* remove_action = new QAction(tr("Remove Signals"), this);
517  connect(remove_action, &QAction::triggered, [this, tree, item]() { removeSignal(tree, item); });
518  menu.addAction(remove_action);
519  }
520 
521  menu.exec(tree->mapToGlobal(point));
522 }
523 
524 void SignalCollectionWidget::addContextActionsSingleValueSignal(SignalWidget& signal_widget, QMenu& menu)
525 {
526  // first check if we can access the signal data which we want to modify
527  SignalHelper::SignalData* signal_data = _signal_helper->signalData(signal_widget.key());
528  int signal_value_idx = signal_widget.valueIdx();
529  if (!signal_data) return;
530 
531  // Test for TimeSeriesSignal
532  TimeSeriesSignal::Ptr ts_signal = std::dynamic_pointer_cast<TimeSeriesSignal>(signal_data->signal);
533  if (ts_signal)
534  {
535  addContextActionsTimeSeriesSignal(ts_signal, menu, signal_value_idx);
536  }
537 
538  // Test for TimeSeriesSequenceSignal
539  TimeSeriesSequenceSignal::Ptr ts_seq_signal = std::dynamic_pointer_cast<TimeSeriesSequenceSignal>(signal_data->signal);
540  if (ts_seq_signal)
541  {
542  addContextActionsTimeSeriesSequenceSignal(ts_seq_signal, menu, signal_value_idx);
543  }
544 
545  // Test for IndexedValuesSetSignal
546  IndexedValuesSetSignal::Ptr ivs_signal = std::dynamic_pointer_cast<IndexedValuesSetSignal>(signal_data->signal);
547  if (ivs_signal)
548  {
549  addContextActionsIndexedValuesSetSignal(ivs_signal, menu);
550  }
551 
552  // Test for MatrixSignal
553  MatrixSignal::Ptr matrix_signal = std::dynamic_pointer_cast<MatrixSignal>(signal_data->signal);
554  if (matrix_signal)
555  {
556  addContextActionsMatrixSignal(matrix_signal, menu);
557  }
558 
559  // Test for MatrixSetSignal
560  MatrixSetSignal::Ptr matrix_set_signal = std::dynamic_pointer_cast<MatrixSetSignal>(signal_data->signal);
561  if (matrix_set_signal)
562  {
563  addContextActionsMatrixSetSignal(matrix_set_signal, menu);
564  }
565 }
566 
567 void SignalCollectionWidget::addContextActionsMultiValueSignal(SignalInterface::Ptr signal, QMenu& menu, QMenu& menu_export)
568 {
569  // Test for TimeSeriesSignal
570  TimeSeriesSignal::Ptr ts_signal = std::dynamic_pointer_cast<TimeSeriesSignal>(signal);
571  if (ts_signal)
572  {
573  addContextActionsTimeSeriesSignal(ts_signal, menu, &menu_export);
574  }
575 
576  // Test for TimeSeriesSequenceSignal
577  TimeSeriesSequenceSignal::Ptr ts_seq_signal = std::dynamic_pointer_cast<TimeSeriesSequenceSignal>(signal);
578  if (ts_seq_signal)
579  {
580  addContextActionsTimeSeriesSequenceSignal(ts_seq_signal, menu, &menu_export);
581  }
582 
583  // Test for IndexedValuesSetSignal
584  IndexedValuesSetSignal::Ptr ivs_signal = std::dynamic_pointer_cast<IndexedValuesSetSignal>(signal);
585  if (ivs_signal)
586  {
587  addContextActionsIndexedValuesSetSignal(ivs_signal, menu, &menu_export);
588  }
589 }
590 
591 void SignalCollectionWidget::addContextActionsTimeSeriesSignal(TimeSeriesSignal::Ptr ts_signal, QMenu& menu, int signal_value_idx, QMenu* menu_export)
592 {
593  if (!ts_signal) return;
594 
595  // QMenu* menu_calc = menu.addMenu(tr("Calculations")); // add menu here. We might move that before the signal checks if we add other
596  // calculations
597  QMenu* menu_normalization = menu.addMenu(tr("Normalization"));
598 
599  // normalize by first value
600  QAction* normalize_firstval_action = new QAction(tr("Normalize by First Value"), this);
601  connect(normalize_firstval_action, &QAction::triggered,
602  [ts_signal, signal_value_idx]() { ts_signal->getTimeSeriesRaw()->normalize(TimeSeries::Normalization::FirstValue, signal_value_idx); });
603  menu_normalization->addAction(normalize_firstval_action);
604 
605  // normalize by absolute maximum
606  QAction* normalize_absmax_action = new QAction(tr("Normalize by Absolute Maximum"), this);
607  connect(normalize_absmax_action, &QAction::triggered, [ts_signal, signal_value_idx]() {
608  ts_signal->getTimeSeriesRaw()->normalize(TimeSeries::Normalization::AbsoluteMaximum, signal_value_idx);
609  });
610  menu_normalization->addAction(normalize_absmax_action);
611 
612  // normalize by mean
613  QAction* normalize_mean_action = new QAction(tr("Normalize by Mean"), this);
614  connect(normalize_mean_action, &QAction::triggered,
615  [ts_signal, signal_value_idx]() { ts_signal->getTimeSeriesRaw()->normalize(TimeSeries::Normalization::Mean, signal_value_idx); });
616  menu_normalization->addAction(normalize_mean_action);
617 
618  // add separator after normalziation menu
619  menu.addSeparator();
620 
621  QMenu* menu_export_aux = menu_export ? menu_export : menu.addMenu(tr("Export to ..."));
622 
623  // add all exporters that support 'TimeSeriesSignal export'
624  for (const auto& item : DataExporterInterface::getFactory().getObjectMap())
625  {
626  DataExporterInterface::Ptr exporter_in_factory = item.second;
627  if (exporter_in_factory->isSupportingTimeSeriesSignal())
628  {
629  QAction* export_group_action = new QAction(QString::fromStdString(exporter_in_factory->getFormatName()), this);
630  connect(export_group_action, &QAction::triggered, [this, ts_signal, exporter_in_factory]() {
631  DataExporterInterface::Ptr exporter =
632  exporter_in_factory->getInstance(); // create new exporter instance since we do not want to modify the one in the factory
633  QString format_name = QString::fromStdString(exporter->getFormatName());
634  QString file_suffix = QString::fromStdString(exporter->getFileSuffix());
635  std::string filename =
636  QFileDialog::getSaveFileName(this, "Save " + format_name, "data" + file_suffix, format_name + " File (* " + file_suffix + ")")
637  .toStdString();
638  exporter->exportTimeSeriesSignal(filename, (*ts_signal));
639  });
640  menu_export_aux->addAction(export_group_action);
641  }
642  }
643 }
644 
645 void SignalCollectionWidget::addContextActionsTimeSeriesSignal(TimeSeriesSignal::Ptr ts_signal, QMenu& menu, QMenu* menu_export)
646 {
647  if (!ts_signal) return;
648 
649  QMenu* menu_export_aux = menu_export ? menu_export : menu.addMenu(tr("Export to ..."));
650 
651  // add all exporters that support 'TimeSeriesSignal export'
652  for (const auto& item : DataExporterInterface::getFactory().getObjectMap())
653  {
654  DataExporterInterface::Ptr exporter_in_factory = item.second;
655  if (exporter_in_factory->isSupportingTimeSeriesSignal())
656  {
657  QAction* export_group_action = new QAction(QString::fromStdString(exporter_in_factory->getFormatName()), this);
658  connect(export_group_action, &QAction::triggered, [this, ts_signal, exporter_in_factory]() {
659  DataExporterInterface::Ptr exporter =
660  exporter_in_factory->getInstance(); // create new exporter instance since we do not want to modify the one in the factory
661  QString format_name = QString::fromStdString(exporter->getFormatName());
662  QString file_suffix = QString::fromStdString(exporter->getFileSuffix());
663  std::string filename =
664  QFileDialog::getSaveFileName(this, "Save " + format_name, "data" + file_suffix, format_name + " File (* " + file_suffix + ")")
665  .toStdString();
666  exporter->exportTimeSeriesSignal(filename, (*ts_signal));
667  });
668  menu_export_aux->addAction(export_group_action);
669  }
670  }
671 }
672 
673 void SignalCollectionWidget::addContextActionsTimeSeriesSequenceSignal(TimeSeriesSequenceSignal::Ptr ts_seq_signal, QMenu& menu, int signal_value_idx,
674  QMenu* menu_export)
675 {
676  if (!ts_seq_signal) return;
677 
678  QMenu* menu_export_aux = menu_export ? menu_export : menu.addMenu(tr("Export to ..."));
679 
680  // add all exporters that support 'TimeSeriesSignal export'
681  for (const auto& item : DataExporterInterface::getFactory().getObjectMap())
682  {
683  DataExporterInterface::Ptr exporter_in_factory = item.second;
684  if (exporter_in_factory->isSupportingTimeSeriesSequenceSignal())
685  {
686  QAction* export_group_action = new QAction(QString::fromStdString(exporter_in_factory->getFormatName()), this);
687  connect(export_group_action, &QAction::triggered, [this, ts_seq_signal, exporter_in_factory]() {
688  DataExporterInterface::Ptr exporter =
689  exporter_in_factory->getInstance(); // create new exporter instance since we do not want to modify the one in the factory
690  QString format_name = QString::fromStdString(exporter->getFormatName());
691  QString file_suffix = QString::fromStdString(exporter->getFileSuffix());
692  std::string filename =
693  QFileDialog::getSaveFileName(this, "Save " + format_name, "data" + file_suffix, format_name + " File (* " + file_suffix + ")")
694  .toStdString();
695  exporter->exportTimeSeriesSequenceSignal(filename, (*ts_seq_signal));
696  });
697  menu_export_aux->addAction(export_group_action);
698  }
699  }
700 }
701 
702 void SignalCollectionWidget::addContextActionsTimeSeriesSequenceSignal(TimeSeriesSequenceSignal::Ptr ts_seq_signal, QMenu& menu, QMenu* menu_export)
703 {
704  if (!ts_seq_signal) return;
705 
706  QMenu* menu_export_aux = menu_export ? menu_export : menu.addMenu(tr("Export to ..."));
707 
708  // add all exporters that support 'TimeSeriesSignal export'
709  for (const auto& item : DataExporterInterface::getFactory().getObjectMap())
710  {
711  DataExporterInterface::Ptr exporter_in_factory = item.second;
712  if (exporter_in_factory->isSupportingTimeSeriesSequenceSignal())
713  {
714  QAction* export_group_action = new QAction(QString::fromStdString(exporter_in_factory->getFormatName()), this);
715  connect(export_group_action, &QAction::triggered, [this, ts_seq_signal, exporter_in_factory]() {
716  DataExporterInterface::Ptr exporter =
717  exporter_in_factory->getInstance(); // create new exporter instance since we do not want to modify the one in the factory
718  QString format_name = QString::fromStdString(exporter->getFormatName());
719  QString file_suffix = QString::fromStdString(exporter->getFileSuffix());
720  std::string filename =
721  QFileDialog::getSaveFileName(this, "Save " + format_name, "data" + file_suffix, format_name + " File (* " + file_suffix + ")")
722  .toStdString();
723  exporter->exportTimeSeriesSequenceSignal(filename, (*ts_seq_signal));
724  });
725  menu_export_aux->addAction(export_group_action);
726  }
727  }
728 }
729 
730 void SignalCollectionWidget::addContextActionsIndexedValuesSetSignal(IndexedValuesSetSignal::Ptr ivs_signal, QMenu& menu, QMenu* menu_export)
731 {
732  if (!ivs_signal) return;
733 
734  QMenu* menu_export_aux = menu_export ? menu_export : menu.addMenu(tr("Export to ..."));
735 
736  // add all exporters that support 'IndexedValuesSetSignal export'
737  for (const auto& item : DataExporterInterface::getFactory().getObjectMap())
738  {
739  DataExporterInterface::Ptr exporter_in_factory = item.second;
740  if (exporter_in_factory->isSupportingIndexedValuesSetSignal())
741  {
742  QAction* export_group_action = new QAction(QString::fromStdString(exporter_in_factory->getFormatName()), this);
743  connect(export_group_action, &QAction::triggered, [this, ivs_signal, exporter_in_factory]() {
744  DataExporterInterface::Ptr exporter =
745  exporter_in_factory->getInstance(); // create new exporter instance since we do not want to modify the one in the factory
746  QString format_name = QString::fromStdString(exporter->getFormatName());
747  QString file_suffix = QString::fromStdString(exporter->getFileSuffix());
748  std::string filename =
749  QFileDialog::getSaveFileName(this, "Save " + format_name, "data" + file_suffix, format_name + " File (* " + file_suffix + ")")
750  .toStdString();
751  exporter->exportIndexedValuesSetSignal(filename, (*ivs_signal));
752  });
753  menu_export_aux->addAction(export_group_action);
754  }
755  }
756 }
757 
758 void SignalCollectionWidget::addContextActionsMatrixSignal(MatrixSignal::Ptr mat_signal, QMenu& menu, QMenu* menu_export)
759 {
760  if (!mat_signal) return;
761 
762  QMenu* menu_export_aux = menu_export ? menu_export : menu.addMenu(tr("Export to ..."));
763 
764  // add all exporters that support 'TimeSeriesSignal export'
765  for (const auto& item : DataExporterInterface::getFactory().getObjectMap())
766  {
767  DataExporterInterface::Ptr exporter_in_factory = item.second;
768  if (exporter_in_factory->isSupportingMatrixSignal())
769  {
770  QAction* export_group_action = new QAction(QString::fromStdString(exporter_in_factory->getFormatName()), this);
771  connect(export_group_action, &QAction::triggered, [this, mat_signal, exporter_in_factory]() {
772  DataExporterInterface::Ptr exporter =
773  exporter_in_factory->getInstance(); // create new exporter instance since we do not want to modify the one in the factory
774  QString format_name = QString::fromStdString(exporter->getFormatName());
775  QString file_suffix = QString::fromStdString(exporter->getFileSuffix());
776  std::string filename =
777  QFileDialog::getSaveFileName(this, "Save " + format_name, "data" + file_suffix, format_name + " File (* " + file_suffix + ")")
778  .toStdString();
779  exporter->exportMatrixSignal(filename, (*mat_signal));
780  });
781  menu_export_aux->addAction(export_group_action);
782  }
783  }
784 }
785 
786 void SignalCollectionWidget::addContextActionsMatrixSetSignal(MatrixSetSignal::Ptr mat_set_signal, QMenu& menu, QMenu* menu_export)
787 {
788  if (!mat_set_signal) return;
789 
790  QMenu* menu_export_aux = menu_export ? menu_export : menu.addMenu(tr("Export to ..."));
791 
792  // add all exporters that support 'TimeSeriesSignal export'
793  for (const auto& item : DataExporterInterface::getFactory().getObjectMap())
794  {
795  DataExporterInterface::Ptr exporter_in_factory = item.second;
796  if (exporter_in_factory->isSupportingMatrixSetSignal())
797  {
798  QAction* export_group_action = new QAction(QString::fromStdString(exporter_in_factory->getFormatName()), this);
799  connect(export_group_action, &QAction::triggered, [this, mat_set_signal, exporter_in_factory]() {
800  DataExporterInterface::Ptr exporter =
801  exporter_in_factory->getInstance(); // create new exporter instance since we do not want to modify the one in the factory
802  QString format_name = QString::fromStdString(exporter->getFormatName());
803  QString file_suffix = QString::fromStdString(exporter->getFileSuffix());
804  std::string filename =
805  QFileDialog::getSaveFileName(this, "Save " + format_name, "data" + file_suffix, format_name + " File (* " + file_suffix + ")")
806  .toStdString();
807  exporter->exportMatrixSetSignal(filename, (*mat_set_signal));
808  });
809  menu_export_aux->addAction(export_group_action);
810  }
811  }
812 }
813 
814 } // namespace gui
815 } // namespace corbo
corbo::MatrixSignal::Ptr
std::shared_ptr< MatrixSignal > Ptr
Definition: signals.h:564
corbo::gui::SignalCollectionWidget::SignalCollectionWidget
SignalCollectionWidget(SignalHelper::Ptr signal_helper, QWidget *parent=0)
Definition: signal_collection_widget.cpp:84
corbo::TimeSeries::Normalization::AbsoluteMaximum
@ AbsoluteMaximum
corbo::gui::SignalHelper::SignalData::value_labels
QStringList value_labels
Definition: signal_helper.h:126
PRINT_ERROR_NAMED
#define PRINT_ERROR_NAMED(msg)
Definition: console.h:260
corbo::TimeSeries::Normalization::Mean
@ Mean
signal_collection_widget.h
corbo::gui::SignalHelper::SignalData::namespaces
QStringList namespaces
Definition: signal_helper.h:120
corbo
Definition: communication/include/corbo-communication/utilities.h:37
corbo::SignalInterface::Ptr
std::shared_ptr< SignalInterface > Ptr
Definition: signals.h:158
corbo::gui::SignalWidget::key
QString key() const
Definition: signal_widget.h:112
corbo::gui::SignalWidget::valueIdx
int valueIdx() const
Definition: signal_widget.h:113
corbo::DataExporterInterface::getFactory
static Factory< DataExporterInterface > & getFactory()
Get access to the associated factory.
Definition: data_exporter_interface.h:101
corbo::MatrixSetSignal::Ptr
std::shared_ptr< MatrixSetSignal > Ptr
Definition: signals.h:632
corbo::gui::util::create_horizontal_line
QFrame * create_horizontal_line()
Definition: gui/include/corbo-gui/utilities.h:248
corbo::gui::SignalHelper::SignalData::short_name
QString short_name
Definition: signal_helper.h:118
corbo::gui::SignalHelper::SignalData::active_indices
QSet< int > active_indices
Definition: signal_helper.h:125
utility::tuple::make_tuple
Tuple< Args... > make_tuple(Args... args)
Creates a tuple object, deducing the target type from the types of arguments.
Definition: TensorSyclTuple.h:133
corbo_core.h
relicense.filename
filename
Definition: relicense.py:57
corbo::DataExporterInterface::Ptr
std::shared_ptr< DataExporterInterface > Ptr
Definition: data_exporter_interface.h:92
corbo::SIGNAL_NAMESPACE_DELIMITER
constexpr const char SIGNAL_NAMESPACE_DELIMITER
Definition: signals.h:68
corbo::gui::SignalWidget
Definition: signal_widget.h:80
corbo::IndexedValuesSetSignal::Ptr
std::shared_ptr< IndexedValuesSetSignal > Ptr
Definition: signals.h:499
corbo::TimeSeries::Normalization::FirstValue
@ FirstValue
corbo::gui::SignalHelper::SignalData
Definition: signal_helper.h:116
n
PlainMatrixType mat * n
Definition: eigenvalues.cpp:41
corbo::TimeSeriesSequenceSignal::Ptr
std::shared_ptr< TimeSeriesSequenceSignal > Ptr
Definition: signals.h:358
corbo::gui::SignalHelper::Ptr
std::shared_ptr< SignalHelper > Ptr
Definition: signal_helper.h:111
corbo::gui::SignalHelper::SignalData::signal
SignalInterface::Ptr signal
Definition: signal_helper.h:123
corbo::CommonSignalTarget::SignalGroup
Tree node containing a group of signals and a set of child tree nodes.
Definition: common_signal_target.h:101
utilities.h
corbo::TimeSeriesSignal::Ptr
std::shared_ptr< TimeSeriesSignal > Ptr
Definition: signals.h:269


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Wed Mar 2 2022 00:06:12