5 #include <QActionGroup> 7 #include <QCommandLineParser> 9 #include <QDesktopServices> 10 #include <QDomDocument> 11 #include <QDoubleSpinBox> 12 #include <QElapsedTimer> 13 #include <QFileDialog> 14 #include <QInputDialog> 17 #include <QMessageBox> 19 #include <QMouseEvent> 20 #include <QPluginLoader> 21 #include <QPushButton> 22 #include <QKeySequence> 25 #include <QStringListModel> 28 #include <QTextStream> 30 #include <QHeaderView> 41 #include "ui_aboutdialog.h" 42 #include "ui_support_dialog.h" 48 _undo_shortcut(QKeySequence(
Qt::CTRL +
Qt::Key_Z), this),
49 _redo_shortcut(QKeySequence(
Qt::CTRL +
Qt::SHIFT +
Qt::Key_Z), this),
50 _fullscreen_shortcut(
Qt::Key_F10, this),
51 _streaming_shortcut(QKeySequence(
Qt::CTRL +
Qt::Key_Space), this),
52 _playback_shotcut(
Qt::Key_Space, this),
54 _current_streamer(nullptr),
55 _disable_undo_logging(false),
59 QLocale::setDefault(QLocale::c());
68 if( commandline_parser.isSet(
"buffer_size"))
70 int buffer_size = std::max(10, commandline_parser.value(
"buffer_size").toInt() );
71 ui->streamingSpinBox->setMaximum(buffer_size);
75 QIcon icon(
":/icons/resources/light/office_chart_line_stacked.png");
78 this->setWindowIcon(icon);
79 QApplication::setWindowIcon(icon);
107 connect(
ui->playbackRate, &QDoubleSpinBox::editingFinished,
this, [
this]()
109 ui->playbackRate->clearFocus();
118 ui->splitter->setCollapsible(0,
true);
119 ui->splitter->setStretchFactor(0,2);
120 ui->splitter->setStretchFactor(1,6);
122 connect(
ui->splitter, SIGNAL(splitterMoved(
int,
int)), SLOT(
on_splitterMoved(
int,
int)) );
138 _publish_timer->setInterval(20);
141 ui->menuFile->setToolTipsVisible(
true);
142 ui->horizontalSpacer->changeSize(0,0, QSizePolicy::Fixed, QSizePolicy::Fixed);
143 ui->streamingLabel->setHidden(
true);
144 ui->streamingSpinBox->setHidden(
true);
146 this->setMenuBar(
ui->menuBar);
147 ui->menuBar->setNativeMenuBar(
false);
151 connect(
ui->actionLoadDummyData, &QAction::triggered,
156 ui->actionLoadDummyData->setVisible(
false);
159 bool file_loaded =
false;
160 if( commandline_parser.isSet(
"datafile") )
162 QStringList datafiles = commandline_parser.values(
"datafile");
165 if( commandline_parser.isSet(
"layout"))
171 restoreGeometry(settings.value(
"MainWindow.geometry").toByteArray());
173 bool activate_grid = settings.value(
"MainWindow.activateGrid",
false).toBool();
174 ui->pushButtonActivateGrid->setChecked(activate_grid);
176 int streaming_buffer_value = settings.value(
"MainWindow.streamingBufferValue", 5).toInt();
177 ui->streamingSpinBox->setValue(streaming_buffer_value);
179 bool datetime_display = settings.value(
"MainWindow.dateTimeDisplay",
false).toBool();
180 ui->pushButtonUseDateTime->setChecked( datetime_display );
182 bool remove_time_offset = settings.value(
"MainWindow.removeTimeOffset",
true).toBool();
183 ui->pushButtonRemoveTimeOffset->setChecked(remove_time_offset);
185 ui->widgetOptions->setVisible(
ui->pushButtonOptions->isChecked() );
186 ui->line->setVisible(
ui->pushButtonOptions->isChecked() );
189 QIcon trackerIconA, trackerIconB, trackerIconC;
191 trackerIconA.addFile(QStringLiteral(
":/icons/resources/light/line_tracker.png"), QSize(36, 36));
192 trackerIconB.addFile(QStringLiteral(
":/icons/resources/light/line_tracker_1.png"), QSize(36, 36));
193 trackerIconC.addFile(QStringLiteral(
":/icons/resources/light/line_tracker_a.png"), QSize(36, 36));
199 int tracker_setting = settings.value(
"MainWindow.timeTrackerSetting", (
int)
CurveTracker::VALUE ).toInt();
221 if( elapsed_ms < 100)
278 const int vertical_height = table_view->visibleRegion().boundingRect().height();
282 int vertical_pos = table_view->rowViewportPosition(row);
283 if( vertical_pos < 0 || table_view->isRowHidden(row) ){
continue; }
284 if( vertical_pos > vertical_height){
break; }
286 const std::string&
name = table_model->item(row,0)->text().toStdString();
290 auto&
data = it->second;
303 else if(
data.size() > 0)
310 QString num_text = QString::number( num,
'f', 3);
311 if(num_text.contains(
'.'))
313 int idx = num_text.length() -1;
314 while( num_text[idx] ==
'0' )
319 if( num_text[idx] ==
'.') num_text[idx] =
' ';
321 table_model->item(row,1)->setText(num_text +
' ');
333 auto prev =
ui->timeSlider->blockSignals(
true);
335 ui->timeSlider->blockSignals(prev);
353 it.second->updateState( absolute_time);
368 if( suggest_win_name.isEmpty())
372 suggest_win_name = QString(
"Window%1").arg(
i);
384 connect( window, SIGNAL(destroyed(QObject*)),
this, SLOT(
onUndoableChange()) );
388 window->setAttribute( Qt::WA_DeleteOnClose,
true );
390 window->activateWindow();
406 connect( &
_playback_shotcut, &QShortcut::activated,
ui->pushButtonPlay, &QPushButton::toggle );
409 QShortcut* open_menu_shortcut =
new QShortcut(QKeySequence(Qt::ALT + Qt::Key_F),
this);
410 connect( open_menu_shortcut, &QShortcut::activated, [
this](){
411 ui->menuFile->exec(
ui->menuBar->mapToGlobal(QPoint(0,25)));
414 QShortcut* open_streaming_shortcut =
new QShortcut(QKeySequence(Qt::ALT + Qt::Key_S),
this);
415 connect( open_streaming_shortcut, &QShortcut::activated, [
this](){
416 ui->menuStreaming->exec(
ui->menuBar->mapToGlobal(QPoint(50,25)));
419 QShortcut* open_publish_shortcut =
new QShortcut(QKeySequence(Qt::ALT + Qt::Key_P),
this);
420 connect( open_publish_shortcut, &QShortcut::activated, [
this](){
421 ui->menuPublishers->exec(
ui->menuBar->mapToGlobal(QPoint(140,25)));
424 QShortcut* open_help_shortcut =
new QShortcut(QKeySequence(Qt::ALT + Qt::Key_H),
this);
425 connect( open_help_shortcut, &QShortcut::activated, [
this](){
426 ui->menuHelp->exec(
ui->menuBar->mapToGlobal(QPoint(230,25)));
438 static std::set<QString> loaded_plugins;
440 QDir pluginsDir( directory_name );
442 for (
const QString&
filename: pluginsDir.entryList(QDir::Files))
445 if( fileinfo.suffix() !=
"so" && fileinfo.suffix() !=
"dll" && fileinfo.suffix() !=
"dylib"){
449 if( loaded_plugins.find(
filename ) != loaded_plugins.end())
454 QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(
filename),
this);
456 QObject *plugin = pluginLoader.instance();
464 if( loader ) plugin_name = loader->
name();
465 if( publisher ) plugin_name = publisher->name();
466 if( streamer ) plugin_name = streamer->name();
467 plugin_name.replace(
" ",
"_");
469 if( loaded_plugins.find(plugin_name) == loaded_plugins.end())
471 loaded_plugins.insert( plugin_name );
474 QMessageBox::warning(
this, tr(
"Warning"),
475 tr(
"Trying to load twice a plugin with name [%1].\n" 476 "Only the first will be loaded.").
arg(plugin_name) );
482 qDebug() <<
filename <<
": is a DataLoader plugin";
485 qDebug() <<
filename <<
"...but will be ignored unless the argument -t is used.";
488 _data_loader.insert( std::make_pair( plugin_name, loader) );
494 qDebug() <<
filename <<
": is a StatePublisher plugin";
497 qDebug() <<
filename <<
"...but will be ignored unless the argument -t is used.";
501 ui->menuPublishers->setEnabled(
true);
504 QAction* activatePublisher =
new QAction(tr(
"Start: ") + plugin_name ,
this);
505 activatePublisher->setProperty(
"starter_button",
true);
506 activatePublisher->setCheckable(
true);
507 activatePublisher->setChecked(
false);
509 ui->menuPublishers->addSeparator();
510 ui->menuPublishers->addSection(plugin_name);
511 ui->menuPublishers->addAction(activatePublisher);
512 publisher->setParentMenu(
ui->menuPublishers, activatePublisher );
514 connect(activatePublisher, &QAction::toggled,
517 publisher->setEnabled( enable );
523 qDebug() <<
filename <<
": is a DataStreamer plugin";
526 qDebug() <<
filename <<
"...but will be ignored unless the argument -t is used.";
531 QAction* startStreamer =
new QAction(QString(
"Start: ") + plugin_name,
this);
532 ui->menuStreaming->setEnabled(
true);
533 ui->menuStreaming->addAction(startStreamer);
535 streamer->addActionsToParentMenu(
ui->menuStreaming );
536 ui->menuStreaming->addSeparator();
538 connect(startStreamer, &QAction::triggered,
this, [
this, plugin_name]()
545 ui->actionStopStreaming, &QAction::trigger );
553 if( pluginLoader.errorString().contains(
"is not an ELF object") ==
false)
555 qDebug() <<
filename <<
": " << pluginLoader.errorString();
565 static int count = 0;
569 QStringList words_list;
570 words_list <<
"world/siam" <<
"world/tre" <<
"walk/piccoli" <<
"walk/porcellin" 571 <<
"fly/high/mai" <<
"fly/high/nessun" <<
"fly/low/ci" <<
"fly/low/dividera" 572 <<
"data_1" <<
"data_2" <<
"data_3" <<
"data_10";
574 for(
int i=0;
i<10;
i++)
576 words_list.append(QString(
"data_vect/%1").
arg(count++));
579 for(
const QString&
name: words_list)
581 double A = 6* ((double)qrand()/(double)RAND_MAX) - 3;
582 double B = 3* ((double)qrand()/(double)RAND_MAX) ;
583 double C = 3* ((double)qrand()/(double)RAND_MAX) ;
584 double D = 20* ((double)qrand()/(double)RAND_MAX) ;
590 for (
unsigned indx=0; indx<SIZE; indx++)
593 plot.
pushBack( PlotData::Point( t+35, A*sin(B*t + C) + D*t*0.02 ) ) ;
601 for (
unsigned indx=0; indx<SIZE; indx++)
604 sin_plot.
pushBack( PlotData::Point( t+20, sin(t*0.4) ) ) ;
605 cos_plot.
pushBack( PlotData::Point( t+20, cos(t*0.4) ) ) ;
613 QList<int> sizes =
ui->splitter->sizes();
615 int totalWidth = sizes[0] + sizes[1];
617 if( sizes[0] > maxLeftWidth)
619 sizes[0] = maxLeftWidth;
620 sizes[1] = totalWidth - maxLeftWidth;
621 ui->splitter->setSizes(sizes);
653 plot, SLOT(on_changeTimeOffset(
double)) );
655 connect(
ui->pushButtonUseDateTime, &QPushButton::toggled,
677 QDomProcessingInstruction instr =
678 doc.createProcessingInstruction(
"xml",
"version='1.0' encoding='UTF-8'");
680 doc.appendChild(instr);
682 QDomElement root = doc.createElement(
"root" );
686 QDomElement tabbed_area = it.second->xmlSaveState(doc);
687 root.appendChild( tabbed_area );
690 doc.appendChild(root);
692 QDomElement relative_time = doc.createElement(
"use_relative_time_offset" );
693 relative_time.setAttribute(
"enabled",
ui->pushButtonRemoveTimeOffset->isChecked() );
694 root.appendChild( relative_time );
701 std::set<std::string> curves;
703 for ( QDomElement tw = root.firstChildElement(
"tabbed_widget" ) ;
704 !tw.isNull(); tw = tw.nextSiblingElement(
"tabbed_widget" ) )
706 for ( QDomElement pm = tw.firstChildElement(
"plotmatrix" ) ;
707 !pm.isNull(); pm = pm.nextSiblingElement(
"plotmatrix" ) )
709 for ( QDomElement pl = pm.firstChildElement(
"plot" ) ;
710 !pl.isNull(); pl = pl.nextSiblingElement(
"plot" ) )
712 for ( QDomElement cv = pl.firstChildElement(
"curve" ) ;
713 !cv.isNull(); cv = cv.nextSiblingElement(
"curve" ) )
715 curves.insert( cv.attribute(
"name").toStdString() );
721 std::vector<std::string> missing_curves;
723 for (
auto& curve_name: curves)
727 missing_curves.push_back(curve_name);
730 if( missing_curves.size() > 0 )
732 QMessageBox msgBox(
this);
733 msgBox.setWindowTitle(
"Warning");
734 msgBox.setText(tr(
"One or more timeseries in the layout haven't been loaded yet\n" 735 "What do you want to do?"));
737 QPushButton* buttonRemove = msgBox.addButton(tr(
"Remove curves from plots"), QMessageBox::RejectRole);
738 QPushButton* buttonPlaceholder = msgBox.addButton(tr(
"Create empty placeholders"), QMessageBox::YesRole);
739 msgBox.setDefaultButton(buttonPlaceholder);
741 if( msgBox.clickedButton() == buttonPlaceholder )
743 for(
auto&
name: missing_curves )
755 QDomElement root = state_document.namedItem(
"root").toElement();
756 if ( root.isNull() ) {
757 qWarning() <<
"No <root> element found at the top-level of the XML file!";
761 size_t num_floating = 0;
762 std::map<QString,QDomElement> tabbed_widgets_with_name;
764 for (QDomElement tw = root.firstChildElement(
"tabbed_widget" ) ;
765 tw.isNull() ==
false;
766 tw = tw.nextSiblingElement(
"tabbed_widget" ) )
768 if( tw.attribute(
"parent") != (
"main_window") )
772 tabbed_widgets_with_name[ tw.attribute(
"name") ] = tw;
776 for(
const auto& it: tabbed_widgets_with_name)
787 if( tabbed_widgets_with_name.count( it.first ) == 0)
789 it.second->deleteLater();
797 for ( QDomElement tw = root.firstChildElement(
"tabbed_widget" ) ;
798 tw.isNull() ==
false;
799 tw = tw.nextSiblingElement(
"tabbed_widget" ) )
805 QDomElement relative_time = root.firstChildElement(
"use_relative_time_offset" );
806 if( !relative_time.isNull())
808 bool remove_offset = (relative_time.attribute(
"enabled") == QString(
"1"));
809 ui->pushButtonRemoveTimeOffset->setChecked(remove_offset);
817 for(
const auto& curve_name: curve_names )
848 QMenu* menu =
ui->menuRecentData;
850 QAction* separator =
nullptr;
851 QStringList prev_filenames;
852 for (QAction *
action: menu->actions())
854 if (
action->isSeparator() )
859 if(new_filenames.contains(
action->text() ) ==
false)
861 prev_filenames.push_back(
action->text() );
863 menu->removeAction(
action );
866 new_filenames.append( prev_filenames );
867 while( new_filenames.size() > 10 )
869 new_filenames.removeLast();
872 for (
const auto&
filename: new_filenames)
875 connect( action, &QAction::triggered,
this, [
this,
filename]
879 menu->insertAction(separator, action );
883 settings.setValue(
"MainWindow.recentlyLoadedDatafile", new_filenames );
884 menu->setEnabled( new_filenames.size() > 0 );
889 QMenu* menu =
ui->menuRecentLayout;
891 QAction* separator =
nullptr;
892 QStringList prev_filenames;
893 for (QAction *
action: menu->actions())
895 if (
action->isSeparator() )
900 if(new_filenames.contains(
action->text() ) ==
false)
902 prev_filenames.push_back(
action->text() );
904 menu->removeAction(
action );
907 new_filenames.append( prev_filenames );
908 while( new_filenames.size() > 10 )
910 new_filenames.removeLast();
913 for (
const auto&
filename: new_filenames)
916 connect( action, &QAction::triggered,
this, [
this,
filename]
923 menu->insertAction(separator, action );
927 settings.setValue(
"MainWindow.recentlyLoadedLayout", new_filenames );
928 menu->setEnabled( new_filenames.size() > 0 );
944 bool stopped =
false;
945 for (QAction*
action:
ui->menuPublishers->actions())
947 auto is_start_button =
action->property(
"starter_button");
948 if( is_start_button.isValid() && is_start_button.toBool() &&
action->isChecked() )
950 action->setChecked(
false );
957 QMessageBox::warning(
this,
"State publishers stopped",
958 "All the state publishers have been stopped because old data has been deleted.");
963 template <
typename T>
965 std::unordered_map<std::string,T>& destination,
968 for (
auto& it: source)
970 const std::string&
name = it.first;
971 T& source_plot = it.second;
972 auto plot_with_same_name = destination.find(name);
975 if( plot_with_same_name == destination.end() )
977 plot_with_same_name = destination.emplace( std::piecewise_construct,
978 std::forward_as_tuple(name),
979 std::forward_as_tuple(name)
982 T& destination_plot = plot_with_same_name->second;
986 double max_range_x = destination_plot.maximumRangeX();
987 destination_plot.swapData(source_plot);
988 destination_plot.setMaximumRangeX(max_range_x);
992 for (
size_t i=0;
i< source_plot.size();
i++)
994 destination_plot.pushBack( source_plot.at(
i) );
1010 std::vector<std::string> old_plots_to_delete;
1015 if( new_data.
numeric.count( it.first ) == 0 )
1017 old_plots_to_delete.push_back( it.first );
1021 if( !old_plots_to_delete.empty() )
1023 QMessageBox::StandardButton reply;
1024 reply = QMessageBox::question(
this, tr(
"Warning"),
1025 tr(
"Do you want to remove the previously loaded data?\n"),
1026 QMessageBox::Yes | QMessageBox::No,
1028 if( reply == QMessageBox::Yes )
1035 bool curvelist_modified =
false;
1036 for (
auto& it: new_data.
numeric)
1038 const std::string&
name = it.first;
1042 curvelist_modified =
true;
1049 if( curvelist_modified )
1062 if( filenames.size() > 1 )
1064 static bool show_me =
true;
1067 msgbox.setWindowTitle(
"Loading multiple files");
1068 msgbox.setText(
"You are loading multiple files at once. A prefix will be automatically added to the name of the timeseries.\n\n" 1069 "This is an experimental feature. Publishers will not work as you may expect.");
1070 msgbox.addButton(QMessageBox::Ok);
1077 char prefix_ch =
'A';
1078 QStringList loaded_filenames;
1080 for(
const auto&
filename: filenames)
1084 if( filenames.size() > 1 )
1091 loaded_filenames.push_back(
filename);
1095 if( loaded_filenames.size() > 0 )
1105 const QString extension = QFileInfo(info.
filename).suffix().toLower();
1107 typedef std::map<QString,DataLoader*>::iterator MapIterator;
1109 std::vector<MapIterator> compatible_loaders;
1116 for(
auto& ext: extensions){
1118 if( extension == QString(ext).toLower()){
1119 compatible_loaders.push_back( it );
1127 if( compatible_loaders.size() == 1)
1129 dataloader = compatible_loaders.front()->second;
1132 static QString last_plugin_name_used;
1135 for (
auto& cl: compatible_loaders)
1137 const auto&
name = cl->first;
1139 if(
name == last_plugin_name_used ){
1140 names.push_front(
name );
1143 names.push_back(
name );
1148 QString plugin_name = QInputDialog::getItem(
this, tr(
"QInputDialog::getItem()"),
1149 tr(
"Select the loader to use:"),
1150 names, 0,
false, &ok);
1151 if (ok && !plugin_name.isEmpty())
1154 last_plugin_name_used = plugin_name;
1162 if (!file.open(QFile::ReadOnly | QFile::Text)) {
1163 QMessageBox::warning(
this, tr(
"Datafile"),
1164 tr(
"Cannot read file %1:\n%2.")
1166 .arg(file.errorString()));
1187 catch(std::exception &ex)
1189 QMessageBox::warning(
this, tr(
"Exception from the plugin"),
1190 tr(
"The plugin [%1] thrown the following exception: \n\n %3\n")
1196 QMessageBox::warning(
this, tr(
"Error"),
1197 tr(
"Cannot read files with extension %1.\n No plugin can handle that!\n")
1202 ui->timeSlider->setRealValue(
ui->timeSlider->getMinimum() );
1218 qDebug() <<
"Error, no streamer loaded";
1234 qDebug() <<
"Error. The streamer " << streamer_name <<
1240 bool started =
false;
1245 catch(std::runtime_error& err)
1247 QMessageBox::warning(
this, tr(
"Exception from the plugin"),
1248 tr(
"The plugin thrown the following exception: \n\n %1\n")
1259 for(
auto&
action:
ui->menuStreaming->actions()) {
1260 action->setEnabled(
false);
1262 ui->actionClearBuffer->setEnabled(
true);
1264 ui->actionStopStreaming->setEnabled(
true);
1265 ui->actionDeleteAllData->setToolTip(
"Stop streaming to be able to delete the data");
1267 ui->pushButtonStreaming->setEnabled(
true);
1268 ui->pushButtonStreaming->setChecked(
true);
1269 ui->pushButtonRemoveTimeOffset->setEnabled(
false );
1274 qDebug() <<
"Failed to launch the streamer";
1280 QDomElement plugins = root.firstChildElement(
"Plugins");
1282 for ( QDomElement plugin_elem = plugins.firstChildElement() ;
1283 plugin_elem.isNull() ==
false;
1284 plugin_elem = plugin_elem.nextSiblingElement() )
1286 const QString plugin_name = plugin_elem.attribute(
"ID");
1288 if( plugin_elem.nodeName() !=
"plugin" || plugin_name.isEmpty() )
1290 QMessageBox::warning(
this, tr(
"Error loading Plugin State from Layout"),
1291 tr(
"The method xmlSaveState() must return a node line this <plugin ID=\"PluginName\" ") );
1296 _data_loader[plugin_name]->xmlLoadState( plugin_elem );
1317 QDomElement list_plugins = doc.createElement(
"Plugins" );
1319 auto CheckValidFormat = [
this](
const QString& expected_name,
const QDomElement& elem)
1321 if( elem.nodeName() !=
"plugin" || elem.attribute(
"ID") != expected_name )
1323 QMessageBox::warning(
this, tr(
"Error saving Plugin State to Layout"),
1324 tr(
"[%1] The method xmlSaveState() must return a node line this <plugin ID=\"PluginName\">")
1325 .
arg(expected_name) );
1332 QDomElement plugin_elem = dataloader->
xmlSaveState(doc);
1333 if( !plugin_elem.isNull() )
1335 list_plugins.appendChild( plugin_elem );
1336 CheckValidFormat( it.first, plugin_elem );
1343 QDomElement plugin_elem = datastreamer->
xmlSaveState(doc);
1344 if( !plugin_elem.isNull() )
1346 list_plugins.appendChild( plugin_elem );
1347 CheckValidFormat( it.first, plugin_elem );
1354 QDomElement plugin_elem = state_publisher->
xmlSaveState(doc);
1355 if( !plugin_elem.isNull() )
1357 list_plugins.appendChild( plugin_elem );
1358 CheckValidFormat( it.first, plugin_elem );
1361 plugin_elem.setAttribute(
"status", state_publisher->
enabled() ?
"active" :
"idle");
1364 return list_plugins;
1370 double min_time = std::numeric_limits<double>::max();
1371 double max_time = -std::numeric_limits<double>::max();
1378 const auto& curve_name = it.first;
1383 const double t0 =
data.front().x;
1384 const double t1 =
data.back().x;
1385 min_time = std::min( min_time, t0);
1386 max_time = std::max( max_time, t1);
1387 max_steps = std::max( max_steps, (
int)
data.size());
1393 if( max_steps == 0 || max_time < min_time)
1400 const double t0 = data.
front().
x;
1401 const double t1 = data.
back().
x;
1402 min_time = std::min( min_time, t0);
1403 max_time = std::max( max_time, t1);
1404 max_steps = std::max( max_steps, (
int)data.
size());
1410 if( max_steps == 0 || max_time < min_time)
1416 return std::tuple<double,double,int>( min_time, max_time, max_steps );
1425 QFile file(filename);
1426 if (!file.open(QFile::ReadOnly | QFile::Text)) {
1427 QMessageBox::warning(
this, tr(
"Layout"),
1428 tr(
"Cannot read file %1:\n%2.")
1430 .
arg(file.errorString()));
1435 int errorLine, errorColumn;
1437 QDomDocument domDocument;
1439 if (!domDocument.setContent(&file,
true, &errorStr, &errorLine, &errorColumn)) {
1440 QMessageBox::information(window(), tr(
"XML Layout"),
1441 tr(
"Parse error at line %1:\n%2")
1449 QDomElement root = domDocument.namedItem(
"root").toElement();
1451 if( !root.hasAttribute(
"version") && root.attribute(
"version") !=
LAYOUT_VERSION )
1453 QMessageBox::warning(
this, tr(
"Wrong Layout version"),
1454 tr(
"This Layout ID is not supported [%1].\nThis version of PlotJuggler use Layout ID [%2]")
1455 .
arg(root.attribute(
"version"))
1462 QDomElement previously_loaded_datafile = root.firstChildElement(
"previouslyLoaded_Datafiles" );
1464 QDomElement datafile_elem = previously_loaded_datafile.firstChildElement(
"fileInfo" );
1465 while( !datafile_elem.isNull() )
1468 info.
filename = datafile_elem.attribute(
"filename");
1469 info.
prefix = datafile_elem.attribute(
"prefix");
1471 QDomElement datasources_elem = datafile_elem.firstChildElement(
"selected_datasources" );
1472 QString topics_list = datasources_elem.attribute(
"value");
1475 auto plugin_elem = datafile_elem.firstChildElement(
"plugin" );
1479 datafile_elem = datafile_elem.nextSiblingElement(
"fileInfo" );
1482 QDomElement previousl_streamer = root.firstChildElement(
"previouslyLoaded_Streamer" );
1483 if( !previousl_streamer.isNull() )
1485 QString streamer_name = previousl_streamer.attribute(
"name");
1487 QMessageBox msgBox(
this);
1488 msgBox.setWindowTitle(
"Start Streaming?");
1489 msgBox.setText(tr(
"Start the previously used streaming plugin?\n\n %1 \n\n").
arg(streamer_name));
1490 QPushButton* yes = msgBox.addButton(tr(
"Yes"), QMessageBox::YesRole);
1491 QPushButton* no = msgBox.addButton(tr(
"No"), QMessageBox::RejectRole);
1492 msgBox.setDefaultButton(yes);
1495 if( msgBox.clickedButton() == yes )
1502 QMessageBox::warning(
this, tr(
"Error Loading Streamer"),
1503 tr(
"The streamer named %1 can not be loaded.").
arg(streamer_name));
1509 QDomElement plugins = root.firstChildElement(
"Plugins");
1513 for ( QDomElement plugin_elem = plugins.firstChildElement() ;
1514 plugin_elem.isNull() ==
false;
1515 plugin_elem = plugin_elem.nextSiblingElement() )
1517 const QString plugin_name = plugin_elem.nodeName();
1522 if( plugin_elem.attribute(
"status") ==
"active" )
1530 auto custom_equations = root.firstChildElement(
"customMathEquations" );
1533 if( !custom_equations.isNull() )
1535 for (QDomElement custom_eq = custom_equations.firstChildElement(
"snippet" ) ;
1536 custom_eq.isNull() ==
false;
1537 custom_eq = custom_eq.nextSiblingElement(
"snippet" ) )
1540 const auto&
name = new_custom_plot->name();
1548 catch( std::runtime_error& err)
1550 QMessageBox::warning(
this, tr(
"Exception"),
1551 tr(
"Failed to refresh a customMathEquation \n\n %1\n")
1555 QByteArray snippets_saved_xml = settings.value(
"AddCustomPlotDialog.savedXML",
1556 QByteArray() ).toByteArray();
1558 auto snippets_element = root.firstChildElement(
"snippets");
1559 if( !snippets_element.isNull() )
1564 bool snippets_are_different =
false;
1565 for(
const auto& snippet_it : snippets_layout)
1567 auto prev_it = snippets_previous.find( snippet_it.first );
1569 if( prev_it == snippets_previous.end() ||
1570 prev_it->second.equation != snippet_it.second.equation ||
1571 prev_it->second.globalVars != snippet_it.second.globalVars)
1573 snippets_are_different =
true;
1578 if( snippets_are_different )
1580 QMessageBox msgBox(
this);
1581 msgBox.setWindowTitle(
"Overwrite custom transforms?");
1582 msgBox.setText(
"Your layour file contains a set of custom transforms different from " 1583 "the last one you used.\nant to load these transformations?");
1584 msgBox.addButton(QMessageBox::No);
1585 msgBox.addButton(QMessageBox::Yes);
1586 msgBox.setDefaultButton(QMessageBox::Yes);
1588 if( msgBox.exec() == QMessageBox::Yes )
1590 for(
const auto& snippet_it : snippets_layout)
1592 snippets_previous[snippet_it.first] = snippet_it.second;
1595 auto snippets_root_element =
ExportSnippets( snippets_previous, doc);
1596 doc.appendChild( snippets_root_element );
1597 settings.setValue(
"AddCustomPlotDialog.savedXML", doc.toByteArray(2));
1641 auto func = [&](QTabWidget * tabs)
1643 for (
int t=0; t < tabs->count(); t++)
1647 for(
unsigned row=0; row< matrix->
rowsCount(); row++)
1649 for(
unsigned col=0; col< matrix->
colsCount(); col++)
1652 operation(plot, matrix, row, col);
1660 func( it.second->tabWidget() );
1673 ui->timeSlider->setLimits(std::get<0>(range),
1675 std::get<2>(range));
1678 _tracker_time = std::min( _tracker_time,
ui->timeSlider->getMaximum() );
1684 double min_time = std::get<0>(range);
1686 const bool remove_offset =
ui->pushButtonRemoveTimeOffset->isChecked();
1687 if( remove_offset && min_time != std::numeric_limits<double>::max())
1698 if( !source || !destination )
return;
1707 if( plot == source ) {
1708 src_matrix = matrix;
1709 src_pos.setX( row );
1710 src_pos.setY( col );
1712 else if( plot == destination )
1714 dst_matrix = matrix;
1715 dst_pos.setX( row );
1716 dst_pos.setY( col );
1720 if(src_matrix && dst_matrix)
1722 src_matrix->
gridLayout()->removeWidget( source );
1723 dst_matrix->
gridLayout()->removeWidget( destination );
1725 src_matrix->
gridLayout()->addWidget( destination, src_pos.x(), src_pos.y() );
1726 dst_matrix->
gridLayout()->addWidget( source, dst_pos.x(), dst_pos.y() );
1729 if( src_matrix != dst_matrix){
1745 ui->pushButtonRemoveTimeOffset->setEnabled( !streaming );
1749 ui->horizontalSpacer->changeSize(1,1, QSizePolicy::Expanding, QSizePolicy::Fixed);
1750 ui->pushButtonStreaming->setText(
"Streaming ON");
1754 ui->horizontalSpacer->changeSize(0,0, QSizePolicy::Fixed, QSizePolicy::Fixed);
1755 ui->pushButtonStreaming->setText(
"Streaming OFF");
1757 ui->streamingLabel->setHidden( !streaming );
1758 ui->streamingSpinBox->setHidden( !streaming );
1759 ui->timeSlider->setHidden( streaming );
1760 ui->pushButtonPlay->setHidden( streaming );
1762 if( streaming &&
ui->pushButtonPlay->isChecked() )
1764 ui->pushButtonPlay->setChecked(
false);
1787 if(
ui->pushButtonStreaming->isEnabled() )
1789 bool streaming =
ui->pushButtonStreaming->isChecked();
1790 ui->pushButtonStreaming->setChecked( !streaming );
1793 if(
ui->pushButtonPlay->isEnabled() )
1795 bool playing =
ui->pushButtonPlay->isChecked();
1796 ui->pushButtonPlay->setChecked( !playing );
1809 for(
const auto& str: curvelist_added)
1814 if( curvelist_added.size() > 0 )
1836 if( is_streaming_active )
1839 double max_time = std::get<1>(range);
1851 if( replot_hidden_tabs )
1853 QTabWidget* tabs = it.second->tabWidget();
1854 for (
int index=0; index < tabs->count(); index++)
1861 PlotMatrix* matrix = it.second->currentTab() ;
1869 double real_value = value;
1870 if ( value ==
ui->streamingSpinBox->maximum())
1872 real_value = std::numeric_limits<double>::max();
1873 ui->streamingSpinBox->setStyleSheet(
"QSpinBox { color: red; }");
1874 ui->streamingSpinBox->setSuffix(
"=inf");
1877 ui->streamingSpinBox->setStyleSheet(
"QSpinBox { color: black; }");
1878 ui->streamingSpinBox->setSuffix(
" sec");
1888 it.second.setMaximumRangeX( real_value );
1893 it.second.setMaximumRangeX( real_value );
1904 ui->pushButtonStreaming->setChecked(
false);
1905 ui->pushButtonStreaming->setEnabled(
false);
1910 for(
auto&
action:
ui->menuStreaming->actions()) {
1911 action->setEnabled(
true);
1913 ui->actionStopStreaming->setEnabled(
false);
1916 ui->actionDeleteAllData->setToolTip(
"");
1922 it.second.setMaximumRangeX( std::numeric_limits<double>::max() );
1926 it.second.setMaximumRangeX( std::numeric_limits<double>::max() );
1952 ui->widgetOptions->setVisible( checked );
1953 ui->line->setVisible( checked );
1958 QLineEdit* timeLine =
ui->displayTime;
1960 if(
ui->pushButtonUseDateTime->isChecked() )
1962 if(
ui->pushButtonRemoveTimeOffset->isChecked() )
1964 QTime time = QTime::fromMSecsSinceStartOfDay( std::round(relative_time*1000.0));
1965 timeLine->setText( time.toString(
"HH:mm::ss.zzz") );
1968 QDateTime datetime = QDateTime::fromMSecsSinceEpoch( std::round(
_tracker_time*1000.0) );
1969 timeLine->setText( datetime.toString(
"[yyyy MMM dd] HH:mm::ss.zzz") );
1973 timeLine->setText( QString::number(relative_time,
'f', 3));
1976 QFontMetrics fm( timeLine->font() );
1977 int width = fm.width( timeLine->text()) + 10;
1978 timeLine->setFixedWidth( std::max( 100, width ) );
2065 settings.setValue(
"MainWindow.geometry", saveGeometry());
2066 settings.setValue(
"MainWindow.activateGrid",
ui->pushButtonActivateGrid->isChecked() );
2067 settings.setValue(
"MainWindow.streamingBufferValue",
ui->streamingSpinBox->value() );
2068 settings.setValue(
"MainWindow.removeTimeOffset",
ui->pushButtonRemoveTimeOffset->isChecked() );
2069 settings.setValue(
"MainWindow.dateTimeDisplay",
ui->pushButtonUseDateTime->isChecked() );
2070 settings.setValue(
"MainWindow.timeTrackerSetting", (
int)
_tracker_param );
2094 qWarning(
"failed to find custom equation");
2104 catch(
const std::runtime_error &e)
2106 QMessageBox::critical(
this,
"error",
"Failed to refresh data : " + QString::fromStdString(e.what()));
2126 qWarning(
"failed to find custom equation");
2134 data_it->second.clear();
2139 if(dialog.exec() == QDialog::Accepted)
2141 const QString& qplot_name = dialog.
getName();
2142 std::string plot_name = qplot_name.toStdString();
2148 catch(std::exception& ex)
2150 QMessageBox::warning(
this, tr(
"Warning"),
2151 tr(
"Failed to create the custom timeseries. Error:\n\n%1")
2152 .
arg( ex.what() ) );
2164 custom_it->second = eq;
2183 qint64 delta_ms = (QDateTime::currentMSecsSinceEpoch() -
_prev_publish_time.toMSecsSinceEpoch());
2185 delta_ms = std::max( (qint64)
_publish_timer->interval(), delta_ms);
2188 if( _tracker_time >=
ui->timeSlider->getMaximum())
2190 if( !
ui->playbackLoop->isChecked() )
2192 ui->pushButtonPlay->setChecked(
false);
2194 _tracker_time =
ui->timeSlider->getMinimum();
2197 auto prev =
ui->timeSlider->blockSignals(
true);
2198 ui->timeSlider->setRealValue( _tracker_time );
2199 ui->timeSlider->blockSignals(prev);
2207 it.second->play( _tracker_time );
2219 QDesktopServices::openUrl( QUrl(
"https://github.com/facontidavide/PlotJuggler/issues" ));
2224 QDialog* dialog =
new QDialog(
this);
2225 auto ui =
new Ui::AboutDialog();
2226 ui->setupUi(dialog);
2228 ui->label_version->setText( QApplication::applicationVersion() );
2229 dialog->setAttribute(Qt::WA_DeleteOnClose);
2239 dialog->restoreGeometry(settings.value(
"Cheatsheet.geometry").toByteArray());
2240 dialog->setAttribute(Qt::WA_DeleteOnClose);
2243 connect(dialog, &QDialog::finished,
this, [
this, dialog]()
2246 settings.setValue(
"Cheatsheet.geometry", dialog->saveGeometry());
2252 QDialog* dialog =
new QDialog(
this);
2253 auto ui =
new Ui::SupportDialog();
2254 ui->setupUi(dialog);
2256 dialog->setAttribute(Qt::WA_DeleteOnClose);
2264 QString directory_path = settings.value(
"MainWindow.saveAllPlotTabs",
2265 QDir::currentPath() ).toString();
2267 QFileDialog saveDialog;
2268 saveDialog.setDirectory(directory_path);
2269 saveDialog.setFileMode(QFileDialog::FileMode::Directory);
2270 saveDialog.setAcceptMode(QFileDialog::AcceptSave);
2273 uint image_number = 1;
2274 if(saveDialog.result() == QDialog::Accepted && !saveDialog.selectedFiles().empty())
2277 QString directory = saveDialog.selectedFiles().first();
2278 settings.setValue(
"MainWindow.saveAllPlotTabs", directory);
2280 QStringList file_names;
2281 QStringList existing_files;
2282 QDateTime current_date_time(QDateTime::currentDateTime());
2283 QString current_date_time_name(current_date_time.toString(
"yyyy-MM-dd_HH-mm-ss"));
2286 auto tab_widget = it.second->tabWidget();
2287 for(
int i=0;
i< tab_widget->count();
i++)
2290 QString
name = QString(
"%1/%2_%3_%4.png")
2292 .arg(current_date_time_name)
2293 .arg(image_number, 2, 10, QLatin1Char(
'0')).arg(matrix->
name());
2294 file_names.push_back( name );
2297 QFileInfo check_file( file_names.back() );
2298 if( check_file.exists() && check_file.isFile() )
2300 existing_files.push_back( name );
2304 if( existing_files.isEmpty() ==
false)
2307 msgBox.setText(
"One or more files will be overwritten. ant to continue?");
2309 for(
const auto& str: existing_files)
2311 all_files.push_back(
"\n");
2312 all_files.append(str);
2314 msgBox.setInformativeText(all_files);
2315 msgBox.setStandardButtons(QMessageBox::Cancel | QMessageBox::Ok );
2316 msgBox.setDefaultButton(QMessageBox::Ok);
2318 if( msgBox.exec() != QMessageBox::Ok)
2327 auto tab_widget = it.second->tabWidget();
2328 for(
int i=0;
i< tab_widget->count();
i++)
2342 QMessageBox::warning(
this, tr(
"Warning"),
2343 tr(
"No plugin was loaded to process a data file\n") );
2349 QString file_extension_filter;
2351 std::set<QString> extensions;
2358 extensions.insert( extension.toLower() );
2362 for (
const auto& it: extensions)
2364 file_extension_filter.append( QString(
" *.") + it );
2367 QString directory_path = settings.value(
"MainWindow.lastDatafileDirectory", QDir::currentPath() ).toString();
2369 QFileDialog loadDialog(
this );
2370 loadDialog.setFileMode(QFileDialog::ExistingFiles);
2371 loadDialog.setViewMode(QFileDialog::Detail);
2372 loadDialog.setNameFilter(file_extension_filter);
2373 loadDialog.setDirectory(directory_path);
2375 QStringList fileNames;
2376 if (loadDialog.exec())
2378 fileNames = loadDialog.selectedFiles();
2381 if (fileNames.isEmpty()) {
2385 directory_path = QFileInfo(fileNames[0]).absolutePath();
2386 settings.setValue(
"MainWindow.lastDatafileDirectory", directory_path);
2398 QString directory_path = settings.value(
"MainWindow.lastLayoutDirectory", QDir::currentPath()).toString();
2399 QString
filename = QFileDialog::getOpenFileName(
this,
"Open Layout",
2400 directory_path,
"*.xml");
2401 if (filename.isEmpty()){
2410 directory_path = QFileInfo(filename).absolutePath();
2411 settings.setValue(
"MainWindow.lastLayoutDirectory", directory_path);
2421 QString directory_path = settings.value(
"MainWindow.lastLayoutDirectory",
2422 QDir::currentPath() ).toString();
2424 QFileDialog saveDialog;
2425 saveDialog.setOption(QFileDialog::DontUseNativeDialog,
true);
2427 QGridLayout *save_layout =
static_cast<QGridLayout*
>(saveDialog.layout());
2429 QFrame* frame =
new QFrame;
2430 frame->setFrameStyle(QFrame::Box | QFrame::Plain);
2431 frame->setLineWidth(1);
2433 QVBoxLayout *vbox =
new QVBoxLayout;
2434 QLabel* title =
new QLabel(
"Save Layout options");
2435 QFrame* separator =
new QFrame;
2436 separator->setFrameStyle(QFrame::HLine | QFrame::Plain);
2438 auto checkbox_datasource =
new QCheckBox(
"Save data source");
2439 checkbox_datasource->setToolTip(
"ant the layout to remember the source of your data,\n" 2440 "i.e. the Datafile used or the Streaming Plugin loaded ?");
2441 checkbox_datasource->setFocusPolicy( Qt::NoFocus );
2442 checkbox_datasource->setChecked( settings.value(
"MainWindow.saveLayoutDataSource",
true).toBool() );
2444 auto checkbox_snippets =
new QCheckBox(
"Save custom transformations");
2445 checkbox_snippets->setToolTip(
"Do you want the layout to save the custom transformations?");
2446 checkbox_snippets->setFocusPolicy( Qt::NoFocus );
2447 checkbox_snippets->setChecked( settings.value(
"MainWindow.saveLayoutSnippets",
true).toBool() );
2449 vbox->addWidget(title);
2450 vbox->addWidget(separator);
2451 vbox->addWidget(checkbox_datasource);
2452 vbox->addWidget(checkbox_snippets);
2453 frame->setLayout(vbox);
2455 int rows = save_layout->rowCount();
2456 int col = save_layout->columnCount();
2457 save_layout->addWidget(frame, 0, col, rows, 1, Qt::AlignTop);
2459 saveDialog.setAcceptMode(QFileDialog::AcceptSave);
2460 saveDialog.setDefaultSuffix(
"xml");
2461 saveDialog.setNameFilter(
"XML (*.xml)");
2462 saveDialog.setDirectory(directory_path);
2465 if(saveDialog.result() != QDialog::Accepted || saveDialog.selectedFiles().empty())
2470 QString fileName = saveDialog.selectedFiles().first();
2472 if (fileName.isEmpty()){
2476 directory_path = QFileInfo(fileName).absolutePath();
2477 settings.setValue(
"MainWindow.lastLayoutDirectory", directory_path);
2478 settings.setValue(
"MainWindow.saveLayoutDataSource", checkbox_datasource->isChecked() );
2479 settings.setValue(
"MainWindow.saveLayoutSnippets", checkbox_snippets->isChecked() );
2481 QDomElement root = doc.namedItem(
"root").toElement();
2484 root.appendChild( doc.createComment(
" - - - - - - - - - - - - - - ") );
2486 root.appendChild( doc.createComment(
" - - - - - - - - - - - - - - ") );
2490 root.appendChild( doc.createComment(
" - - - - - - - - - - - - - - ") );
2492 if( checkbox_datasource->isChecked() )
2494 QDomElement loaded_list = doc.createElement(
"previouslyLoaded_Datafiles" );
2498 QDomElement file_elem = doc.createElement(
"fileInfo" );
2499 file_elem.setAttribute(
"filename", loaded.filename );
2500 file_elem.setAttribute(
"prefix", loaded.prefix );
2502 QDomElement datasources_elem = doc.createElement(
"selected_datasources" );
2503 QString topics_list = loaded.selected_datasources.join(
";");
2504 datasources_elem.setAttribute(
"value", topics_list);
2505 file_elem.appendChild( datasources_elem );
2507 file_elem.appendChild( loaded.plugin_config.firstChild() );
2508 loaded_list.appendChild( file_elem );
2510 root.appendChild( loaded_list );
2514 QDomElement loaded_streamer = doc.createElement(
"previouslyLoaded_Streamer" );
2516 streamer_name.replace(
" ",
"_");
2517 loaded_streamer.setAttribute(
"name", streamer_name );
2518 root.appendChild( loaded_streamer );
2522 root.appendChild( doc.createComment(
" - - - - - - - - - - - - - - ") );
2523 if( checkbox_snippets->isChecked() )
2525 QDomElement custom_equations = doc.createElement(
"customMathEquations");
2528 const auto& custom_plot = custom_it.second;
2529 custom_equations.appendChild( custom_plot->xmlSaveState(doc) );
2531 root.appendChild(custom_equations);
2533 QByteArray snippets_xml_text = settings.value(
"AddCustomPlotDialog.savedXML",
2534 QByteArray() ).toByteArray();
2537 root.appendChild(snippets_root);
2539 root.appendChild( doc.createComment(
" - - - - - - - - - - - - - - ") );
2541 QFile file(fileName);
2542 if (file.open(QIODevice::WriteOnly)) {
2543 QTextStream stream(&file);
2544 stream << doc.toString() << endl;
2550 static bool first_call =
true;
2554 QMessageBox::information(
this,
"Remember!",
"Press F10 to switch back to the normal view");
2560 ui->widgetOptions->setVisible( !
_minimized &&
ui->pushButtonOptions->isChecked() );
2566 it.second->setControlsVisible( !
_minimized );
2584 QMenu* menu =
ui->menuRecentData;
2585 for (QAction *
action: menu->actions())
2587 if (
action->isSeparator() ){
2590 menu->removeAction(
action );
2592 menu->setEnabled(
false);
2594 settings.setValue(
"MainWindow.recentlyLoadedDatafile", {} );
2599 QMenu* menu =
ui->menuRecentLayout;
2600 for (QAction *
action: menu->actions())
2602 if (
action->isSeparator() ){
2605 menu->removeAction(
action );
2607 menu->setEnabled(
false);
2609 settings.setValue(
"MainWindow.recentlyLoadedLayout", {} );
2614 QMessageBox msgBox(
this);
2615 msgBox.setWindowTitle(
"Warning. Can't be undone.");
2616 msgBox.setText(tr(
"Do you want to remove the previously loaded data?\n"));
2617 msgBox.addButton(QMessageBox::No);
2618 msgBox.addButton(QMessageBox::Yes);
2619 msgBox.setDefaultButton(QMessageBox::Yes);
2621 auto reply = msgBox.exec();
2623 if( reply == QMessageBox::No ) {
void on_actionExit_triggered()
bool is2ndColumnHidden() const
void on_actionSaveAllPlotTabs_triggered()
void addItem(const QString &item_name)
void on_actionClearBuffer_triggered()
int findRowByName(const std::string &text) const
virtual bool xmlLoadState(const QDomElement &parent_element)
DataStreamer * _current_streamer
QGridLayout * gridLayout()
std::unordered_map< std::string, PlotData > numeric
void on_actionStopStreaming_triggered()
QShortcut _streaming_shortcut
void on_actionSupportPlotJuggler_triggered()
PlotDataMapRef & dataMap()
void initializePlugins(QString subdir_name)
void on_actionSaveLayout_triggered()
QShortcut _fullscreen_shortcut
void on_actionFunctionEditor_triggered()
void onPlotMatrixAdded(PlotMatrix *matrix)
void editMathPlot(const std::string &plot_name)
CurveListPanel * _curvelist_widget
void updatedDisplayTime()
std::vector< FileLoadInfo > _loaded_datafiles
void resizeEvent(QResizeEvent *)
bool loadDataFromFiles(QStringList filenames)
void setLinkedPlotName(const QString &linkedPlotName)
void checkAllCurvesFromLayout(const QDomElement &root)
PlotDataMapRef _mapped_plot_data
void closeEvent(QCloseEvent *event)
std::map< CurveTracker::Parameter, QIcon > _tracker_button_icons
bool xmlLoadState(QDomDocument state_document)
bool loadLayoutFromFile(QString filename)
QStandardItemModel * getTableModel() const
void on_actionReportBug_triggered()
virtual const std::vector< const char * > & compatibleFileExtensions() const =0
void on_actionClearRecentLayout_triggered()
void on_actionCheatsheet_triggered()
void setEditorMode(EditorMode mode)
void onTrackerMovedFromWidget(QPointF pos)
void on_actionClearRecentData_triggered()
std::deque< QDomDocument > _redo_states
QTableView * getTableView() const
void onPlotAdded(PlotWidget *plot)
void updateDataAndReplot(bool replot_hidden_tabs)
void refreshMathPlot(const std::string &curve_name)
void on_actionStartStreaming(QString streamer_name)
void onTimeSlider_valueChanged(double abs_time)
void createMathPlot(const std::string &linked_plot)
virtual void shutdown()=0
PlotWidget * plotAt(unsigned row, unsigned column)
void addOrEditMathPlot(const std::string &name, bool edit)
const QString & name() const
MainWindow(const QCommandLineParser &commandline_parser, QWidget *parent=nullptr)
std::unordered_map< std::string, PlotDataAny > user_defined
void on_pushButtonOptions_toggled(bool checked)
QDomElement ExportSnippets(const SnippetsMap &snippets, QDomDocument &doc)
void onSwapPlots(PlotWidget *source, PlotWidget *destination)
void on_pushButtonPlay_toggled(bool checked)
void on_actionLoadDummyData_triggered()
QElapsedTimer _undo_timer
void on_pushButtonStreaming_toggled(bool streaming)
TFSIMD_FORCE_INLINE const tfScalar & x() const
bool loadDataFromFile(const FileLoadInfo &info)
void on_actionLoadLayout_triggered()
void realValueChanged(double)
void importPlotDataMapHelper(std::unordered_map< std::string, T > &source, std::unordered_map< std::string, T > &destination, bool delete_older)
void onTrackerTimeUpdated(double absolute_time, bool do_replot)
TabbedPlotWidget * _main_tabbed_widget
void on_editMathPlot(const std::string &plot_name)
virtual bool xmlSaveState(QDomDocument &doc, QDomElement &parent_element) const
void on_actionFullscreen_triggered()
void updateRecentLayoutMenu(QStringList new_filenames)
QShortcut _playback_shotcut
CustomPlotPtr getCustomPlotData() const
virtual const char * name() const =0
void loadPluginState(const QDomElement &root)
unsigned colsCount() const
std::shared_ptr< CustomFunction > CustomPlotPtr
void deleteCurves(const std::vector< std::string > &curve_names)
const Point & front() const
SnippetsMap GetSnippetsFromXML(const QString &xml_text)
void on_refreshMathPlot(const std::string &plot_name)
void on_tabbedAreaDestroyed(QObject *object)
static const QString LAYOUT_VERSION
QDateTime _prev_publish_time
CurveTracker::Parameter _tracker_param
std::map< QString, DataStreamer * > _data_streamer
void on_pushButtonRatio_toggled(bool checked)
void on_streamingSpinBox_valueChanged(int value)
unsigned rowsCount() const
QDomElement savePluginState(QDomDocument &doc)
bool isStreamingActive() const
void createTabbedDialog(QString suggest_win_name, PlotMatrix *first_tab)
void on_actionDeleteAllData_triggered()
virtual size_t size() const
CustomPlotMap _custom_plots
void onDeleteMultipleCurves(const std::vector< std::string > &curve_names)
bool _autostart_publishers
MonitoredValue _time_offset
QStringList selected_datasources
void on_addMathPlot(const std::string &linked_name)
static CustomPlotPtr createFromXML(QDomElement &element)
void on_actionAbout_triggered()
std::deque< QDomDocument > _undo_states
void set(double newValue)
virtual bool readDataFromFile(FileLoadInfo *fileload_info, PlotDataMapRef &destination)=0
void setMaximumRange(double range)
virtual bool isDebugPlugin()
void editExistingPlot(CustomPlotPtr data)
void on_actionLoadData_triggered()
void plotAdded(PlotWidget *)
void onFloatingWindowDestroyed(QObject *object)
void on_streamingToggled()
void activateStreamingMode(bool active)
void hiddenItemsChanged()
void on_pushButtonUseDateTime_toggled(bool checked)
void requestRemoveCurveByName(const std::string &name)
std::unordered_map< std::string, PlotData >::iterator addNumeric(const std::string &name)
void AddPrefixToPlotData(const std::string &prefix, std::unordered_map< std::string, Value > &data)
void forEachWidget(std::function< void(PlotWidget *, PlotMatrix *, int, int)> op)
std::map< QString, DataLoader * > _data_loader
QTableView * getCustomView() const
virtual bool enabled() const =0
const Point & back() const
void on_pushButtonActivateGrid_toggled(bool checked)
void importPlotDataMap(PlotDataMapRef &new_data, bool remove_old)
void on_pushButtonTimeTracker_pressed()
bool _disable_undo_logging
virtual bool start(QStringList *)=0
empty_struct data[sizeof(T)/sizeof(empty_struct)]
std::map< QString, StatePublisher * > _state_publisher
TabbedPlotWidget * tabbedWidget()
The DataStreamer base class to create your own plugin.
QDomDocument xmlSaveState() const
virtual std::vector< QString > appendData(PlotDataMapRef &destination)
void on_splitterMoved(int, int)
virtual bool xmlSaveState(QDomDocument &doc, QDomElement &parent_element) const
void on_pushButtonRemoveTimeOffset_toggled(bool checked)
void updateRecentDataMenu(QStringList new_filenames)
void onUpdateLeftTableValues()
std::tuple< double, double, int > calculateVisibleRangeX()
virtual void setEnabled(bool enabled)
QDomDocument plugin_config
void onCreateFloatingWindow(PlotMatrix *first_tab=nullptr)