5 #include <QApplication> 6 #include <QActionGroup> 8 #include <QCommandLineParser> 10 #include <QDesktopServices> 11 #include <QDomDocument> 12 #include <QDoubleSpinBox> 13 #include <QElapsedTimer> 14 #include <QFileDialog> 15 #include <QInputDialog> 18 #include <QMessageBox> 20 #include <QMouseEvent> 21 #include <QPluginLoader> 22 #include <QPushButton> 23 #include <QKeySequence> 26 #include <QStringListModel> 29 #include <QTextStream> 31 #include <QHeaderView> 32 #include <QStandardPaths> 44 #include "ui_aboutdialog.h" 45 #include "ui_support_dialog.h" 54 , _undo_shortcut(QKeySequence(Qt::CTRL + Qt::Key_Z), this)
55 , _redo_shortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this)
56 , _fullscreen_shortcut(Qt::Key_F10, this)
57 , _streaming_shortcut(QKeySequence(Qt::CTRL + Qt::Key_Space), this)
58 , _playback_shotcut(Qt::Key_Space, this)
60 , _active_streamer_plugin(
nullptr)
61 , _disable_undo_logging(false)
65 , _recent_data_files(new QMenu())
66 ,_recent_layout_files(new QMenu())
77 ui->playbackLoop->setText(
"");
78 ui->pushButtonZoomOut->setText(
"");
79 ui->pushButtonPlay->setText(
"");
80 ui->pushButtonUseDateTime->setText(
"");
81 ui->pushButtonActivateGrid->setText(
"");
82 ui->pushButtonRatio->setText(
"");
83 ui->pushButtonLink->setText(
"");
84 ui->pushButtonTimeTracker->setText(
"");
85 ui->pushButtonLoadDatafile->setText(
"");
86 ui->pushButtonRemoveTimeOffset->setText(
"");
87 ui->pushButtonLegend->setText(
"");
89 if (commandline_parser.isSet(
"buffer_size"))
91 int buffer_size =
std::max(10, commandline_parser.value(
"buffer_size").toInt());
92 ui->streamingSpinBox->setMaximum(buffer_size);
110 ui->labelStreamingAnimation->setHidden(
true);
128 connect(
ui->playbackRate, &QDoubleSpinBox::editingFinished,
this, [
this]() { ui->playbackRate->clearFocus(); });
130 connect(
ui->playbackStep, &QDoubleSpinBox::editingFinished,
this, [
this]() { ui->playbackStep->clearFocus(); });
139 ui->mainSplitter->setCollapsible(0,
true);
140 ui->mainSplitter->setStretchFactor(0, 2);
141 ui->mainSplitter->setStretchFactor(1, 6);
143 ui->layoutTimescale->removeWidget(
ui->widgetButtons );
146 connect(
ui->mainSplitter, SIGNAL(splitterMoved(
int,
int)), SLOT(
on_splitterMoved(
int,
int)));
152 loaded +=
initializePlugins( QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation) +
"/PlotJuggler" );
154 auto extra_folders = commandline_parser.value(
"extra-plugin-folders").split(
";", QString::SkipEmptyParts);
156 for(
const auto& folder: extra_folders)
171 _publish_timer->setInterval(20);
174 ui->menuFile->setToolTipsVisible(
true);
176 this->setMenuBar(
ui->menuBar);
177 ui->menuBar->setNativeMenuBar(
false);
184 bool file_loaded =
false;
185 if (commandline_parser.isSet(
"datafile"))
187 QStringList datafiles = commandline_parser.values(
"datafile");
190 if (commandline_parser.isSet(
"layout"))
196 restoreGeometry(settings.value(
"MainWindow.geometry").toByteArray());
197 restoreState(settings.value(
"MainWindow.state").toByteArray());
201 bool activate_grid = settings.value(
"MainWindow.activateGrid",
false).toBool();
202 ui->pushButtonActivateGrid->setChecked(activate_grid);
204 bool zoom_link_active = settings.value(
"MainWindow.buttonLink",
true).toBool();
205 ui->pushButtonLink->setChecked(zoom_link_active);
207 bool ration_active = settings.value(
"MainWindow.buttonRatio",
true).toBool();
208 ui->pushButtonRatio->setChecked(ration_active);
210 int streaming_buffer_value = settings.value(
"MainWindow.streamingBufferValue", 5).toInt();
211 ui->streamingSpinBox->setValue(streaming_buffer_value);
213 bool datetime_display = settings.value(
"MainWindow.dateTimeDisplay",
false).toBool();
214 ui->pushButtonUseDateTime->setChecked(datetime_display);
216 bool remove_time_offset = settings.value(
"MainWindow.removeTimeOffset",
true).toBool();
217 ui->pushButtonRemoveTimeOffset->setChecked(remove_time_offset);
221 if( settings.value(
"MainWindow.hiddenFileFrame",
false).toBool() )
223 ui->buttonHideFileFrame->setText(
"+");
224 ui->frameFile->setHidden(
true);
226 if( settings.value(
"MainWindow.hiddenStreamingFrame",
false).toBool() )
228 ui->buttonHideStreamingFrame->setText(
"+");
229 ui->frameStreaming->setHidden(
true);
231 if( settings.value(
"MainWindow.hiddenPublishersFrame",
false).toBool() )
233 ui->buttonHidePublishersFrame->setText(
"+");
234 ui->framePublishers->setHidden(
true);
238 QIcon trackerIconA, trackerIconB, trackerIconC;
240 trackerIconA.addFile(QStringLiteral(
":/style_light/line_tracker.png"), QSize(36, 36));
241 trackerIconB.addFile(QStringLiteral(
":/style_light/line_tracker_1.png"), QSize(36, 36));
242 trackerIconC.addFile(QStringLiteral(
":/style_light/line_tracker_a.png"), QSize(36, 36));
248 int tracker_setting = settings.value(
"MainWindow.timeTrackerSetting", (
int)
CurveTracker::VALUE).toInt();
255 auto editor_layout =
new QVBoxLayout();
256 editor_layout->setMargin(0);
257 ui->formulaPage->setLayout(editor_layout);
263 ui->widgetStack->setCurrentIndex(0);
272 QString theme = settings.value(
"Preferences::theme",
"light").toString();
273 if( theme !=
"dark") {
299 if (elapsed_ms < 100)
356 auto prev =
ui->timeSlider->blockSignals(
true);
358 ui->timeSlider->blockSignals(prev);
376 it.second->updateState(absolute_time);
397 connect(&
_playback_shotcut, &QShortcut::activated,
ui->pushButtonPlay, &QPushButton::toggle);
400 QShortcut* open_menu_shortcut =
new QShortcut(QKeySequence(Qt::ALT + Qt::Key_F),
this);
401 connect(open_menu_shortcut, &QShortcut::activated,
402 [
this]() {
ui->menuFile->exec(
ui->menuBar->mapToGlobal(QPoint(0, 25))); });
404 QShortcut* open_help_shortcut =
new QShortcut(QKeySequence(Qt::ALT + Qt::Key_H),
this);
405 connect(open_help_shortcut, &QShortcut::activated,
406 [
this]() {
ui->menuHelp->exec(
ui->menuBar->mapToGlobal(QPoint(230, 25))); });
417 static std::set<QString> loaded_plugins;
418 QStringList loaded_out;
420 qDebug() <<
"Loading compatible plugins from directory: " << directory_name;
421 int loaded_count = 0;
423 QDir pluginsDir(directory_name);
425 for (
const QString& filename : pluginsDir.entryList(QDir::Files))
427 QFileInfo fileinfo(filename);
428 if (fileinfo.suffix() !=
"so" && fileinfo.suffix() !=
"dll" && fileinfo.suffix() !=
"dylib")
433 if (loaded_plugins.find(filename) != loaded_plugins.end())
438 QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(filename),
this);
441 QObject* plugin = pluginLoader.instance();
444 auto class_name = pluginLoader.metaData().value(
"className").toString();
445 loaded_out.push_back( class_name );
455 plugin_name = loader->
name();
458 plugin_name = publisher->name();
461 plugin_name = streamer->name();
463 else if (message_parser){
464 plugin_name = streamer->name();
467 if (loaded_plugins.find(plugin_name) == loaded_plugins.end())
469 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.")
483 qDebug() << filename <<
": is a DataLoader plugin";
486 qDebug() << filename <<
"...but will be ignored unless the argument -t is used.";
490 _data_loader.insert(std::make_pair(plugin_name, loader));
496 qDebug() << filename <<
": is a StatePublisher plugin";
499 qDebug() << filename <<
"...but will be ignored unless the argument -t is used.";
505 ui->layoutPublishers->setColumnStretch(0, 1.0);
508 auto label =
new QLabel(plugin_name,
ui->framePublishers);
509 ui->layoutPublishers->addWidget(label, row, 0);
511 auto start_checkbox =
new QCheckBox(
ui->framePublishers);
512 ui->layoutPublishers->addWidget(start_checkbox, row, 1);
513 start_checkbox->setFocusPolicy( Qt::FocusPolicy::NoFocus );
515 connect(start_checkbox, &QCheckBox::toggled,
this,
516 [=](
bool enable) { publisher->setEnabled(enable); });
518 connect(publisher, &StatePublisher::closed, start_checkbox,
519 [=]() {start_checkbox->setChecked(
false);} );
521 if( publisher->availableActions().empty() )
523 QFrame* empty =
new QFrame(
ui->framePublishers);
524 empty->setFixedSize({22,22});
525 ui->layoutPublishers->addWidget(empty, row, 2);
528 auto options_button =
new QPushButton(
ui->framePublishers);
529 options_button->setFlat(
true);
530 options_button->setFixedSize({24,24});
531 ui->layoutPublishers->addWidget(options_button, row, 2);
533 options_button->setIcon(
LoadSvgIcon(
":/resources/svg/settings_cog.svg",
"light"));
534 options_button->setIconSize( {16,16} );
536 auto optionsMenu = [=]()
539 for(
auto action: publisher->availableActions()) {
540 menu->addAction(action);
545 connect( options_button, &QPushButton::clicked,
546 options_button, optionsMenu);
549 options_button, [=](QString style)
551 options_button->setIcon(
LoadSvgIcon(
":/resources/svg/settings_cog.svg", style));
556 else if (message_parser)
562 qDebug() << filename <<
": is a DataStreamer plugin";
565 qDebug() << filename <<
"...but will be ignored unless the argument -t is used.";
573 connect(streamer, &DataStreamer::closed,
this,
576 connect(streamer, &DataStreamer::clearBuffers,
579 connect(streamer, &DataStreamer::dataReceived,
590 if (pluginLoader.errorString().contains(
"is not an ELF object") ==
false)
592 qDebug() << filename <<
": " << pluginLoader.errorString();
598 QSignalBlocker
block(
ui->comboStreaming);
599 ui->comboStreaming->setEnabled(
true);
600 ui->buttonStreamingStart->setEnabled(
true);
604 if(
ui->comboStreaming->findText(it.first) == -1){
605 ui->comboStreaming->addItem(it.first);
611 QString streaming_name = settings.value(
"MainWindow.previousStreamingPlugin",
612 ui->comboStreaming->itemText(0)).toString();
614 auto streamer_it = _data_streamer.find(streaming_name);
615 if( streamer_it == _data_streamer.end() )
617 streamer_it = _data_streamer.begin();
618 streaming_name = streamer_it->first;
621 ui->comboStreaming->setCurrentText(streaming_name);
623 bool contains_options = !streamer_it->second->availableActions().empty();
624 ui->buttonStreamingOptions->setEnabled(contains_options);
626 qDebug() <<
"Number of plugins loaded: " << loaded_count <<
"\n";
634 static int count = 0;
638 QStringList words_list;
639 words_list <<
"world/siam" 646 <<
"fly/low/dividera" 652 for (
int i = 0; i < 100; i++)
654 words_list.append(QString(
"data_vect/%1").
arg(count++));
657 for (
const QString&
name : words_list)
659 double A = 6 * ((double)qrand() / (double)RAND_MAX) - 3;
660 double B = 3 * ((double)qrand() / (double)RAND_MAX);
661 double C = 3 * ((double)qrand() / (double)RAND_MAX);
662 double D = 20 * ((double)qrand() / (double)RAND_MAX);
668 for (
unsigned indx = 0; indx < SIZE; indx++)
679 for (
unsigned indx = 0; indx < SIZE; indx++)
693 int totalWidth = sizes[0] + sizes[1];
696 static bool first =
true;
697 if (sizes[0] != 0 && first)
701 int splitter_width = settings.value(
"MainWindow.splitterWidth", 200).toInt();
702 auto sizes =
ui->mainSplitter->sizes();
703 int tot_splitter_width = sizes[0] + sizes[1];
704 sizes[0] = splitter_width;
705 sizes[1] = tot_splitter_width - splitter_width;
706 ui->mainSplitter->setSizes(sizes);
710 if (sizes[0] > max_left_size)
712 sizes[0] = max_left_size;
713 sizes[1] = totalWidth - max_left_size;
714 ui->mainSplitter->setSizes(sizes);
738 connect(&
_time_offset, SIGNAL(valueChanged(
double)), plot, SLOT(on_changeTimeOffset(
double)));
747 if(plot != p) p->setLegendSize(point_size);
763 if (
ui->pushButtonLink->isChecked())
766 if(plot != modified_plot &&
770 QRectF bound_act = plot->canvasBoundingRect();
771 bound_act.setLeft(new_range.left());
772 bound_act.setRight(new_range.right());
773 plot->setZoomRectangle(bound_act,
false);
774 plot->on_zoomOutVertical_triggered(
false);
796 QDomProcessingInstruction instr = doc.createProcessingInstruction(
"xml",
"version='1.0' encoding='UTF-8'");
798 doc.appendChild(instr);
800 QDomElement root = doc.createElement(
"root");
804 QDomElement tabbed_area = it.second->xmlSaveState(doc);
805 root.appendChild(tabbed_area);
808 doc.appendChild(root);
810 QDomElement relative_time = doc.createElement(
"use_relative_time_offset");
811 relative_time.setAttribute(
"enabled",
ui->pushButtonRemoveTimeOffset->isChecked());
812 root.appendChild(relative_time);
819 std::set<std::string> curves;
821 for (QDomElement tw = root.firstChildElement(
"tabbed_widget"); !tw.isNull(); tw = tw.nextSiblingElement(
"tabbed_" 824 for (QDomElement pm = tw.firstChildElement(
"plotmatrix"); !pm.isNull(); pm = pm.nextSiblingElement(
"plotmatrix"))
826 for (QDomElement pl = pm.firstChildElement(
"plot"); !pl.isNull(); pl = pl.nextSiblingElement(
"plot"))
828 QDomElement tran_elem = pl.firstChildElement(
"transform");
829 std::string trans = tran_elem.attribute(
"value").toStdString();
830 bool is_XY_plot = (trans ==
"XYPlot");
832 for (QDomElement cv = pl.firstChildElement(
"curve"); !cv.isNull(); cv = cv.nextSiblingElement(
"curve"))
836 curves.insert(cv.attribute(
"curve_x").toStdString());
837 curves.insert(cv.attribute(
"curve_y").toStdString());
841 curves.insert(cv.attribute(
"name").toStdString());
848 std::vector<std::string> missing_curves;
850 for (
auto& curve_name : curves)
854 missing_curves.push_back(curve_name);
857 if (missing_curves.size() > 0)
859 QMessageBox msgBox(
this);
860 msgBox.setWindowTitle(
"Warning");
861 msgBox.setText(tr(
"One or more timeseries in the layout haven't been loaded yet\n" 862 "What do you want to do?"));
864 QPushButton* buttonRemove = msgBox.addButton(tr(
"Remove curves from plots"), QMessageBox::RejectRole);
865 QPushButton* buttonPlaceholder = msgBox.addButton(tr(
"Create empty placeholders"), QMessageBox::YesRole);
866 msgBox.setDefaultButton(buttonPlaceholder);
868 if (msgBox.clickedButton() == buttonPlaceholder)
870 for (
auto&
name : missing_curves)
882 QDomElement root = state_document.namedItem(
"root").toElement();
885 qWarning() <<
"No <root> element found at the top-level of the XML file!";
889 size_t num_floating = 0;
890 std::map<QString, QDomElement> tabbed_widgets_with_name;
892 for (QDomElement tw = root.firstChildElement(
"tabbed_widget");
893 tw.isNull() ==
false;
894 tw = tw.nextSiblingElement(
"tabbed_widget"))
896 if (tw.attribute(
"parent") != (
"main_window"))
900 tabbed_widgets_with_name[tw.attribute(
"name")] = tw;
904 for (
const auto& it : tabbed_widgets_with_name)
915 if (tabbed_widgets_with_name.count(it.first) == 0)
917 it.second->deleteLater();
925 for (QDomElement tw = root.firstChildElement(
"tabbed_widget");
926 tw.isNull() ==
false;
927 tw = tw.nextSiblingElement(
"tabbed_widget"))
933 QDomElement relative_time = root.firstChildElement(
"use_relative_time_offset");
934 if (!relative_time.isNull())
936 bool remove_offset = (relative_time.attribute(
"enabled") == QString(
"1"));
937 ui->pushButtonRemoveTimeOffset->setChecked(remove_offset);
944 for (
const auto& curve_name : curve_names)
971 QAction* separator =
nullptr;
972 QStringList prev_filenames;
973 for (QAction* action : menu->actions())
975 if (action->isSeparator())
980 if (new_filenames.contains(action->text()) ==
false)
982 prev_filenames.push_back(action->text());
984 menu->removeAction(action);
987 new_filenames.append(prev_filenames);
988 while (new_filenames.size() > 10)
990 new_filenames.removeLast();
993 for (
const auto& filename : new_filenames)
995 QAction* action =
new QAction(filename,
nullptr);
996 connect(action, &QAction::triggered,
this, [
this, filename] {
loadDataFromFiles({ filename }); });
997 menu->insertAction(separator, action);
1001 settings.setValue(
"MainWindow.recentlyLoadedDatafile", new_filenames);
1002 menu->setEnabled(new_filenames.size() > 0);
1009 QAction* separator =
nullptr;
1010 QStringList prev_filenames;
1011 for (QAction* action : menu->actions())
1013 if (action->isSeparator())
1018 if (new_filenames.contains(action->text()) ==
false)
1020 prev_filenames.push_back(action->text());
1022 menu->removeAction(action);
1025 new_filenames.append(prev_filenames);
1026 while (new_filenames.size() > 10)
1028 new_filenames.removeLast();
1031 for (
const auto& filename : new_filenames)
1033 QAction* action =
new QAction(filename,
nullptr);
1034 connect(action, &QAction::triggered,
this, [
this, filename] {
1040 menu->insertAction(separator, action);
1044 settings.setValue(
"MainWindow.recentlyLoadedLayout", new_filenames);
1045 menu->setEnabled(new_filenames.size() > 0);
1058 bool stopped =
false;
1060 for(
int idx = 0; idx <
ui->layoutPublishers->count(); idx++)
1062 QLayoutItem * item =
ui->layoutPublishers->itemAt(idx);
1063 if(dynamic_cast<QWidgetItem *>(item))
1065 if(
auto checkbox = dynamic_cast<QCheckBox *>(item->widget())) {
1066 if( checkbox->isChecked() ) {
1067 checkbox->setChecked(
false);
1076 QMessageBox::warning(
this,
"State publishers stopped",
1077 "All the state publishers have been stopped because old data has been deleted.");
1081 template <
typename T>
1083 std::unordered_map<std::string, T>& destination,
bool delete_older)
1085 for (
auto& it : source)
1087 const std::string&
name = it.first;
1088 T& source_plot = it.second;
1089 auto plot_with_same_name = destination.find(name);
1092 if (plot_with_same_name == destination.end())
1094 plot_with_same_name =
1095 destination.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(name)).first;
1097 T& destination_plot = plot_with_same_name->second;
1101 double max_range_x = destination_plot.maximumRangeX();
1102 destination_plot.swapData(source_plot);
1103 destination_plot.setMaximumRangeX(max_range_x);
1107 for (
size_t i = 0; i < source_plot.size(); i++)
1109 destination_plot.pushBack(source_plot.at(i));
1112 source_plot.clear();
1125 std::vector<std::string> old_plots_to_delete;
1130 if (new_data.
numeric.count(it.first) == 0)
1132 old_plots_to_delete.push_back(it.first);
1136 if (!old_plots_to_delete.empty())
1138 QMessageBox::StandardButton reply;
1139 reply = QMessageBox::question(
this, tr(
"Warning"), tr(
"Do you want to remove the previously loaded data?\n"),
1140 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
1141 if (reply == QMessageBox::Yes)
1148 bool curvelist_modified =
false;
1149 for (
auto& it : new_data.
numeric)
1151 const std::string&
name = it.first;
1155 curvelist_modified =
true;
1162 if (curvelist_modified)
1171 return !
ui->buttonStreamingPause->isChecked() &&
1177 if (filenames.size() > 1)
1180 msgbox.setWindowTitle(
"Loading multiple files");
1181 msgbox.setText(
"You are loading multiple files at once. A prefix will be automatically added to the name of the " 1183 "This is an experimental feature. Publishers will not work as you may expect.");
1184 msgbox.addButton(QMessageBox::Ok);
1191 char prefix_ch =
'A';
1192 QStringList loaded_filenames;
1194 for (
int i = 0; i < filenames.size(); i++)
1198 if (filenames.size() > 1)
1205 loaded_filenames.push_back(filenames[i]);
1209 if (loaded_filenames.size() > 0)
1219 ui->pushButtonPlay->setChecked(
false);
1221 const QString extension = QFileInfo(info.
filename).suffix().toLower();
1223 typedef std::map<QString, DataLoaderPtr>::iterator MapIterator;
1225 std::vector<MapIterator> compatible_loaders;
1230 std::vector<const char*> extensions = data_loader->compatibleFileExtensions();
1232 for (
auto& ext : extensions)
1234 if (extension == QString(ext).toLower())
1236 compatible_loaders.push_back(it);
1244 if (compatible_loaders.size() == 1)
1246 dataloader = compatible_loaders.front()->second;
1250 static QString last_plugin_name_used;
1253 for (
auto& cl : compatible_loaders)
1255 const auto&
name = cl->first;
1257 if (
name == last_plugin_name_used)
1259 names.push_front(
name);
1263 names.push_back(
name);
1268 QString plugin_name = QInputDialog::getItem(
this, tr(
"QInputDialog::getItem()"), tr(
"Select the loader to use:"),
1269 names, 0,
false, &ok);
1270 if (ok && !plugin_name.isEmpty())
1273 last_plugin_name_used = plugin_name;
1281 if (!file.open(QFile::ReadOnly | QFile::Text))
1283 QMessageBox::warning(
this, tr(
"Datafile"),
1284 tr(
"Cannot read file %1:\n%2.").
arg(info.
filename).arg(file.errorString()));
1294 if (dataloader->readDataFromFile(&new_info, mapped_data))
1300 QDomElement plugin_elem = dataloader->xmlSaveState(new_info.
plugin_config);
1303 bool duplicate =
false;
1308 if (prev_loaded.filename == new_info.
filename && prev_loaded.prefix == new_info.
prefix)
1310 prev_loaded = new_info;
1318 _loaded_datafiles.push_back(new_info);
1322 catch (std::exception& ex)
1324 QMessageBox::warning(
1325 this, tr(
"Exception from the plugin"),
1326 tr(
"The plugin [%1] thrown the following exception: \n\n %3\n").
arg(dataloader->name()).
arg(ex.what()));
1332 QMessageBox::warning(
this, tr(
"Error"),
1333 tr(
"Cannot read files with extension %1.\n No plugin can handle that!\n").
arg(info.
filename));
1337 ui->timeSlider->setRealValue(
ui->timeSlider->getMinimum());
1350 ui->pushButtonRemoveTimeOffset->setEnabled(paused);
1351 ui->widgetPlay->setEnabled(paused);
1353 if (!paused &&
ui->pushButtonPlay->isChecked())
1355 ui->pushButtonPlay->setChecked(
false);
1380 bool prev_state =
ui->buttonStreamingPause->isChecked();
1381 ui->buttonStreamingPause->setChecked(!prev_state);
1388 ui->comboStreaming->setEnabled(
true);
1389 ui->buttonStreamingStart->setText(
"Start");
1390 ui->buttonStreamingPause->setEnabled(
false);
1391 ui->labelStreamingAnimation->setHidden(
true);
1394 if(
ui->buttonStreamingPause->isChecked() )
1397 ui->buttonStreamingPause->setChecked(
false);
1411 ui->actionDeleteAllData->setToolTip(
"");
1435 qDebug() <<
"Error, no streamer loaded";
1446 qDebug() <<
"Error. The streamer " << streamer_name <<
" can't be loaded";
1451 bool started =
false;
1457 catch (std::runtime_error& err)
1459 QMessageBox::warning(
this, tr(
"Exception from the plugin"),
1460 tr(
"The plugin thrown the following exception: \n\n %1\n").
arg(err.what()));
1471 ui->actionClearBuffer->setEnabled(
true);
1472 ui->actionDeleteAllData->setToolTip(
"Stop streaming to be able to delete the data");
1474 ui->buttonStreamingStart->setText(
"Stop");
1475 ui->buttonStreamingPause->setEnabled(
true);
1476 ui->buttonStreamingPause->setChecked(
false);
1477 ui->comboStreaming->setEnabled(
false);
1478 ui->labelStreamingAnimation->setHidden(
false);
1487 QSignalBlocker
block(
ui->buttonStreamingStart );
1488 ui->buttonStreamingStart->setChecked(
false);
1489 qDebug() <<
"Failed to launch the streamer";
1496 QFile styleFile(file_path);
1497 styleFile.open(QFile::ReadOnly);
1507 catch( std::runtime_error& err )
1509 QMessageBox::warning(
this, tr(
"Error loading StyleSheet"), tr(err.what()));
1516 ui->pushButtonLoadDatafile->setIcon(
LoadSvgIcon(
":/resources/svg/import.svg", theme));
1517 ui->buttonStreamingPause->setIcon(
LoadSvgIcon(
":/resources/svg/pause.svg", theme));
1519 ui->buttonRecentData->setIcon(
LoadSvgIcon(
":/resources/svg/right-arrow.svg", theme));
1520 ui->buttonRecentLayout->setIcon(
LoadSvgIcon(
":/resources/svg/right-arrow.svg", theme));
1522 ui->pushButtonZoomOut->setIcon(
LoadSvgIcon(
":/resources/svg/zoom_max.svg", theme));
1523 ui->playbackLoop->setIcon(
LoadSvgIcon(
":/resources/svg/loop.svg", theme));
1524 ui->pushButtonPlay->setIcon(
LoadSvgIcon(
":/resources/svg/play_arrow.svg", theme));
1525 ui->pushButtonUseDateTime->setIcon(
LoadSvgIcon(
":/resources/svg/datetime.svg", theme));
1526 ui->pushButtonActivateGrid->setIcon(
LoadSvgIcon(
":/resources/svg/grid.svg", theme));
1527 ui->pushButtonRatio->setIcon(
LoadSvgIcon(
":/resources/svg/ratio.svg", theme));
1529 ui->pushButtonLoadLayout->setIcon(
LoadSvgIcon(
":/resources/svg/import.svg", theme));
1530 ui->pushButtonSaveLayout->setIcon(
LoadSvgIcon(
":/resources/svg/export.svg", theme));
1532 ui->pushButtonLink->setIcon(
LoadSvgIcon(
":/resources/svg/link.svg", theme));
1533 ui->pushButtonRemoveTimeOffset->setIcon(
LoadSvgIcon(
":/resources/svg/t0.svg", theme));
1534 ui->pushButtonLegend->setIcon(
LoadSvgIcon(
":/resources/svg/legend.svg", theme));
1536 ui->buttonStreamingOptions->setIcon(
LoadSvgIcon(
":/resources/svg/settings_cog.svg", theme));
1541 QDomElement plugins = root.firstChildElement(
"Plugins");
1543 for (QDomElement plugin_elem = plugins.firstChildElement(); plugin_elem.isNull() ==
false;
1544 plugin_elem = plugin_elem.nextSiblingElement())
1546 const QString plugin_name = plugin_elem.attribute(
"ID");
1548 if (plugin_elem.nodeName() !=
"plugin" || plugin_name.isEmpty())
1550 QMessageBox::warning(
this, tr(
"Error loading Plugin State from Layout"),
1551 tr(
"The method xmlSaveState() must return a node like this <plugin ID=\"PluginName\" "));
1565 publisher->xmlLoadState(plugin_elem);
1569 publisher->setEnabled(
true);
1577 QDomElement list_plugins = doc.createElement(
"Plugins");
1579 auto CheckValidFormat = [
this](
const QString& expected_name,
const QDomElement& elem) {
1580 if (elem.nodeName() !=
"plugin" || elem.attribute(
"ID") != expected_name)
1582 QMessageBox::warning(
this, tr(
"Error saving Plugin State to Layout"),
1583 tr(
"The method xmlSaveState() returned\n<plugin ID=\"%1\">\ninstead of\n<plugin ID=\"%2\">")
1584 .
arg(elem.attribute(
"ID"))
1585 .
arg(expected_name));
1592 QDomElement plugin_elem = dataloader->xmlSaveState(doc);
1593 if (!plugin_elem.isNull())
1595 list_plugins.appendChild(plugin_elem);
1596 CheckValidFormat(it.first, plugin_elem);
1603 QDomElement plugin_elem = datastreamer->xmlSaveState(doc);
1604 if (!plugin_elem.isNull())
1606 list_plugins.appendChild(plugin_elem);
1607 CheckValidFormat(it.first, plugin_elem);
1614 QDomElement plugin_elem = state_publisher->xmlSaveState(doc);
1615 if (!plugin_elem.isNull())
1617 list_plugins.appendChild(plugin_elem);
1618 CheckValidFormat(it.first, plugin_elem);
1621 plugin_elem.setAttribute(
"status", state_publisher->enabled() ?
"active" :
"idle");
1623 return list_plugins;
1636 const auto& curve_name = it.src_name;
1643 const auto&
data = plot_it->second;
1644 if (
data.size() >= 1)
1646 const double t0 =
data.front().x;
1647 const double t1 =
data.back().x;
1656 if (max_steps == 0 || max_time < min_time)
1661 if (data.
size() >= 1)
1663 const double t0 = data.
front().
x;
1664 const double t1 = data.
back().
x;
1673 if (max_steps == 0 || max_time < min_time)
1679 return std::tuple<double, double, int>(min_time, max_time, max_steps);
1688 QFile file(filename);
1689 if (!file.open(QFile::ReadOnly | QFile::Text))
1691 QMessageBox::warning(
this, tr(
"Layout"), tr(
"Cannot read file %1:\n%2.").
arg(filename).
arg(file.errorString()));
1696 int errorLine, errorColumn;
1698 QDomDocument domDocument;
1700 if (!domDocument.setContent(&file,
true, &errorStr, &errorLine, &errorColumn))
1702 QMessageBox::information(window(), tr(
"XML Layout"),
1703 tr(
"Parse error at line %1:\n%2").
arg(errorLine).
arg(errorStr));
1709 QDomElement root = domDocument.namedItem(
"root").toElement();
1711 if (!root.hasAttribute(
"version") || root.attribute(
"version") !=
LAYOUT_VERSION)
1713 QMessageBox msgBox(
this);
1714 msgBox.setWindowTitle(
"Obsolate Layout version");
1715 msgBox.setText(tr(
"This Layout ID is not supported [%1].\nThis version of PlotJuggler use Layout ID [%2]")
1716 .
arg(root.attribute(
"version"))
1719 msgBox.setStandardButtons(QMessageBox::Abort);
1720 QPushButton* continueButton = msgBox.addButton(tr(
"Continue anyway"), QMessageBox::ActionRole);
1721 msgBox.setDefaultButton(continueButton);
1723 int ret = msgBox.exec();
1724 if (ret == QMessageBox::Abort)
1732 QDomElement previously_loaded_datafile = root.firstChildElement(
"previouslyLoaded_Datafiles");
1734 QDomElement datafile_elem = previously_loaded_datafile.firstChildElement(
"fileInfo");
1735 while (!datafile_elem.isNull())
1738 info.
filename = datafile_elem.attribute(
"filename");
1739 info.
prefix = datafile_elem.attribute(
"prefix");
1741 QDomElement datasources_elem = datafile_elem.firstChildElement(
"selected_datasources");
1742 QString topics_list = datasources_elem.attribute(
"value");
1745 auto plugin_elem = datafile_elem.firstChildElement(
"plugin");
1749 datafile_elem = datafile_elem.nextSiblingElement(
"fileInfo");
1752 QDomElement previousl_streamer = root.firstChildElement(
"previouslyLoaded_Streamer");
1753 if (!previousl_streamer.isNull())
1755 QString streamer_name = previousl_streamer.attribute(
"name");
1757 QMessageBox msgBox(
this);
1758 msgBox.setWindowTitle(
"Start Streaming?");
1759 msgBox.setText(tr(
"Start the previously used streaming plugin?\n\n %1 \n\n").
arg(streamer_name));
1760 QPushButton* yes = msgBox.addButton(tr(
"Yes"), QMessageBox::YesRole);
1761 QPushButton* no = msgBox.addButton(tr(
"No"), QMessageBox::RejectRole);
1762 msgBox.setDefaultButton(yes);
1765 if (msgBox.clickedButton() == yes)
1773 QMessageBox::warning(
this, tr(
"Error Loading Streamer"),
1774 tr(
"The streamer named %1 can not be loaded.").
arg(streamer_name));
1780 QDomElement plugins = root.firstChildElement(
"Plugins");
1784 for (QDomElement plugin_elem = plugins.firstChildElement(); plugin_elem.isNull() ==
false;
1785 plugin_elem = plugin_elem.nextSiblingElement())
1787 const QString plugin_name = plugin_elem.nodeName();
1792 if (plugin_elem.attribute(
"status") ==
"active")
1794 publisher->setEnabled(
true);
1800 auto custom_equations = root.firstChildElement(
"customMathEquations");
1804 if (!custom_equations.isNull())
1806 for (QDomElement custom_eq = custom_equations.firstChildElement(
"snippet"); custom_eq.isNull() ==
false;
1807 custom_eq = custom_eq.nextSiblingElement(
"snippet"))
1810 const auto&
name = new_custom_plot->name();
1818 catch (std::runtime_error& err)
1820 QMessageBox::warning(
this, tr(
"Exception"), tr(
"Failed to refresh a customMathEquation \n\n %1\n").
arg(err.what()));
1823 QByteArray snippets_saved_xml = settings.value(
"AddCustomPlotDialog.savedXML", QByteArray()).toByteArray();
1825 auto snippets_element = root.firstChildElement(
"snippets");
1826 if (!snippets_element.isNull())
1831 bool snippets_are_different =
false;
1832 for (
const auto& snippet_it : snippets_layout)
1834 auto prev_it = snippets_previous.find(snippet_it.first);
1836 if (prev_it == snippets_previous.end() ||
1837 prev_it->second.function != snippet_it.second.function ||
1838 prev_it->second.globalVars != snippet_it.second.globalVars)
1840 snippets_are_different =
true;
1845 if (snippets_are_different)
1847 QMessageBox msgBox(
this);
1848 msgBox.setWindowTitle(
"Overwrite custom transforms?");
1849 msgBox.setText(
"Your layour file contains a set of custom transforms different from " 1850 "the last one you used.\nant to load these transformations?");
1851 msgBox.addButton(QMessageBox::No);
1852 msgBox.addButton(QMessageBox::Yes);
1853 msgBox.setDefaultButton(QMessageBox::Yes);
1855 if (msgBox.exec() == QMessageBox::Yes)
1857 for (
const auto& snippet_it : snippets_layout)
1859 snippets_previous[snippet_it.first] = snippet_it.second;
1862 auto snippets_root_element =
ExportSnippets(snippets_previous, doc);
1863 doc.appendChild(snippets_root_element);
1864 settings.setValue(
"AddCustomPlotDialog.savedXML", doc.toByteArray(2));
1888 auto func = [&](QTabWidget* tabs) {
1889 for (
int t = 0; t < tabs->count(); t++)
1896 for (
int index = 0; index < matrix->
plotCount(); index++)
1899 operation(plot, matrix, index);
1906 func(it.second->tabWidget());
1919 ui->timeSlider->setLimits(std::get<0>(range), std::get<1>(range), std::get<2>(range));
1928 double min_time = std::get<0>(range);
1930 const bool remove_offset =
ui->pushButtonRemoveTimeOffset->isChecked();
1943 bool data_updated_by_streamer =
false;
1947 std::vector<QString> curvelist_added;
1952 curvelist_added = res.first;
1953 data_updated_by_streamer = res.second;
1956 for (
const auto& str : curvelist_added)
1961 if (curvelist_added.size() > 0)
1979 if( data_updated_by_streamer )
1987 if (is_streaming_active)
1990 double max_time = std::get<1>(range);
2000 if( data_updated_by_streamer )
2004 if (replot_hidden_tabs)
2006 QTabWidget* tabs = it.second->tabWidget();
2007 for (
int index = 0; index < tabs->count(); index++)
2015 PlotDocker* matrix = it.second->currentTab();
2029 double real_value =
value;
2038 it.second.setMaximumRangeX(real_value);
2043 it.second.setMaximumRangeX(real_value);
2066 if (this->signalsBlocked() ==
false){
2073 QLineEdit* timeLine =
ui->displayTime;
2075 if (
ui->pushButtonUseDateTime->isChecked())
2077 if (
ui->pushButtonRemoveTimeOffset->isChecked())
2079 QTime
time = QTime::fromMSecsSinceStartOfDay(std::round(relative_time * 1000.0));
2080 timeLine->setText(time.toString(
"HH:mm::ss.zzz"));
2084 QDateTime datetime = QDateTime::fromMSecsSinceEpoch(std::round(
_tracker_time * 1000.0));
2085 timeLine->setText(datetime.toString(
"[yyyy MMM dd] HH:mm::ss.zzz"));
2090 timeLine->setText(QString::number(relative_time,
'f', 3));
2093 QFontMetrics fm(timeLine->font());
2094 int width = fm.width(timeLine->text()) + 10;
2095 timeLine->setFixedWidth(
std::max(100, width));
2188 settings.setValue(
"MainWindow.geometry", saveGeometry());
2189 settings.setValue(
"MainWindow.state", saveState());
2191 settings.setValue(
"MainWindow.activateGrid",
ui->pushButtonActivateGrid->isChecked());
2192 settings.setValue(
"MainWindow.removeTimeOffset",
ui->pushButtonRemoveTimeOffset->isChecked());
2193 settings.setValue(
"MainWindow.dateTimeDisplay",
ui->pushButtonUseDateTime->isChecked());
2194 settings.setValue(
"MainWindow.buttonLink",
ui->pushButtonLink->isChecked());
2195 settings.setValue(
"MainWindow.buttonRatio",
ui->pushButtonRatio->isChecked());
2197 settings.setValue(
"MainWindow.streamingBufferValue",
ui->streamingSpinBox->value());
2198 settings.setValue(
"MainWindow.timeTrackerSetting", (
int)
_tracker_param);
2199 settings.setValue(
"MainWindow.splitterWidth",
ui->mainSplitter->sizes()[0]);
2204 ui->widgetStack->setCurrentIndex(1);
2211 ui->widgetStack->setCurrentIndex(1);
2215 qWarning(
"failed to find custom equation");
2228 qWarning(
"failed to find custom equation");
2238 catch (
const std::runtime_error& e)
2240 QMessageBox::critical(
this,
"error",
"Failed to refresh data : " + QString::fromStdString(e.what()));
2247 qint64 delta_ms = (QDateTime::currentMSecsSinceEpoch() -
_prev_publish_time.toMSecsSinceEpoch());
2252 if (_tracker_time >=
ui->timeSlider->getMaximum())
2254 if (!
ui->playbackLoop->isChecked())
2256 ui->pushButtonPlay->setChecked(
false);
2258 _tracker_time =
ui->timeSlider->getMinimum();
2261 auto prev =
ui->timeSlider->blockSignals(
true);
2262 ui->timeSlider->setRealValue(_tracker_time);
2263 ui->timeSlider->blockSignals(prev);
2271 it.second->play(_tracker_time);
2282 const std::string&
name = custom_plot->name();
2288 data_it->second.clear();
2295 catch (std::exception& ex)
2297 QMessageBox::warning(
this, tr(
"Warning"),
2298 tr(
"Failed to create the custom timeseries. Error:\n\n%1").
arg(ex.what()));
2302 ui->widgetStack->setCurrentIndex(0);
2314 custom_it->second = custom_plot;
2323 auto it = std::find_if(curves.begin(), curves.end(),
2326 return info.src_name ==
name;
2329 if (it != curves.end())
2339 QDesktopServices::openUrl(QUrl(
"https://github.com/facontidavide/PlotJuggler/issues"));
2344 QDesktopServices::openUrl(QUrl(
"https://twitter.com/intent/tweet?hashtags=PlotJuggler"));
2349 QDialog* dialog =
new QDialog(
this);
2350 auto ui =
new Ui::AboutDialog();
2351 ui->setupUi(dialog);
2353 ui->label_version->setText(QApplication::applicationVersion());
2354 dialog->setAttribute(Qt::WA_DeleteOnClose);
2364 dialog->restoreGeometry(settings.value(
"Cheatsheet.geometry").toByteArray());
2366 settings.setValue(
"Cheatsheet.geometry", dialog->saveGeometry());
2367 dialog->deleteLater();
2372 QDialog* dialog =
new QDialog(
this);
2373 auto ui =
new Ui::SupportDialog();
2374 ui->setupUi(dialog);
2376 dialog->setAttribute(Qt::WA_DeleteOnClose);
2463 QMessageBox::warning(
this, tr(
"Warning"), tr(
"No plugin was loaded to process a data file\n"));
2469 QString file_extension_filter;
2471 std::set<QString> extensions;
2476 for (QString extension : loader->compatibleFileExtensions())
2478 extensions.insert(extension.toLower());
2482 for (
const auto& it : extensions)
2484 file_extension_filter.append(QString(
" *.") + it);
2487 QString directory_path = settings.value(
"MainWindow.lastDatafileDirectory", QDir::currentPath()).toString();
2489 QFileDialog loadDialog(
this);
2490 loadDialog.setFileMode(QFileDialog::ExistingFiles);
2491 loadDialog.setViewMode(QFileDialog::Detail);
2492 loadDialog.setNameFilter(file_extension_filter);
2493 loadDialog.setDirectory(directory_path);
2495 QStringList fileNames;
2496 if (loadDialog.exec())
2498 fileNames = loadDialog.selectedFiles();
2501 if (fileNames.isEmpty())
2506 directory_path = QFileInfo(fileNames[0]).absolutePath();
2507 settings.setValue(
"MainWindow.lastDatafileDirectory", directory_path);
2519 QString directory_path = settings.value(
"MainWindow.lastLayoutDirectory", QDir::currentPath()).toString();
2520 QString filename = QFileDialog::getOpenFileName(
this,
"Open Layout", directory_path,
"*.xml");
2521 if (filename.isEmpty())
2531 directory_path = QFileInfo(filename).absolutePath();
2532 settings.setValue(
"MainWindow.lastLayoutDirectory", directory_path);
2541 QString directory_path = settings.value(
"MainWindow.lastLayoutDirectory", QDir::currentPath()).toString();
2543 QFileDialog saveDialog(
this);
2544 saveDialog.setOption(QFileDialog::DontUseNativeDialog,
true);
2546 QGridLayout* save_layout =
static_cast<QGridLayout*
>(saveDialog.layout());
2548 QFrame* frame =
new QFrame;
2549 frame->setFrameStyle(QFrame::Box | QFrame::Plain);
2550 frame->setLineWidth(1);
2552 QVBoxLayout* vbox =
new QVBoxLayout;
2553 QLabel* title =
new QLabel(
"Save Layout options");
2554 QFrame* separator =
new QFrame;
2555 separator->setFrameStyle(QFrame::HLine | QFrame::Plain);
2557 auto checkbox_datasource =
new QCheckBox(
"Save data source");
2558 checkbox_datasource->setToolTip(
"ant the layout to remember the source of your data,\n" 2559 "i.e. the Datafile used or the Streaming Plugin loaded ?");
2560 checkbox_datasource->setFocusPolicy(Qt::NoFocus);
2561 checkbox_datasource->setChecked(settings.value(
"MainWindow.saveLayoutDataSource",
true).toBool());
2563 auto checkbox_snippets =
new QCheckBox(
"Save custom transformations");
2564 checkbox_snippets->setToolTip(
"Do you want the layout to save the custom transformations?");
2565 checkbox_snippets->setFocusPolicy(Qt::NoFocus);
2566 checkbox_snippets->setChecked(settings.value(
"MainWindow.saveLayoutSnippets",
true).toBool());
2568 vbox->addWidget(title);
2569 vbox->addWidget(separator);
2570 vbox->addWidget(checkbox_datasource);
2571 vbox->addWidget(checkbox_snippets);
2572 frame->setLayout(vbox);
2574 int rows = save_layout->rowCount();
2575 int col = save_layout->columnCount();
2576 save_layout->addWidget(frame, 0, col, rows, 1, Qt::AlignTop);
2578 saveDialog.setAcceptMode(QFileDialog::AcceptSave);
2579 saveDialog.setDefaultSuffix(
"xml");
2580 saveDialog.setNameFilter(
"XML (*.xml)");
2581 saveDialog.setDirectory(directory_path);
2584 if (saveDialog.result() != QDialog::Accepted || saveDialog.selectedFiles().empty())
2589 QString fileName = saveDialog.selectedFiles().first();
2591 if (fileName.isEmpty())
2596 directory_path = QFileInfo(fileName).absolutePath();
2597 settings.setValue(
"MainWindow.lastLayoutDirectory", directory_path);
2598 settings.setValue(
"MainWindow.saveLayoutDataSource", checkbox_datasource->isChecked());
2599 settings.setValue(
"MainWindow.saveLayoutSnippets", checkbox_snippets->isChecked());
2601 QDomElement root = doc.namedItem(
"root").toElement();
2604 root.appendChild(doc.createComment(
" - - - - - - - - - - - - - - "));
2606 root.appendChild(doc.createComment(
" - - - - - - - - - - - - - - "));
2610 root.appendChild(doc.createComment(
" - - - - - - - - - - - - - - "));
2612 if (checkbox_datasource->isChecked())
2614 QDomElement loaded_list = doc.createElement(
"previouslyLoaded_Datafiles");
2618 QDomElement file_elem = doc.createElement(
"fileInfo");
2619 file_elem.setAttribute(
"filename", loaded.filename);
2620 file_elem.setAttribute(
"prefix", loaded.prefix);
2622 QDomElement datasources_elem = doc.createElement(
"selected_datasources");
2623 QString topics_list = loaded.selected_datasources.join(
";");
2624 datasources_elem.setAttribute(
"value", topics_list);
2625 file_elem.appendChild(datasources_elem);
2627 file_elem.appendChild(loaded.plugin_config.firstChild());
2628 loaded_list.appendChild(file_elem);
2630 root.appendChild(loaded_list);
2634 QDomElement loaded_streamer = doc.createElement(
"previouslyLoaded_Streamer");
2636 loaded_streamer.setAttribute(
"name", streamer_name);
2637 root.appendChild(loaded_streamer);
2641 root.appendChild(doc.createComment(
" - - - - - - - - - - - - - - "));
2642 if (checkbox_snippets->isChecked())
2644 QDomElement custom_equations = doc.createElement(
"customMathEquations");
2647 const auto& custom_plot = custom_it.second;
2648 custom_equations.appendChild(custom_plot->xmlSaveState(doc));
2650 root.appendChild(custom_equations);
2652 QByteArray snippets_xml_text = settings.value(
"AddCustomPlotDialog.savedXML", QByteArray()).toByteArray();
2655 root.appendChild(snippets_root);
2657 root.appendChild(doc.createComment(
" - - - - - - - - - - - - - - "));
2659 QFile file(fileName);
2660 if (file.open(QIODevice::WriteOnly))
2662 QTextStream stream(&file);
2663 stream << doc.toString() << endl;
2669 static bool first_call =
true;
2673 QMessageBox::information(
this,
"Remember!",
"Press F10 to switch back to the normal view");
2692 for (QAction* action : menu->actions())
2694 if (action->isSeparator())
2698 menu->removeAction(action);
2700 menu->setEnabled(
false);
2702 settings.setValue(
"MainWindow.recentlyLoadedDatafile", {});
2708 for (QAction* action : menu->actions())
2710 if (action->isSeparator())
2714 menu->removeAction(action);
2716 menu->setEnabled(
false);
2718 settings.setValue(
"MainWindow.recentlyLoadedLayout", {});
2723 QMessageBox msgBox(
this);
2724 msgBox.setWindowTitle(
"Warning. Can't be undone.");
2725 msgBox.setText(tr(
"Do you want to remove the previously loaded data?\n"));
2726 msgBox.addButton(QMessageBox::No);
2727 msgBox.addButton(QMessageBox::Yes);
2728 msgBox.setDefaultButton(QMessageBox::Yes);
2730 auto reply = msgBox.exec();
2732 if (reply == QMessageBox::No)
2763 QString prev_style = settings.value(
"Preferences::theme",
"light").toString();
2768 QString theme = settings.value(
"Preferences::theme").toString();
2770 if (theme != prev_style)
2778 ui->timeSlider->setFocus();
2779 ui->timeSlider->setRealStepValue(step);
2785 QString directory_path = settings.value(
"MainWindow.loadStyleSheetDirectory", QDir::currentPath()).toString();
2787 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load StyleSheet"), directory_path, tr(
"(*.qss)"));
2788 if (fileName.isEmpty())
2795 directory_path = QFileInfo(fileName).absolutePath();
2796 settings.setValue(
"MainWindow.loadStyleSheetDirectory", directory_path);
2809 case LabelStatus::HIDDEN:
2820 plot->setLegendAlignment(Qt::AlignLeft);
2824 plot->setLegendAlignment(Qt::AlignRight);
2836 plot->zoomOut(
false);
2845 settings.setValue(
"MainWindow.previousStreamingPlugin", current_text );
2847 ui->buttonStreamingOptions->setEnabled( !streamer->availableActions().empty() );
2852 ui->buttonStreamingStart->setEnabled(
false);
2853 if(
ui->buttonStreamingStart->text() ==
"Start")
2860 ui->buttonStreamingStart->setEnabled(
true);
2865 QMenu(parent), _w(relative_widget)
2871 QPoint p =
_w->mapToGlobal( {} );
2872 QRect geo =
_w->geometry();
2873 this->
move( p.x() + geo.width(), p.y() );
2883 _w->setAttribute(Qt::WA_UnderMouse,
false);
2890 for(
auto action: _recent_data_files->actions()) {
2891 menu->addAction(action);
2898 auto streamer = _data_streamer.at( ui->comboStreaming->currentText() );
2901 for(
auto action: streamer->availableActions() ) {
2902 menu->addAction(action);
2910 bool hidden = !ui->frameFile->isHidden();
2911 ui->buttonHideFileFrame->setText( hidden ?
"+" :
" -");
2912 ui->frameFile->setHidden( hidden );
2915 settings.setValue(
"MainWindow.hiddenFileFrame", hidden);
2920 bool hidden = !ui->frameStreaming->isHidden();
2921 ui->buttonHideStreamingFrame->setText( hidden ?
"+" :
" -");
2922 ui->frameStreaming->setHidden( hidden );
2925 settings.setValue(
"MainWindow.hiddenStreamingFrame", hidden);
2930 bool hidden = !ui->framePublishers->isHidden();
2931 ui->buttonHidePublishersFrame->setText( hidden ?
"+" :
" -");
2932 ui->framePublishers->setHidden( hidden );
2935 settings.setValue(
"MainWindow.hiddenPublishersFrame", hidden);
2942 for(
auto action: _recent_layout_files->actions()) {
2943 menu->addAction(action);
void on_actionExit_triggered()
const Point & back() const
QTimer * _animated_streaming_timer
void on_comboStreaming_currentIndexChanged(const QString ¤t_text)
void on_actionClearBuffer_triggered()
void onActionFullscreenTriggered()
enum MQTTPropertyCodes value
QShortcut _streaming_shortcut
void on_actionSupportPlotJuggler_triggered()
void dataSourceRemoved(const std::string &name)
void on_pushButtonLoadDatafile_clicked()
void on_actionShare_the_love_triggered()
QShortcut _fullscreen_shortcut
PlotWidget * plotAt(int index)
void onEditCustomPlot(const std::string &plot_name)
void on_buttonStreamingStart_clicked()
void onPlotTabAdded(PlotDocker *docker)
void editMathPlot(const std::string &plot_name)
void on_actionPreferences_triggered()
const Point & front() const
void plotWidgetAdded(PlotWidget *)
QIcon LoadSvgIcon(QString filename, QString style_name="light")
CurveListPanel * _curvelist_widget
void on_stylesheetChanged(QString style_name)
void updatedDisplayTime()
std::vector< FileLoadInfo > _loaded_datafiles
void onCustomPlotCreated(CustomPlotPtr plot)
void resizeEvent(QResizeEvent *)
bool loadDataFromFiles(QStringList filenames)
void checkAllCurvesFromLayout(const QDomElement &root)
PlotDataMapRef _mapped_plot_data
void closeEvent(QCloseEvent *event)
std::unordered_map< std::string, PlotData > numeric
std::map< CurveTracker::Parameter, QIcon > _tracker_button_icons
bool xmlLoadState(QDomDocument state_document)
bool loadLayoutFromFile(QString filename)
void forEachWidget(std::function< void(PlotWidget *, PlotDocker *, int)> op)
void on_actionReportBug_triggered()
The DataStreamer base class to create your own plugin.
std::map< QString, std::shared_ptr< MessageParserCreator > > _message_parser_factory
QStringList selected_datasources
void on_actionClearRecentLayout_triggered()
void on_actionCheatsheet_triggered()
void onTrackerMovedFromWidget(QPointF pos)
void on_pushButtonZoomOut_clicked()
QMovie * _animated_streaming_movie
void on_actionClearRecentData_triggered()
std::deque< QDomDocument > _redo_states
void onPlotAdded(PlotWidget *plot)
void updateDataAndReplot(bool replot_hidden_tabs)
void refreshMathPlot(const std::string &curve_name)
void onTimeSlider_valueChanged(double abs_time)
void createMathPlot(const std::string &linked_plot)
QStringList initializePlugins(QString subdir_name)
std::map< QString, DataStreamerPtr > _data_streamer
void startStreamingPlugin(QString streamer_name)
MainWindow(const QCommandLineParser &commandline_parser, QWidget *parent=nullptr)
std::map< QString, StatePublisherPtr > _state_publisher
QDomElement ExportSnippets(const SnippetsMap &snippets, QDomDocument &doc)
void addCurve(const QString &item_name)
void on_pushButtonPlay_toggled(bool checked)
void onPlotZoomChanged(PlotWidget *modified_plot, QRectF new_range)
QElapsedTimer _undo_timer
bool loadDataFromFile(const FileLoadInfo &info)
void realValueChanged(double)
void importPlotDataMapHelper(std::unordered_map< std::string, T > &source, std::unordered_map< std::string, T > &destination, bool delete_older)
void on_pushButtonLegend_clicked()
void on_pushButtonLoadLayout_clicked()
void onTrackerTimeUpdated(double absolute_time, bool do_replot)
TabbedPlotWidget * _main_tabbed_widget
void updateRecentLayoutMenu(QStringList new_filenames)
QShortcut _playback_shotcut
void on_buttonHidePublishersFrame_clicked()
void loadPluginState(const QDomElement &root)
void loadStyleSheet(QString file_path)
std::shared_ptr< CustomFunction > CustomPlotPtr
void deleteCurves(const std::vector< std::string > &curve_names)
static void block(LexState *ls)
void on_actionLoadStyleSheet_triggered()
void on_buttonHideFileFrame_clicked()
void on_stylesheetChanged(QString theme)
SnippetsMap GetSnippetsFromXML(const QString &xml_text)
void on_tabbedAreaDestroyed(QObject *object)
static const QString LAYOUT_VERSION
LabelStatus _labels_status
QDateTime _prev_publish_time
std::shared_ptr< DataStreamer > DataStreamerPtr
CurveTracker::Parameter _tracker_param
void on_pushButtonRatio_toggled(bool checked)
void on_streamingSpinBox_valueChanged(int value)
QMenu * _recent_data_files
void stopStreamingPlugin()
void stylesheetChanged(QString style_name)
void onAddCustomPlot(const std::string &plot_name)
QDomElement savePluginState(QDomDocument &doc)
bool isStreamingActive() const
void on_actionDeleteAllData_triggered()
void on_buttonRecentData_clicked()
void on_buttonHideStreamingFrame_clicked()
CustomPlotMap _custom_plots
virtual bool isDebugPlugin()
virtual size_t size() const
FunctionEditorWidget * _function_editor
void onDeleteMultipleCurves(const std::vector< std::string > &curve_names)
detail::named_arg< Char, T > arg(const Char *name, const T &arg)
const T & move(const T &v)
void addCustom(const QString &item_name)
bool _autostart_publishers
std::pair< std::vector< QString >, bool > MoveData(PlotDataMapRef &source, PlotDataMapRef &destination)
MonitoredValue _time_offset
static CustomPlotPtr createFromXML(QDomElement &element)
void on_actionAbout_triggered()
std::deque< QDomDocument > _undo_states
void set(double newValue)
std::shared_ptr< StatePublisher > StatePublisherPtr
std::map< QString, DataLoaderPtr > _data_loader
void on_buttonRecentLayout_clicked()
void on_streamingToggled()
std::shared_ptr< DataStreamer > _active_streamer_plugin
void on_stylesheetChanged(QString theme)
std::unordered_map< std::string, PlotDataAny > user_defined
void hiddenItemsChanged()
void on_pushButtonUseDateTime_toggled(bool checked)
void pushBack(const Point &p)
void onRefreshCustomPlot(const std::string &plot_name)
void on_buttonStreamingPause_toggled(bool paused)
virtual const char * name() const =0
void AddPrefixToPlotData(const std::string &prefix, std::unordered_map< std::string, Value > &data)
std::unordered_map< std::string, PlotData >::iterator addNumeric(const std::string &name)
void on_pushButtonActivateGrid_toggled(bool checked)
void importPlotDataMap(PlotDataMapRef &new_data, bool remove_old)
void on_buttonStreamingOptions_clicked()
void on_playbackStep_valueChanged(double arg1)
QDomDocument plugin_config
void on_pushButtonTimeTracker_pressed()
bool _disable_undo_logging
QMenu * _recent_layout_files
void update2ndColumnValues(double time, std::unordered_map< std::string, PlotData > *numeric_data)
QDomDocument xmlSaveState() const
QString SetApplicationStyleSheet(QString style)
void on_splitterMoved(int, int)
void on_pushButtonRemoveTimeOffset_toggled(bool checked)
typename PlotDataBase< Value >::Point Point
std::shared_ptr< DataLoader > DataLoaderPtr
void on_pushButtonSaveLayout_clicked()
void updateRecentDataMenu(QStringList new_filenames)
void onUpdateLeftTableValues()
void removeCurve(const std::string &name)
std::tuple< double, double, int > calculateVisibleRangeX()