main.cpp
Go to the documentation of this file.
1 /*
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5  */
6 
7 #include "mainwindow.h"
8 #include <iostream>
9 #include <QApplication>
10 #include <QSplashScreen>
11 #include <QThread>
12 #include <QCommandLineParser>
13 #include <QDesktopWidget>
14 #include <QFontDatabase>
15 #include <QSettings>
16 #include <QPushButton>
17 #include <QNetworkAccessManager>
18 #include <QNetworkReply>
19 #include <QJsonDocument>
20 #include <QDir>
21 #include <QDialog>
22 #include <QUuid>
23 #include <QDesktopServices>
24 
31 #include "transforms/moving_rms.h"
35 
36 #include "new_release_dialog.h"
37 
38 #ifdef COMPILED_WITH_CATKIN
39 #include <ros/ros.h>
40 #endif
41 #ifdef COMPILED_WITH_AMENT
42 #include <rclcpp/rclcpp.hpp>
43 #endif
44 
45 static QString VERSION_STRING =
46  QString("%1.%2.%3").arg(PJ_MAJOR_VERSION).arg(PJ_MINOR_VERSION).arg(PJ_PATCH_VERSION);
47 
48 inline int GetVersionNumber(QString str)
49 {
50  QStringList online_version = str.split('.');
51  if (online_version.size() != 3)
52  {
53  return 0;
54  }
55  int major = online_version[0].toInt();
56  int minor = online_version[1].toInt();
57  int patch = online_version[2].toInt();
58  return major * 10000 + minor * 100 + patch;
59 }
60 
61 void OpenNewReleaseDialog(QNetworkReply* reply)
62 {
63  if (reply->error())
64  {
65  qDebug() << "reply error";
66  return;
67  }
68 
69  QString answer = reply->readAll();
70  QJsonDocument document = QJsonDocument::fromJson(answer.toUtf8());
71  QJsonObject data = document.object();
72  QString url = data["html_url"].toString();
73  QString name = data["name"].toString();
74  QString tag_name = data["tag_name"].toString();
75  QSettings settings;
76  int online_number = GetVersionNumber(tag_name);
77  QString dont_show =
78  settings.value("NewRelease/dontShowThisVersion", VERSION_STRING).toString();
79  int dontshow_number = GetVersionNumber(dont_show);
80  int current_number = GetVersionNumber(VERSION_STRING);
81 
82  if (online_number > current_number && online_number > dontshow_number)
83  {
84  NewReleaseDialog* dialog = new NewReleaseDialog(nullptr, tag_name, name, url);
85  dialog->exec();
86  }
87 }
88 
90 {
91  QSettings settings;
92  srand(time(nullptr));
93 
94  auto getNum = []() {
95  const int last_image_num = 94;
96  return rand() % (last_image_num);
97  };
98  std::set<int> previous_set;
99  std::list<int> previous_nums;
100 
101  QStringList previous_list = settings.value("previousFunnyMemesList").toStringList();
102  for (auto str : previous_list)
103  {
104  int num = str.toInt();
105  previous_set.insert(num);
106  previous_nums.push_back(num);
107  }
108 
109  int n = getNum();
110  while (previous_set.count(n) != 0)
111  {
112  n = getNum();
113  }
114 
115  while (previous_nums.size() >= 10)
116  {
117  previous_nums.pop_front();
118  }
119  previous_nums.push_back(n);
120 
121  QStringList new_list;
122  for (int num : previous_nums)
123  {
124  new_list.push_back(QString::number(num));
125  }
126 
127  settings.setValue("previousFunnyMemesList", new_list);
128  auto filename = QString("://resources/memes/meme_%1.jpg").arg(n, 2, 10, QChar('0'));
129  return QPixmap(filename);
130 }
131 
132 std::vector<std::string> MergeArguments(const std::vector<std::string>& args)
133 {
134 #ifdef PJ_DEFAULT_ARGS
135  auto default_cmdline_args = QString(PJ_DEFAULT_ARGS).split(" ", PJ::SkipEmptyParts);
136 
137  std::vector<std::string> new_args;
138  new_args.push_back(args.front());
139 
140  // Add the remain arguments, replacing escaped characters if necessary.
141  // Escaping needed because some chars cannot be entered easily in the -DPJ_DEFAULT_ARGS
142  // preprocessor directive
143  // _0x20_ --> ' ' (space)
144  // _0x3b_ --> ';' (semicolon)
145  for (auto cmdline_arg : default_cmdline_args)
146  {
147  // replace(const QString &before, const QString &after, Qt::CaseSensitivity cs =
148  // Qt::CaseSensitive)
149  cmdline_arg = cmdline_arg.replace("_0x20_", " ", Qt::CaseSensitive);
150  cmdline_arg = cmdline_arg.replace("_0x3b_", ";", Qt::CaseSensitive);
151  new_args.push_back(strdup(cmdline_arg.toLocal8Bit().data()));
152  }
153 
154  // If an argument appears repeated, the second value overrides previous one.
155  // Do this after adding default_cmdline_args so the command-line override default
156  for (size_t i = 1; i < args.size(); ++i)
157  {
158  new_args.push_back(args[i]);
159  }
160 
161  return new_args;
162 
163 #else
164  return args;
165 #endif
166 }
167 
168 int main(int argc, char* argv[])
169 {
170  std::vector<std::string> args;
171 
172 #if !defined(COMPILED_WITH_CATKIN) && !defined(COMPILED_WITH_AMENT)
173  for (int i = 0; i < argc; i++)
174  {
175  args.push_back(argv[i]);
176  }
177 #elif defined(COMPILED_WITH_CATKIN)
178  ros::removeROSArgs(argc, argv, args);
179 #elif defined(COMPILED_WITH_AMENT)
180  args = rclcpp::remove_ros_arguments(argc, argv);
181 #endif
182 
184 
185  int new_argc = args.size();
186  std::vector<char*> new_argv;
187  for (int i = 0; i < new_argc; i++)
188  {
189  new_argv.push_back(args[i].data());
190  }
191 
192  QApplication app(new_argc, new_argv.data());
193 
194  //-------------------------
195 
196  QCoreApplication::setOrganizationName("PlotJuggler");
197  QCoreApplication::setApplicationName("PlotJuggler-3");
198  QSettings::setDefaultFormat(QSettings::IniFormat);
199 
200  QSettings settings;
201 
202  if (!settings.isWritable())
203  {
204  qDebug() << "ERROR: the file [" << settings.fileName()
205  << "] is not writable. This may happen when you run PlotJuggler with sudo. "
206  "Change the permissions of the file (\"sudo chmod 666 <file_name>\"on "
207  "linux)";
208  }
209 
210  app.setApplicationVersion(VERSION_STRING);
211 
212  //---------------------------
213  TransformFactory::registerTransform<FirstDerivative>();
214  TransformFactory::registerTransform<ScaleTransform>();
215  TransformFactory::registerTransform<MovingAverageFilter>();
216  TransformFactory::registerTransform<MovingRMS>();
217  TransformFactory::registerTransform<OutlierRemovalFilter>();
218  TransformFactory::registerTransform<IntegralTransform>();
219  TransformFactory::registerTransform<AbsoluteTransform>();
220  TransformFactory::registerTransform<MovingVarianceFilter>();
221  TransformFactory::registerTransform<SamplesCountFilter>();
222  //---------------------------
223 
224  QCommandLineParser parser;
225  parser.setApplicationDescription("PlotJuggler: the time series visualization"
226  " tool that you deserve ");
227  parser.addVersionOption();
228  parser.addHelpOption();
229 
230  QCommandLineOption nosplash_option(QStringList() << "n"
231  << "nosplash",
232  "Don't display the splashscreen");
233  parser.addOption(nosplash_option);
234 
235  QCommandLineOption test_option(QStringList() << "t"
236  << "test",
237  "Generate test curves at startup");
238  parser.addOption(test_option);
239 
240  QCommandLineOption loadfile_option(QStringList() << "d"
241  << "datafile",
242  "Load a file containing data", "file_path");
243  parser.addOption(loadfile_option);
244 
245  QCommandLineOption layout_option(QStringList() << "l"
246  << "layout",
247  "Load a file containing the layout configuration",
248  "file_path");
249  parser.addOption(layout_option);
250 
251  QCommandLineOption publish_option(QStringList() << "p"
252  << "publish",
253  "Automatically start publisher when loading the "
254  "layout file");
255  parser.addOption(publish_option);
256 
257  QCommandLineOption folder_option(QStringList() << "plugin_folders",
258  "Add semicolon-separated list of folders where you "
259  "should look "
260  "for additional plugins.",
261  "directory_paths");
262  parser.addOption(folder_option);
263 
264  QCommandLineOption buffersize_option(QStringList() << "buffer_size",
265  QCoreApplication::translate("main", "Change the "
266  "maximum size "
267  "of the "
268  "streaming "
269  "buffer "
270  "(minimum: 10 "
271  "default: "
272  "60)"),
273  QCoreApplication::translate("main", "seconds"));
274 
275  parser.addOption(buffersize_option);
276 
277  QCommandLineOption nogl_option(QStringList() << "disable_opengl", "Disable OpenGL "
278  "display before "
279  "starting the "
280  "application. "
281  "You can enable it "
282  "again in the "
283  "'Preferences' "
284  "menu.");
285 
286  parser.addOption(nogl_option);
287 
288  QCommandLineOption enabled_plugins_option(QStringList() << "enabled_plugins",
289  "Limit the loaded plugins to ones in the "
290  "semicolon-separated list",
291  "name_list");
292  parser.addOption(enabled_plugins_option);
293 
294  QCommandLineOption disabled_plugins_option(QStringList() << "disabled_plugins",
295  "Do not load any of the plugins in the "
296  "semicolon separated list",
297  "name_list");
298  parser.addOption(disabled_plugins_option);
299 
300  QCommandLineOption skin_path_option(QStringList() << "skin_path",
301  "New \"skin\". Refer to the sample in "
302  "[plotjuggler_app/resources/skin]",
303  "path to folder");
304  parser.addOption(skin_path_option);
305 
306  QCommandLineOption start_streamer(QStringList() << "start_streamer",
307  "Automatically start a Streaming Plugin with the "
308  "give filename",
309  "file_name (no extension)");
310  parser.addOption(start_streamer);
311 
312  QCommandLineOption window_title(QStringList() << "window_title", "Set the window title",
313  "window_title");
314  parser.addOption(window_title);
315 
316  parser.process(*qApp);
317 
318  if (parser.isSet(publish_option) && !parser.isSet(layout_option))
319  {
320  std::cerr << "Option [ -p / --publish ] is invalid unless [ -l / --layout ] is used "
321  "too."
322  << std::endl;
323  return -1;
324  }
325 
326  if (parser.isSet(enabled_plugins_option) && parser.isSet(disabled_plugins_option))
327  {
328  std::cerr << "Option [ --enabled_plugins ] and [ --disabled_plugins ] can't be used "
329  "together."
330  << std::endl;
331  return -1;
332  }
333 
334  if (parser.isSet(nogl_option))
335  {
336  settings.setValue("Preferences::use_opengl", false);
337  }
338 
339  if (parser.isSet(skin_path_option))
340  {
341  QDir path(parser.value(skin_path_option));
342  if (!path.exists())
343  {
344  qDebug() << "Skin path [" << parser.value(skin_path_option) << "] not found";
345  return -1;
346  }
347  }
348 
349  QIcon app_icon("://resources/plotjuggler.svg");
350  QApplication::setWindowIcon(app_icon);
351 
352  QNetworkAccessManager manager_new_release;
353  QObject::connect(&manager_new_release, &QNetworkAccessManager::finished,
355 
356  QNetworkRequest request_new_release;
357  request_new_release.setUrl(QUrl("https://api.github.com/repos/facontidavide/"
358  "PlotJuggler/releases/latest"));
359  manager_new_release.get(request_new_release);
360 
361  MainWindow* window = nullptr;
362 
363  /*
364  * You, fearless code reviewer, decided to start a journey into my source code.
365  * For your bravery, you deserve to know the truth.
366  * The splashscreen is useless; not only it is useless, it will make your start-up
367  * time slower by few seconds for absolutely no reason.
368  * But what are two seconds compared with the time that PlotJuggler will save you?
369  * The splashscreen is the connection between me and my users, the glue that keeps
370  * together our invisible relationship.
371  * Now, it is up to you to decide: you can block the splashscreen forever or not,
372  * reject a message that brings a little of happiness into your day, spent analyzing
373  * data. Please don't do it.
374  */
375  if (!parser.isSet(nosplash_option) &&
376  !(parser.isSet(loadfile_option) || parser.isSet(layout_option)))
377  // if(false) // if you uncomment this line, a kitten will die somewhere in the world.
378  {
379  QPixmap main_pixmap;
380 
381  if (parser.isSet(skin_path_option))
382  {
383  QDir path(parser.value(skin_path_option));
384  QFile splash = path.filePath("pj_splashscreen.png");
385  if (splash.exists())
386  {
387  main_pixmap = QPixmap(splash.fileName());
388  }
389  }
390 
391  if (main_pixmap.isNull())
392  {
393  main_pixmap = getFunnySplashscreen();
394  }
395 
396  QSplashScreen splash(main_pixmap, Qt::WindowStaysOnTopHint);
397  QDesktopWidget* desktop = QApplication::desktop();
398  const int scrn = desktop->screenNumber();
399  const QPoint currentDesktopsCenter = desktop->availableGeometry(scrn).center();
400  splash.move(currentDesktopsCenter - splash.rect().center());
401 
402  splash.show();
403  app.processEvents();
404 
405  auto deadline = QDateTime::currentDateTime().addMSecs(500);
406  while (QDateTime::currentDateTime() < deadline)
407  {
408  app.processEvents();
409  }
410 
411  window = new MainWindow(parser);
412 
413  deadline = QDateTime::currentDateTime().addMSecs(3000);
414  while (QDateTime::currentDateTime() < deadline && !splash.isHidden())
415  {
416  app.processEvents();
417  }
418  }
419 
420  if (!window)
421  {
422  window = new MainWindow(parser);
423  }
424 
425  window->show();
426 
427  if (parser.isSet(start_streamer))
428  {
430  }
431 
432  QNetworkAccessManager manager_message;
433  QObject::connect(&manager_message, &QNetworkAccessManager::finished,
434  [window](QNetworkReply* reply) {
435  if (reply->error())
436  {
437  return;
438  }
439  QString answer = reply->readAll();
440  QJsonDocument document = QJsonDocument::fromJson(answer.toUtf8());
441  QJsonObject data = document.object();
442  QString message = data["message"].toString();
443  window->setStatusBarMessage(message);
444  });
445 
446  QNetworkRequest request_message;
447  request_message.setUrl(QUrl("https://fastapi-example-7kz3.onrender.com"));
448  manager_message.get(request_message);
449 
450  return app.exec();
451 }
getFunnySplashscreen
QPixmap getFunnySplashscreen()
Definition: main.cpp:89
MergeArguments
std::vector< std::string > MergeArguments(const std::vector< std::string > &args)
Definition: main.cpp:132
MainWindow::setStatusBarMessage
void setStatusBarMessage(QString message)
Definition: mainwindow.cpp:1789
ros.h
VERSION_STRING
static QString VERSION_STRING
Definition: main.cpp:45
PJ::SkipEmptyParts
const auto SkipEmptyParts
Definition: plotdatabase.h:31
moving_average_filter.h
OpenNewReleaseDialog
void OpenNewReleaseDialog(QNetworkReply *reply)
Definition: main.cpp:61
transform_function.h
moving_rms.h
MainWindow
Definition: mainwindow.h:37
moving_variance.h
integral_transform.h
mqtt_test.time
float time
Definition: mqtt_test.py:17
absolute_transform.h
MainWindow::on_buttonStreamingStart_clicked
void on_buttonStreamingStart_clicked()
Definition: mainwindow.cpp:3392
udp_client.parser
parser
Definition: udp_client.py:10
new_release_dialog.h
samples_count.h
GetVersionNumber
int GetVersionNumber(QString str)
Definition: main.cpp:48
scale_transform.h
first_derivative.h
mqtt_test.data
dictionary data
Definition: mqtt_test.py:22
NewReleaseDialog
Definition: new_release_dialog.h:17
ros::removeROSArgs
ROSCPP_DECL void removeROSArgs(int argc, const char *const *argv, V_string &args_out)
mainwindow.h
outlier_removal.h
main
int main(int argc, char *argv[])
Definition: main.cpp:168
udp_client.args
args
Definition: udp_client.py:14


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon May 26 2025 02:22:37