plotwidget.cpp
Go to the documentation of this file.
1 #include <QAction>
2 #include <QActionGroup>
3 #include <QApplication>
4 #include <QDebug>
5 #include <QDrag>
6 #include <QDragEnterEvent>
7 #include <QDragMoveEvent>
8 #include <QFileDialog>
9 #include <QFileInfo>
10 #include <QMessageBox>
11 #include <QMenu>
12 #include <QMimeData>
13 #include <QPainter>
14 #include <QPushButton>
15 #include <QWheelEvent>
16 #include <QSettings>
17 #include <QSvgGenerator>
18 #include <QClipboard>
19 #include <iostream>
20 #include <limits>
21 #include <set>
22 #include <memory>
23 #include <QtXml/QDomElement>
24 #include "qwt_scale_widget.h"
25 #include "qwt_plot_canvas.h"
26 #include "qwt_plot_opengl_canvas.h"
27 #include "qwt_scale_engine.h"
28 #include "qwt_scale_map.h"
29 #include "qwt_plot_layout.h"
30 #include "qwt_scale_draw.h"
31 #include "qwt_text.h"
32 #include "plotwidget.h"
33 #include "qwt_plot_renderer.h"
34 #include "qwt_series_data.h"
35 #include "qwt_date_scale_draw.h"
36 #include "point_series_xy.h"
37 #include "suggest_dialog.h"
40 #include "svg_util.h"
41 #include "plotwidget_editor.h"
42 #include "plotwidget_transforms.h"
43 
45 
46 
48 {
49  QSettings settings;
50  bool remember_color = settings.value("Preferences::remember_color", true).toBool();
51  if (data && remember_color && data->getColorHint() != Qt::black)
52  {
53  return data->getColorHint();
54  }
55  QColor color;
56  bool use_plot_color_index = settings.value("Preferences::use_plot_color_index", false).toBool();
57  int index = _curve_list.size();
58 
59  if (!use_plot_color_index)
60  {
62  }
63 
64  // https://matplotlib.org/3.1.1/users/dflt_style_changes.html
65  switch (index % 8)
66  {
67  case 0:
68  color = QColor("#1f77b4");
69  break;
70  case 1:
71  color = QColor("#d62728");
72  break;
73  case 2:
74  color = QColor("#1ac938");
75  break;
76  case 3:
77  color = QColor("#ff7f0e");
78  break;
79 
80  case 4:
81  color = QColor("#f14cc1");
82  break;
83  case 5:
84  color = QColor("#9467bd");
85  break;
86  case 6:
87  color = QColor("#17becf");
88  break;
89  case 7:
90  color = QColor("#bcbd22");
91  break;
92  }
93  if (data)
94  {
95  data->setColorHint(color);
96  }
97 
98  return color;
99 }
100 
102 {
103  virtual QwtText label(double v) const
104  {
105  QDateTime dt = QDateTime::fromMSecsSinceEpoch((qint64)(v * 1000));
106  if (dt.date().year() == 1970 && dt.date().month() == 1 && dt.date().day() == 1)
107  {
108  return dt.toString("hh:mm:ss.z");
109  }
110  return dt.toString("hh:mm:ss.z\nyyyy MMM dd");
111  }
112 };
113 
115 
116 static const char* noTransform = "noTransform";
117 static const char* Derivative1st = "1st Derivative";
118 static const char* Derivative2nd = "2nd Derivative";
119 static bool if_xy_plot_failed_show_dialog = true;
120 
122 
124  : QwtPlot(parent)
125  , _zoomer(nullptr)
127  , _panner1(nullptr)
128  , _panner2(nullptr)
129  , _tracker(nullptr)
130  , _legend(nullptr)
131  , _use_date_time_scale(false)
132  , _color_index(0)
133  , _mapped_data(datamap)
134  , _dragging({ DragInfo::NONE, {}, nullptr })
136  , _time_offset(0.0)
137  , _xy_mode(false)
138  , _transform_select_dialog(nullptr)
139  , _zoom_enabled(true)
140  , _keep_aspect_ratio(true)
141  ,_context_menu_enabled(true)
142 {
143  connect(this, &PlotWidget::curveListChanged, this, [this]() { this->updateMaximumZoomArea(); });
144 
145  this->setAcceptDrops(true);
146 
147  this->setMinimumWidth(100);
148  this->setMinimumHeight(100);
149 
150  this->sizePolicy().setHorizontalPolicy(QSizePolicy::Expanding);
151  this->sizePolicy().setVerticalPolicy(QSizePolicy::Expanding);
152 
153  auto canvas = new QwtPlotOpenGLCanvas();
154 
155  canvas->setFrameStyle(QFrame::NoFrame);
156 
157  canvas->setFrameStyle( QFrame::Box | QFrame::Plain );
158  canvas->setLineWidth( 1 );
159  canvas->setPalette( Qt::white );
160 
161  // canvas->setPaintAttribute(QwtPlotCanvas::BackingStore, true);
162 
163  this->setCanvas(canvas);
164  this->setCanvasBackground(Qt::white);
165 
166  this->setAxisAutoScale(QwtPlot::yLeft, true);
167  this->setAxisAutoScale(QwtPlot::xBottom, true);
168 
170  this->plotLayout()->setAlignCanvasToScales(true);
171 
172  //--------------------------
173  _zoomer = (new PlotZoomer(this->canvas()));
174  _magnifier = (new PlotMagnifier(this->canvas()));
175  _panner1 = (new QwtPlotPanner(this->canvas()));
176  _panner2 = (new QwtPlotPanner(this->canvas()));
177  _tracker = (new CurveTracker(this));
178 
179  _grid = new QwtPlotGrid();
180  _grid->setPen(QPen(Qt::gray, 0.0, Qt::DotLine));
181 
182  _zoomer->setRubberBandPen(QColor(Qt::red, 1, Qt::DotLine));
183  _zoomer->setTrackerPen(QColor(Qt::green, 1, Qt::DotLine));
184  _zoomer->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton, Qt::NoModifier);
186 
187  _magnifier->setAxisEnabled(xTop, false);
189 
190  _magnifier->setZoomInKey(Qt::Key_Plus, Qt::ControlModifier);
191  _magnifier->setZoomOutKey(Qt::Key_Minus, Qt::ControlModifier);
192 
193  // disable right button. keep mouse wheel
194  _magnifier->setMouseButton(Qt::NoButton);
195  connect(_magnifier, &PlotMagnifier::rescaled, this, [this](QRectF rect) {
196  on_externallyResized(rect);
197  replot();
198  });
199 
200  _panner1->setMouseButton(Qt::LeftButton, Qt::ControlModifier);
201  _panner2->setMouseButton(Qt::MiddleButton, Qt::NoModifier);
202 
205 
206  //-------------------------
207 
208  buildActions();
209 
210  _legend = new PlotLegend(this);
211 
212  this->canvas()->setMouseTracking(true);
213 
214  setAxisScale(xBottom, 0.0, 1.0);
215  setAxisScale(yLeft, 0.0, 1.0);
216 
219 
220  QwtScaleWidget* bottomAxis = this->axisWidget(xBottom);
221  QwtScaleWidget* leftAxis = this->axisWidget(yLeft);
222 
223  bottomAxis->installEventFilter(this);
224  leftAxis->installEventFilter(this);
225 }
226 
228 {
229  _context_menu_enabled = enabled;
230 }
231 
233 {
234  QIcon iconDeleteList;
235 
236  _action_edit = new QAction("&Edit curves...", this);
237  connect(_action_edit, &QAction::triggered, this,
238  [=]()
239  {
240  auto editor_dialog = new PlotwidgetEditor(this, this);
241  editor_dialog->exec();
242  editor_dialog->deleteLater();
243  } );
244 
245  _action_formula = new QAction("&Apply filter to data...", this);
246  connect(_action_formula, &QAction::triggered, this,
247  [=]()
248  {
249  auto editor_dialog = new DialogTransformEditor(this);
250  int res = editor_dialog->exec();
251  editor_dialog->deleteLater();
252  if( res == QDialog::Accepted){
253  emit undoableChange();
254  }
255  } );
256 
257  _action_split_horizontal = new QAction("&Split Horizontally", this);
258  connect( _action_split_horizontal, &QAction::triggered, this, &PlotWidget::splitHorizontal);
259 
260  _action_split_vertical = new QAction("&Split Vertically", this);
261  connect( _action_split_vertical, &QAction::triggered, this, &PlotWidget::splitVertical);
262 
263  _action_removeAllCurves = new QAction("&Remove ALL curves", this);
264  connect(_action_removeAllCurves, &QAction::triggered, this, &PlotWidget::removeAllCurves);
265  connect(_action_removeAllCurves, &QAction::triggered, this, &PlotWidget::undoableChange);
266 
267  _action_zoomOutMaximum = new QAction("&Zoom Out", this);
268  connect(_action_zoomOutMaximum, &QAction::triggered, this, [this]() {
269  zoomOut(true);
270  replot();
271  emit undoableChange();
272  });
273 
274  _action_zoomOutHorizontally = new QAction("&Zoom Out Horizontally", this);
275  connect(_action_zoomOutHorizontally, &QAction::triggered, this, [this]() {
277  replot();
278  emit undoableChange();
279  });
280 
281  _action_zoomOutVertically = new QAction("&Zoom Out Vertically", this);
282  connect(_action_zoomOutVertically, &QAction::triggered, this, [this]() {
284  replot();
285  emit undoableChange();
286  });
287 
288  QFont font;
289  font.setPointSize(10);
290 
291  _action_saveToFile = new QAction("&Save plot to file", this);
292  connect(_action_saveToFile, &QAction::triggered, this, &PlotWidget::on_savePlotToFile);
293 
294  _action_copy = new QAction("&Copy", this);
295  connect(_action_copy, &QAction::triggered, this, &PlotWidget::on_copyAction_triggered);
296 
297  _action_paste = new QAction("&Paste", this);
298  connect(_action_paste, &QAction::triggered, this, &PlotWidget::on_pasteAction_triggered);
299 
300  _action_image_to_clipboard = new QAction("&Copy image to clipboard", this);
301  connect(_action_image_to_clipboard, &QAction::triggered, this, &PlotWidget::on_copyToClipboard);
302 
303 }
304 
306 {
307  if( _context_menu_enabled == false )
308  {
309  return;
310  }
311 
312  QSettings settings;
313  QString theme = settings.value("StyleSheet::theme", "light").toString();
314 
315  _action_removeAllCurves->setIcon( LoadSvgIcon(":/resources/svg/remove_red.svg", theme) );
316  _action_edit->setIcon( LoadSvgIcon(":/resources/svg/pencil-edit.svg", theme) );
317  _action_formula->setIcon( LoadSvgIcon(":/resources/svg/Fx.svg", theme) );
318  _action_split_horizontal->setIcon( LoadSvgIcon(":/resources/svg/add_column.svg", theme) );
319  _action_split_vertical->setIcon( LoadSvgIcon(":/resources/svg/add_row.svg", theme) );
320  _action_zoomOutMaximum->setIcon( LoadSvgIcon(":/resources/svg/zoom_max.svg", theme) );
321  _action_zoomOutHorizontally->setIcon( LoadSvgIcon(":/resources/svg/zoom_horizontal.svg", theme) );
322  _action_zoomOutVertically->setIcon( LoadSvgIcon(":/resources/svg/zoom_vertical.svg", theme) );
323  _action_copy->setIcon( LoadSvgIcon(":/resources/svg/copy.svg", theme) );
324  _action_paste->setIcon( LoadSvgIcon(":/resources/svg/paste.svg", theme) );
325  _action_saveToFile->setIcon( LoadSvgIcon(":/resources/svg/save.svg", theme) );
326  _action_image_to_clipboard->setIcon( LoadSvgIcon(":/resources/svg/plot_image.svg", theme) );
327 
328  QMenu menu(this);
329 
330  menu.addAction(_action_edit);
331  menu.addAction(_action_formula);
332  menu.addSeparator();
333  menu.addAction(_action_split_horizontal);
334  menu.addAction(_action_split_vertical);
335  menu.addSeparator();
336  menu.addAction(_action_zoomOutMaximum);
337  menu.addAction(_action_zoomOutHorizontally);
338  menu.addAction(_action_zoomOutVertically);
339  menu.addSeparator();
340  menu.addAction(_action_removeAllCurves);
341  menu.addSeparator();
342  menu.addAction(_action_copy);
343  menu.addAction(_action_paste);
344  menu.addAction(_action_image_to_clipboard);
345  menu.addAction(_action_saveToFile);
346 
347  // check the clipboard
348  QClipboard *clipboard = QGuiApplication::clipboard();
349  QString clipboard_text = clipboard->text();
350  QDomDocument doc;
351  bool valid_clipbaord =
352  ( !clipboard_text.isEmpty() && // not empty
353  doc.setContent(clipboard_text) && // valid xml
354  doc.firstChildElement().tagName() == "PlotWidgetClipBoard");
355 
356  _action_paste->setEnabled(valid_clipbaord);
357 
358  _action_removeAllCurves->setEnabled(!_curve_list.empty());
359  _action_formula->setEnabled(!_curve_list.empty() && !isXYPlot());
360 
361  menu.exec(canvas()->mapToGlobal(pos));
362 }
363 
365 {
366 }
367 
368 
369 PlotWidget::CurveInfo* PlotWidget::addCurve(const std::string& name, QColor color )
370 {
371  auto it = _mapped_data.numeric.find(name);
372  if (it == _mapped_data.numeric.end())
373  {
374  return nullptr;
375  }
376 
377  const auto qname = QString::fromStdString(name);
378 
379  // title is the same of src_name, unless a transform was applied
380  auto curve_it = curveFromTitle(qname);
381  if (curve_it)
382  {
383  return nullptr; //TODO FIXME
384  }
385 
386  PlotData& data = it->second;
387 
388  auto curve = new QwtPlotCurve(qname);
389  try
390  {
391  auto plot_qwt = createTimeSeries("", &data);
392 
393  curve->setPaintAttribute(QwtPlotCurve::ClipPolygons, true);
394  curve->setPaintAttribute(QwtPlotCurve::FilterPointsAggressive, true);
395  curve->setData(plot_qwt);
396  }
397  catch (std::exception& ex)
398  {
399  QMessageBox::warning(this, "Exception!", ex.what());
400  return nullptr;
401  }
402 
403  if( color == Qt::transparent ){
404  color = getColorHint(&data);
405  }
406  curve->setPen(color, (_curve_style == QwtPlotCurve::Dots) ? 4.0 : 1.3);
407  curve->setStyle(_curve_style);
408 
409  curve->setRenderHint(QwtPlotItem::RenderAntialiased, true);
410 
411  curve->attach(this);
412 
413  auto marker = new QwtPlotMarker;
414  marker->attach(this);
415  marker->setVisible(isXYPlot());
416 
417  QwtSymbol* sym = new QwtSymbol(QwtSymbol::Ellipse, Qt::red, QPen(Qt::black), QSize(8, 8));
418  marker->setSymbol(sym);
419 
420  CurveInfo curve_info;
421  curve_info.curve = curve;
422  curve_info.marker = marker;
423  curve_info.src_name = name;
424  _curve_list.push_back( curve_info );
425 
426  return &(_curve_list.back());
427 }
428 
429 PlotWidget::CurveInfo *PlotWidget::addCurveXY(std::string name_x, std::string name_y, QString curve_name)
430 {
431  std::string name = curve_name.toStdString();
432 
433  while (name.empty())
434  {
435  SuggestDialog dialog(name_x, name_y, this);
436 
437  bool ok = (dialog.exec() == QDialog::Accepted);
438  curve_name = dialog.suggestedName();
439  name = curve_name.toStdString();
440  name_x = dialog.nameX().toStdString();
441  name_y = dialog.nameY().toStdString();
442 
443  if (!ok)
444  {
445  return nullptr;
446  }
447 
448  auto curve_it = curveFromTitle(curve_name);
449 
450  if (name.empty() || curve_it )
451  {
452  int ret = QMessageBox::warning(this, "Missing name",
453  "The name of the curve is missing or exist already. Try again or abort.",
454  QMessageBox::Abort | QMessageBox::Retry, QMessageBox::Retry);
455  if (ret == QMessageBox::Abort)
456  {
457  return nullptr;
458  }
459  name.clear();
460  }
461  }
462 
463  auto it = _mapped_data.numeric.find(name_x);
464  if (it == _mapped_data.numeric.end())
465  {
466  throw std::runtime_error("Creation of XY plot failed");
467  }
468  PlotData& data_x = it->second;
469 
470  it = _mapped_data.numeric.find(name_y);
471  if (it == _mapped_data.numeric.end())
472  {
473  throw std::runtime_error("Creation of XY plot failed");
474  }
475  PlotData& data_y = it->second;
476 
477  auto curve_it = curveFromTitle(curve_name);
478  if (curve_it)
479  {
480  return nullptr;
481  }
482 
483  const auto qname = QString::fromStdString(name);
484  auto curve = new QwtPlotCurve(qname);
485 
486  try
487  {
488  auto plot_qwt = createCurveXY(&data_x, &data_y);
489 
490  curve->setPaintAttribute(QwtPlotCurve::ClipPolygons, true);
491  curve->setPaintAttribute(QwtPlotCurve::FilterPointsAggressive, true);
492  curve->setData(plot_qwt);
493  }
494  catch (std::exception& ex)
495  {
496  QMessageBox::warning(this, "Exception!", ex.what());
497  return nullptr;
498  }
499 
500  QColor color = getColorHint(nullptr);
501 
502  curve->setPen(color, (_curve_style == QwtPlotCurve::Dots) ? 4.0 : 1.3);
503  curve->setStyle(_curve_style);
504 
505  curve->setRenderHint(QwtPlotItem::RenderAntialiased, true);
506 
507  curve->attach(this);
508 
509  auto marker = new QwtPlotMarker;
510  marker->attach(this);
511  marker->setVisible(isXYPlot());
512  QwtSymbol* sym = new QwtSymbol(QwtSymbol::Ellipse, Qt::red, QPen(Qt::black), QSize(8, 8));
513  marker->setSymbol(sym);
514 
515  CurveInfo curve_info;
516  curve_info.curve = curve;
517  curve_info.marker = marker;
518  curve_info.src_name = name;
519  _curve_list.push_back( curve_info );
520 
521  return &(_curve_list.back());
522 }
523 
524 void PlotWidget::removeCurve(const QString &title)
525 {
526  auto it = std::find_if(_curve_list.begin(), _curve_list.end(),
527  [&title](const PlotWidget::CurveInfo& info)
528  {
529  return info.curve->title() == title;
530  });
531 
532  if (it != _curve_list.end())
533  {
534  it->curve->detach();
535  it->marker->detach();
536 
537  _curve_list.erase(it);
538 
539  _tracker->redraw();
540  emit curveListChanged();
541  }
542 }
543 
544 void PlotWidget::onSourceDataRemoved(const std::string& src_name)
545 {
546  bool deleted = false;
547 
548  for (auto it = _curve_list.begin(); it != _curve_list.end();)
549  {
550  PointSeriesXY* curve_xy = dynamic_cast<PointSeriesXY*>(it->curve->data());
551  bool remove_curve_xy =
552  curve_xy && (curve_xy->dataX()->name() == src_name || curve_xy->dataY()->name() == src_name);
553 
554  if (it->src_name == src_name || remove_curve_xy)
555  {
556  deleted = true;
557 
558  it->curve->detach();
559  it->marker->detach();
560 
561  it = _curve_list.erase(it);
562  }
563  else {
564  it++;
565  }
566  }
567 
568  if (deleted)
569  {
570  _tracker->redraw();
571  emit curveListChanged();
572  }
573 }
574 
576 {
577  return _curve_list.empty();
578 }
579 
580 const std::list<PlotWidget::CurveInfo>& PlotWidget::curveList() const
581 {
582  return _curve_list;
583 }
584 
585 std::list<PlotWidget::CurveInfo>& PlotWidget::curveList()
586 {
587  return _curve_list;
588 }
589 
591 {
592  auto it = std::find_if(_curve_list.begin(), _curve_list.end(),
593  [&title](const PlotWidget::CurveInfo& info)
594  {
595  return info.curve->title() == title;
596  });
597 
598  if( it == _curve_list.end()){
599  it = std::find_if(_curve_list.begin(), _curve_list.end(),
600  [&title](const PlotWidget::CurveInfo& info)
601  {
602  return info.src_name == title.toStdString();
603  });
604  }
605 
606  if( it == _curve_list.end()){
607  return nullptr;
608  }
609  else{
610  return &(*it);
611  }
612 }
613 
615 {
616  auto it = std::find_if(_curve_list.begin(), _curve_list.end(),
617  [&title](const PlotWidget::CurveInfo& info)
618  {
619  return info.curve->title() == title;
620  });
621 
622  if( it == _curve_list.end()){
623  it = std::find_if(_curve_list.begin(), _curve_list.end(),
624  [&title](const PlotWidget::CurveInfo& info)
625  {
626  return info.src_name == title.toStdString();
627  });
628  }
629 
630  if( it == _curve_list.end()){
631  return nullptr;
632  }
633  return &(*it);
634 }
635 
636 void PlotWidget::dragEnterEvent(QDragEnterEvent* event)
637 {
638  const QMimeData* mimeData = event->mimeData();
639  QStringList mimeFormats = mimeData->formats();
640  _dragging.curves.clear();
641  _dragging.source = event->source();
642 
643  for (const QString& format : mimeFormats)
644  {
645  QByteArray encoded = mimeData->data(format);
646  QDataStream stream(&encoded, QIODevice::ReadOnly);
647 
648  while (!stream.atEnd())
649  {
650  QString curve_name;
651  stream >> curve_name;
652  if (!curve_name.isEmpty())
653  {
654  _dragging.curves.push_back(curve_name);
655  }
656  }
657 
658  if (format == "curveslist/add_curve")
659  {
661  event->acceptProposedAction();
662  }
663  if (format == "curveslist/new_XY_axis")
664  {
665  if (_dragging.curves.size() != 2)
666  {
667  qDebug() << "FATAL: Dragging " << _dragging.curves.size() << " curves";
668  return;
669  }
670  if( _curve_list.empty() || isXYPlot())
671  {
673  event->acceptProposedAction();
674  }
675  else{
676  event->ignore();
677  }
678  }
679  }
680 }
681 
682 void PlotWidget::dragLeaveEvent(QDragLeaveEvent*)
683 {
685  _dragging.curves.clear();
686 }
687 
688 void PlotWidget::dropEvent(QDropEvent*)
689 {
690  bool curves_changed = false;
691 
693  {
694  if ( isXYPlot() && !_curve_list.empty())
695  {
697  _dragging.curves.clear();
698  QMessageBox::warning(this, "Warning",
699  tr("This is a XY plot, you can not drop normal time series here.\n"
700  "Clear all curves to reset it to normal mode."));
701  return;
702  }
703  else if ( isXYPlot() && _curve_list.empty())
704  {
705  setModeXY(false);
706  }
707 
708  for (const auto& curve_name : _dragging.curves)
709  {
710  bool added = addCurve(curve_name.toStdString()) != nullptr;
711  curves_changed = curves_changed || added;
712  }
713  }
714  else if (_dragging.mode == DragInfo::NEW_XY && _dragging.curves.size() == 2)
715  {
716  if (!_curve_list.empty() && !_xy_mode)
717  {
719  _dragging.curves.clear();
720  QMessageBox::warning(this, "Warning",
721  tr("To convert this widget into a XY plot, "
722  "you must first remove all the time series."));
723  return;
724  }
725 
726  setModeXY(true);
727  addCurveXY(_dragging.curves[0].toStdString(),
728  _dragging.curves[1].toStdString());
729 
730  curves_changed = true;
731  }
732 
733  if (curves_changed)
734  {
735  emit curvesDropped();
736  emit curveListChanged();
737  zoomOut(true);
738  }
740  _dragging.curves.clear();
741 }
742 
744 {
745  for (auto& it : _curve_list)
746  {
747  it.curve->detach();
748  it.marker->detach();
749  }
750 
751  setModeXY(false);
752  _curve_list.clear();
753  _tracker->redraw();
754 
755  emit curveListChanged();
756 
757  replot();
758 }
759 
760 void PlotWidget::on_panned(int, int)
761 {
763 }
764 
765 QDomElement PlotWidget::xmlSaveState(QDomDocument& doc) const
766 {
767  QDomElement plot_el = doc.createElement("plot");
768 
769  QDomElement range_el = doc.createElement("range");
770  QRectF rect = this->canvasBoundingRect();
771  range_el.setAttribute("bottom", QString::number(rect.bottom(), 'f', 6));
772  range_el.setAttribute("top", QString::number(rect.top(), 'f', 6));
773  range_el.setAttribute("left", QString::number(rect.left(), 'f', 6));
774  range_el.setAttribute("right", QString::number(rect.right(), 'f', 6));
775  plot_el.appendChild(range_el);
776 
777  QDomElement limitY_el = doc.createElement("limitY");
779  {
780  limitY_el.setAttribute("min", QString::number(_custom_Y_limits.min));
781  }
783  {
784  limitY_el.setAttribute("max", QString::number(_custom_Y_limits.max));
785  }
786  plot_el.appendChild(limitY_el);
787 
789  {
790  plot_el.setAttribute("style", "Lines");
791  }
793  {
794  plot_el.setAttribute("style", "LinesAndDots");
795  }
796  else if (_curve_style == QwtPlotCurve::Dots)
797  {
798  plot_el.setAttribute("style", "Dots");
799  }
800 
801  for (auto& it : _curve_list)
802  {
803  auto& name = it.src_name;
804  QwtPlotCurve* curve = it.curve;
805  QDomElement curve_el = doc.createElement("curve");
806  curve_el.setAttribute("name", QString::fromStdString(name));
807  curve_el.setAttribute("color", curve->pen().color().name());
808 
809  plot_el.appendChild(curve_el);
810 
811  if (isXYPlot())
812  {
813  PointSeriesXY* curve_xy = dynamic_cast<PointSeriesXY*>(curve->data());
814  curve_el.setAttribute("curve_x", QString::fromStdString(curve_xy->dataX()->name()));
815  curve_el.setAttribute("curve_y", QString::fromStdString(curve_xy->dataY()->name()));
816  }
817  else{
818  auto ts = dynamic_cast<TransformedTimeseries*>(curve->data());
819  if(ts && ts->transform())
820  {
821  QDomElement transform_el = doc.createElement("transform");
822  transform_el.setAttribute("name", ts->transformName() );
823  transform_el.setAttribute("alias", ts->transform()->alias() );
824  ts->transform()->xmlSaveState(doc, transform_el);
825  curve_el.appendChild(transform_el);
826  }
827  }
828  }
829 
830  plot_el.setAttribute("mode", isXYPlot() ? "XYPlot" : "TimeSeries");
831 
832  return plot_el;
833 }
834 
835 bool PlotWidget::xmlLoadState(QDomElement& plot_widget)
836 {
837  std::set<std::string> added_curve_names;
838 
839  QString mode = plot_widget.attribute("mode");
840  setModeXY(mode == "XYPlot");
841 
842  QDomElement limitY_el = plot_widget.firstChildElement("limitY");
843 
846 
847  if (!limitY_el.isNull())
848  {
849  if (limitY_el.hasAttribute("min"))
850  {
851  _custom_Y_limits.min = limitY_el.attribute("min").toDouble();
852  }
853  if (limitY_el.hasAttribute("max"))
854  {
855  _custom_Y_limits.max = limitY_el.attribute("max").toDouble();
856  }
857  }
858 
859  static bool warning_message_shown = false;
860 
861  // removeAllCurves simplified
862  for (auto& it : _curve_list)
863  {
864  it.curve->detach();
865  it.marker->detach();
866  }
867  _curve_list.clear();
868 
869  // insert curves
870  for (QDomElement curve_element = plot_widget.firstChildElement("curve"); !curve_element.isNull();
871  curve_element = curve_element.nextSiblingElement("curve"))
872  {
873  QString curve_name = curve_element.attribute("name");
874  std::string curve_name_std = curve_name.toStdString();
875  QColor color( curve_element.attribute("color"));
876 
877  bool error = false;
878  if (!isXYPlot())
879  {
880  if (_mapped_data.numeric.find(curve_name_std) == _mapped_data.numeric.end())
881  {
882  error = true;
883  }
884  else
885  {
886  auto curve_it = addCurve(curve_name_std, color);
887  if( ! curve_it )
888  {
889  continue;
890  }
891  auto &curve = curve_it->curve;
892  curve->setPen(color, 1.3);
893  added_curve_names.insert(curve_name_std);
894 
895  auto ts = dynamic_cast<TransformedTimeseries*>(curve->data());
896  QDomElement transform_el = curve_element.firstChildElement("transform");
897  if( transform_el.isNull() == false )
898  {
899  ts->setTransform( transform_el.attribute("name") );
900  ts->transform()->xmlLoadState(transform_el);
901  ts->updateCache(true);
902  auto alias = transform_el.attribute("alias");
903  ts->transform()->setAlias( alias );
904  curve->setTitle( alias );
905  }
906  }
907  }
908  else
909  {
910  std::string curve_x = curve_element.attribute("curve_x").toStdString();
911  std::string curve_y = curve_element.attribute("curve_y").toStdString();
912 
913  if (_mapped_data.numeric.find(curve_x) == _mapped_data.numeric.end() ||
914  _mapped_data.numeric.find(curve_y) == _mapped_data.numeric.end())
915  {
916  error = true;
917  }
918  else
919  {
920  auto curve_it = addCurveXY(curve_x, curve_y, curve_name);
921  if( ! curve_it )
922  {
923  continue;
924  }
925  curve_it->curve->setPen(color, 1.3);
926  added_curve_names.insert(curve_name_std);
927  }
928  }
929 
930  if (error && !warning_message_shown)
931  {
932  QMessageBox::warning(this, "Warning",
933  tr("Can't find one or more curves.\n"
934  "This message will be shown only once."));
935  warning_message_shown = true;
936  }
937  }
938 
939  _tracker->redraw();
940  emit curveListChanged();
941 
942  //-----------------------------------------
943 
944  QDomElement rectangle = plot_widget.firstChildElement("range");
945 
946  if (!rectangle.isNull())
947  {
948  QRectF rect;
949  rect.setBottom(rectangle.attribute("bottom").toDouble());
950  rect.setTop(rectangle.attribute("top").toDouble());
951  rect.setLeft(rectangle.attribute("left").toDouble());
952  rect.setRight(rectangle.attribute("right").toDouble());
953  this->setZoomRectangle(rect, false);
954  }
955 
956  if (plot_widget.hasAttribute("style"))
957  {
958  QString style = plot_widget.attribute("style");
959  if (style == "Lines")
960  {
962  }
963  else if (style == "LinesAndDots")
964  {
966  }
967  else if (style == "Dots")
968  {
970  }
972  }
973 
975  replot();
976  return true;
977 }
978 
980 {
981  QRectF rect;
982  rect.setBottom(this->canvasMap(yLeft).s1());
983  rect.setTop(this->canvasMap(yLeft).s2());
984  rect.setLeft(this->canvasMap(xBottom).s1());
985  rect.setRight(this->canvasMap(xBottom).s2());
986  return rect;
987 }
988 
990 {
991  QRectF max_rect;
992  auto rangeX = getMaximumRangeX();
993  max_rect.setLeft(rangeX.min);
994  max_rect.setRight(rangeX.max);
995 
996  auto rangeY = getMaximumRangeY(rangeX);
997  max_rect.setBottom(rangeY.min);
998  max_rect.setTop(rangeY.max);
999 
1000  if (isXYPlot() && _keep_aspect_ratio)
1001  {
1002  const QRectF canvas_rect = canvas()->contentsRect();
1003  const double canvas_ratio = fabs(canvas_rect.width() / canvas_rect.height());
1004  const double data_ratio = fabs(max_rect.width() / max_rect.height());
1005  if (data_ratio < canvas_ratio)
1006  {
1007  // height is negative!!!!
1008  double new_width = fabs(max_rect.height() * canvas_ratio);
1009  double increment = new_width - max_rect.width();
1010  max_rect.setWidth(new_width);
1011  max_rect.moveLeft(max_rect.left() - 0.5 * increment);
1012  }
1013  else
1014  {
1015  // height must be negative!!!!
1016  double new_height = -(max_rect.width() / canvas_ratio);
1017  double increment = fabs(new_height - max_rect.height());
1018  max_rect.setHeight(new_height);
1019  max_rect.moveTop(max_rect.top() + 0.5 * increment);
1020  }
1021  _magnifier->setAxisLimits(xBottom, max_rect.left(), max_rect.right());
1022  _magnifier->setAxisLimits(yLeft, max_rect.bottom(), max_rect.top());
1023  _zoomer->keepAspectRatio(true);
1024  }
1025  else
1026  {
1027  _magnifier->setAxisLimits(xBottom, max_rect.left(), max_rect.right());
1028  _magnifier->setAxisLimits(yLeft, max_rect.bottom(), max_rect.top());
1029  _zoomer->keepAspectRatio(false);
1030  }
1031  _max_zoom_rect = max_rect;
1032 }
1033 
1035 {
1036  const QwtScaleMap xMap = canvasMap(QwtPlot::xBottom);
1037  const QwtScaleMap yMap = canvasMap(QwtPlot::yLeft);
1038 
1039  QRectF canvas_rect = canvas()->contentsRect();
1040  canvas_rect = canvas_rect.normalized();
1041  const double x1 = xMap.invTransform(canvas_rect.left());
1042  const double x2 = xMap.invTransform(canvas_rect.right());
1043  const double y1 = yMap.invTransform(canvas_rect.bottom());
1044  const double y2 = yMap.invTransform(canvas_rect.top());
1045 
1046  const double data_ratio = (x2 - x1) / (y2 - y1);
1047  const double canvas_ratio = canvas_rect.width() / canvas_rect.height();
1048 
1049  QRectF rect(QPointF(x1, y2), QPointF(x2, y1));
1050 
1051  if (data_ratio < canvas_ratio)
1052  {
1053  double new_width = fabs(rect.height() * canvas_ratio);
1054  double increment = new_width - rect.width();
1055  rect.setWidth(new_width);
1056  rect.moveLeft(rect.left() - 0.5 * increment);
1057  }
1058  else
1059  {
1060  double new_height = -(rect.width() / canvas_ratio);
1061  double increment = fabs(new_height - rect.height());
1062  rect.setHeight(new_height);
1063  rect.moveTop(rect.top() + 0.5 * increment);
1064  }
1065  if (rect.contains(_max_zoom_rect))
1066  {
1067  rect = _max_zoom_rect;
1068  }
1069 
1070  this->setAxisScale(yLeft, std::min(rect.bottom(), rect.top()), std::max(rect.bottom(), rect.top()));
1071  this->setAxisScale(xBottom, std::min(rect.left(), rect.right()), std::max(rect.left(), rect.right()));
1072  this->updateAxes();
1073 }
1074 
1075 void PlotWidget::resizeEvent(QResizeEvent* ev)
1076 {
1079 
1080  if (isXYPlot() && _keep_aspect_ratio)
1081  {
1083  }
1084 }
1085 
1087 {
1089  // qDebug() << canvasBoundingRect();
1090 }
1091 
1093 {
1094  _keep_aspect_ratio = active;
1095  if (isXYPlot() && active)
1096  {
1097  _zoomer->keepAspectRatio(true);
1098  }
1099  else
1100  {
1101  _zoomer->keepAspectRatio(false);
1102  }
1103  zoomOut(false);
1104 }
1105 
1106 void PlotWidget::setZoomRectangle(QRectF rect, bool emit_signal)
1107 {
1108  QRectF current_rect = canvasBoundingRect();
1109  if (current_rect == rect)
1110  {
1111  return;
1112  }
1113  this->setAxisScale(yLeft, std::min(rect.bottom(), rect.top()), std::max(rect.bottom(), rect.top()));
1114  this->setAxisScale(xBottom, std::min(rect.left(), rect.right()), std::max(rect.left(), rect.right()));
1115  this->updateAxes();
1116 
1117  if (isXYPlot() && _keep_aspect_ratio)
1118  {
1120  }
1121 
1122  if (emit_signal)
1123  {
1124  if (isXYPlot())
1125  {
1126  emit undoableChange();
1127  }
1128  else
1129  {
1130  emit rectChanged(this, rect);
1131  }
1132  }
1133 }
1134 
1136 {
1137  // TODO: this needs MUCH more testing
1138 
1139  int visible = 0;
1140 
1141  for (auto& it : _curve_list)
1142  {
1143  if (it.curve->isVisible()){
1144  visible++;
1145  }
1146 
1147  const auto& curve_name = it.src_name;
1148 
1149  auto data_it = _mapped_data.numeric.find(curve_name);
1150  if (data_it != _mapped_data.numeric.end())
1151  {
1152  const auto& data = data_it->second;
1153  QString transform_name;
1154  if( !isXYPlot() )
1155  {
1156  auto ts = dynamic_cast<TransformedTimeseries*>(it.curve->data());
1157  transform_name = ts->transformName();
1158  }
1159  auto data_series = createTimeSeries(transform_name, &data);
1160  it.curve->setData(data_series);
1161  }
1162  }
1163 
1164  if (_curve_list.size() == 0 || visible == 0)
1165  {
1166  setDefaultRangeX();
1167  }
1168 }
1169 
1170 void PlotWidget::activateLegend(bool activate)
1171 {
1172  _legend->setVisible(activate);
1173 }
1174 
1175 void PlotWidget::activateGrid(bool activate)
1176 {
1177  _grid->enableX(activate);
1178  _grid->enableXMin(activate);
1179  _grid->enableY(activate);
1180  _grid->enableYMin(activate);
1181  _grid->attach(this);
1182 }
1183 
1185 {
1186  _tracker->setParameter(val);
1187 }
1188 
1190 {
1191  _tracker->setEnabled(enable && !isXYPlot());
1192 }
1193 
1195 {
1196  return _tracker->isEnabled();
1197 }
1198 
1199 void PlotWidget::setTrackerPosition(double abs_time)
1200 {
1201  if (isXYPlot())
1202  {
1203  for (auto& it : _curve_list)
1204  {
1205  auto series = dynamic_cast<QwtSeriesWrapper*>(it.curve->data());
1206  auto pointXY = series->sampleFromTime(abs_time);
1207  if (pointXY)
1208  {
1209  it.marker->setValue(pointXY.value());
1210  }
1211  }
1212  }
1213  else
1214  {
1215  double relative_time = abs_time - _time_offset;
1216  _tracker->setPosition(QPointF(relative_time, 0.0));
1217  }
1218 }
1219 
1221 {
1222  auto prev_offset = _time_offset;
1223  _time_offset = offset;
1224 
1225  if (fabs(prev_offset - offset) > std::numeric_limits<double>::epsilon())
1226  {
1227  for (auto& it : _curve_list)
1228  {
1229  auto series = dynamic_cast<QwtSeriesWrapper*>(it.curve->data());
1230  series->setTimeOffset(_time_offset);
1231  }
1232  if (!isXYPlot() && !_curve_list.empty())
1233  {
1234  QRectF rect = canvasBoundingRect();
1235  double delta = prev_offset - offset;
1236  rect.moveLeft(rect.left() + delta);
1237  setZoomRectangle(rect, false);
1238  }
1239  }
1240 }
1241 
1243 {
1245  bool is_timescale = dynamic_cast<TimeScaleDraw*>(axisScaleDraw(QwtPlot::xBottom)) != nullptr;
1246 
1247  if (enable && !isXYPlot())
1248  {
1249  if (!is_timescale)
1250  {
1252  }
1253  }
1254  else
1255  {
1256  if (is_timescale)
1257  {
1259  }
1260  }
1261 }
1262 
1264 {
1267 
1268  for (auto& it : _curve_list)
1269  {
1270  if (!it.curve->isVisible())
1271  continue;
1272 
1273  auto series = dynamic_cast<QwtSeriesWrapper*>(it.curve->data());
1274  const auto max_range_X = series->getVisualizationRangeX();
1275  if (!max_range_X)
1276  continue;
1277 
1278  left = std::min(max_range_X->min, left);
1279  right = std::max(max_range_X->max, right);
1280  }
1281 
1282  if (left > right)
1283  {
1284  left = 0;
1285  right = 0;
1286  }
1287 
1288  double margin = 0.0;
1289  if (fabs(right - left) > std::numeric_limits<double>::epsilon())
1290  {
1291  margin = isXYPlot() ? ((right - left) * 0.025) : 0.0;
1292  }
1293  right = right + margin;
1294  left = left - margin;
1295 
1296  return Range({ left, right });
1297 }
1298 
1299 // TODO report failure for empty dataset
1301 {
1303  double bottom = std::numeric_limits<double>::max();
1304 
1305  for (auto& it : _curve_list)
1306  {
1307  if (!it.curve->isVisible())
1308  continue;
1309 
1310  auto series = dynamic_cast<QwtSeriesWrapper*>(it.curve->data());
1311 
1312  const auto max_range_X = series->getVisualizationRangeX();
1313  if (!max_range_X){
1314  continue;
1315  }
1316 
1317  double left = std::max(max_range_X->min, range_X.min);
1318  double right = std::min(max_range_X->max, range_X.max);
1319 
1320  left += _time_offset;
1321  right += _time_offset;
1322  left = std::nextafter(left, right);
1323  right = std::nextafter(right, left);
1324 
1325  auto range_Y = series->getVisualizationRangeY({ left, right });
1326  if (!range_Y)
1327  {
1328  qDebug() << " invalid range_Y in PlotWidget::maximumRangeY";
1329  continue;
1330  }
1331  if (top < range_Y->max){
1332  top = range_Y->max;
1333  }
1334  if (bottom > range_Y->min){
1335  bottom = range_Y->min;
1336  }
1337  }
1338 
1339  double margin = 0.1;
1340 
1341  if (bottom > top)
1342  {
1343  bottom = 0;
1344  top = 0;
1345  }
1346 
1347  if (top - bottom > std::numeric_limits<double>::epsilon())
1348  {
1349  margin = (top - bottom) * 0.025;
1350  }
1351 
1352  const bool lower_limit = _custom_Y_limits.min > -MAX_DOUBLE;
1353  const bool upper_limit = _custom_Y_limits.max < MAX_DOUBLE;
1354 
1355  if (lower_limit)
1356  {
1357  bottom = _custom_Y_limits.min;
1358  if (top < bottom){
1359  top = bottom + margin;
1360  }
1361  }
1362 
1363  if (upper_limit)
1364  {
1365  top = _custom_Y_limits.max;
1366  if (top < bottom){
1367  bottom = top - margin;
1368  }
1369  }
1370 
1371  if (!lower_limit && !upper_limit)
1372  {
1373  top += margin;
1374  bottom -= margin;
1375  }
1376 
1377  return Range({ bottom, top });
1378 }
1379 
1381 {
1382  for (auto& it : _curve_list)
1383  {
1384  auto series = dynamic_cast<QwtSeriesWrapper*>(it.curve->data());
1385  bool res = series->updateCache(false);
1386  // TODO check res and do something if false.
1387  }
1389 }
1390 
1391 std::map<QString, QColor> PlotWidget::getCurveColors() const
1392 {
1393  std::map<QString, QColor> color_by_name;
1394 
1395  for (auto& it : _curve_list)
1396  {
1397  color_by_name.insert( {it.curve->title().text(), it.curve->pen().color()} );
1398  }
1399  return color_by_name;
1400 }
1401 
1402 void PlotWidget::on_changeCurveColor(const QString& curve_name, QColor new_color)
1403 {
1404  for(auto& it: _curve_list)
1405  {
1406  if (it.curve->title() == curve_name)
1407  {
1408  auto& curve = it.curve;
1409  if (curve->pen().color() != new_color)
1410  {
1411  curve->setPen(new_color, 1.3);
1412  }
1413  replot();
1414  break;
1415  }
1416  }
1417 }
1418 
1420 {
1421  _curve_style = style;
1422  for (auto& it : _curve_list)
1423  {
1424  auto& curve = it.curve;
1425  curve->setPen(curve->pen().color(), (_curve_style == QwtPlotCurve::Dots) ? 4.0 : 1.3);
1426  curve->setStyle(_curve_style);
1427  }
1428  replot();
1429 }
1430 
1432 {
1434  {
1436  }
1438  {
1440  }
1441  else if (_curve_style == QwtPlotCurve::Dots)
1442  {
1444  }
1445 
1447 }
1448 
1449 void PlotWidget::on_externallyResized(const QRectF& rect)
1450 {
1451  QRectF current_rect = canvasBoundingRect();
1452  if (current_rect == rect)
1453  {
1454  return;
1455  }
1456 
1457  if (isXYPlot())
1458  {
1459  emit undoableChange();
1460  }
1461  else
1462  {
1463  emit rectChanged(this, rect);
1464  }
1465 }
1466 
1467 void PlotWidget::zoomOut(bool emit_signal)
1468 {
1469  if (_curve_list.size() == 0)
1470  {
1471  QRectF rect(0, 1, 1, -1);
1472  this->setZoomRectangle(rect, false);
1473  return;
1474  }
1476  setZoomRectangle(_max_zoom_rect, emit_signal);
1477  replot();
1478 }
1479 
1481 {
1483  QRectF act = canvasBoundingRect();
1484  auto rangeX = getMaximumRangeX();
1485 
1486  act.setLeft(rangeX.min);
1487  act.setRight(rangeX.max);
1488  this->setZoomRectangle(act, emit_signal);
1489 }
1490 
1492 {
1494  QRectF rect = canvasBoundingRect();
1495  auto rangeY = getMaximumRangeY({ rect.left(), rect.right() });
1496 
1497  rect.setBottom(rangeY.min);
1498  rect.setTop(rangeY.max);
1499  this->setZoomRectangle(rect, emit_signal);
1500 }
1501 
1502 //void PlotWidget::on_changeToBuiltinTransforms(QString new_transform)
1503 //{
1504 // _xy_mode = false;
1505 
1506 // enableTracker(true);
1507 
1508 // for (auto& it : _curve_list)
1509 // {
1510 // const auto& curve_name = it.first;
1511 // auto& curve = it.second;
1512 
1513 // _point_marker[curve_name]->setVisible(false);
1514 // curve->setTitle(QString::fromStdString(curve_name));
1515 // _curves_transform[curve_name] = new_transform;
1516 
1517 // auto data_it = _mapped_data.numeric.find(curve_name);
1518 // if (data_it != _mapped_data.numeric.end())
1519 // {
1520 // const auto& data = data_it->second;
1521 // auto data_series = createTimeSeries(new_transform, &data);
1522 // curve->setData(data_series);
1523 // }
1524 // }
1525 
1526 // _default_transform = new_transform;
1527 // zoomOut(true);
1528 // on_changeDateTimeScale(_use_date_time_scale);
1529 // replot();
1530 //}
1531 
1533 {
1534  return _xy_mode;
1535 }
1536 
1538 {
1539  if( enable == _xy_mode)
1540  {
1541  return;
1542  }
1543  _xy_mode = enable;
1544 
1545  enableTracker(!enable);
1546 
1547  if( enable ){
1548  QFont font_footer;
1549  font_footer.setPointSize(10);
1550  QwtText text("XY Plot");
1551  text.setFont(font_footer);
1552  this->setFooter(text);
1553  }
1554  else{
1555  this->setFooter("");
1556  }
1557 
1558  zoomOut(true);
1560  replot();
1561 }
1562 
1564 {
1565  QSettings settings;
1566  QByteArray xml_text = settings.value("AddCustomPlotDialog.savedXML", QByteArray()).toByteArray();
1567  if (!xml_text.isEmpty())
1568  {
1569  _snippets = GetSnippetsFromXML(xml_text);
1570  }
1571 }
1572 
1574 {
1575  std::string error_message;
1576 
1577  /*for (auto& curve_it : _curve_list)
1578  {
1579  auto& curve = curve_it.second;
1580  const auto& curve_name = curve_it.first;
1581  const auto& transform = _curves_transform.at(curve_name);
1582 
1583  auto data_it = _mapped_data.numeric.find(curve_name);
1584  if (data_it != _mapped_data.numeric.end())
1585  {
1586  auto& data = data_it->second;
1587  try
1588  {
1589  auto data_series = createTimeSeries(transform, &data);
1590  curve->setData(data_series);
1591 
1592  if (transform == noTransform || transform.isEmpty())
1593  {
1594  curve->setTitle(QString::fromStdString(curve_name));
1595  }
1596  else
1597  {
1598  curve->setTitle(QString::fromStdString(curve_name) + tr(" [") + transform + tr("]"));
1599  }
1600  }
1601  catch (std::runtime_error& err)
1602  {
1603  auto data_series = createTimeSeries(noTransform, &data);
1604  curve->setData(data_series);
1605 
1606  error_message += curve_name + (" [") + transform.toStdString() + ("]: ");
1607  error_message += err.what();
1608 
1609  curve->setTitle(QString::fromStdString(curve_name));
1610  }
1611  }
1612  }
1613  if (error_message.size() > 0)
1614  {
1615  QMessageBox msgBox(this);
1616  msgBox.setWindowTitle("Warnings");
1617  msgBox.setText(tr("Something wrong happened while creating the following curves. "
1618  "Please check that the transform equation is correct.\n\n") +
1619  QString::fromStdString(error_message));
1620  msgBox.exec();
1621  }*/
1622 }
1623 
1625 {
1626  QString fileName;
1627 
1628  QFileDialog saveDialog(this);
1629  saveDialog.setAcceptMode(QFileDialog::AcceptSave);
1630 
1631  QStringList filters;
1632  filters << "png (*.png)"
1633  << "jpg (*.jpg *.jpeg)"
1634  << "svg (*.svg)";
1635 
1636  saveDialog.setNameFilters(filters);
1637  saveDialog.exec();
1638 
1639  if (saveDialog.result() == QDialog::Accepted && !saveDialog.selectedFiles().empty())
1640  {
1641  fileName = saveDialog.selectedFiles().first();
1642 
1643  if (fileName.isEmpty())
1644  {
1645  return;
1646  }
1647 
1648  bool is_svg = false;
1649  QFileInfo fileinfo(fileName);
1650  if (fileinfo.suffix().isEmpty())
1651  {
1652  auto filter = saveDialog.selectedNameFilter();
1653  if (filter == filters[0])
1654  {
1655  fileName.append(".png");
1656  }
1657  else if (filter == filters[1])
1658  {
1659  fileName.append(".jpg");
1660  }
1661  else if (filter == filters[2])
1662  {
1663  fileName.append(".svg");
1664  is_svg = true;
1665  }
1666  }
1667 
1668  bool tracker_enabled = _tracker->isEnabled();
1669  if (tracker_enabled)
1670  {
1671  this->enableTracker(false);
1672  replot();
1673  }
1674 
1675  QRect documentRect(0, 0, 1200, 900);
1676  QwtPlotRenderer rend;
1677 
1678  if (is_svg)
1679  {
1680  QSvgGenerator generator;
1681  generator.setFileName(fileName);
1682  generator.setResolution(80);
1683  generator.setViewBox(documentRect);
1684  QPainter painter(&generator);
1685  rend.render(this, &painter, documentRect);
1686  }
1687  else
1688  {
1689  QPixmap pixmap(1200, 900);
1690  QPainter painter(&pixmap);
1691  rend.render(this, &painter, documentRect);
1692  pixmap.save(fileName);
1693  }
1694 
1695  if (tracker_enabled)
1696  {
1697  this->enableTracker(true);
1698  replot();
1699  }
1700  }
1701 }
1702 
1704 {
1705  _custom_Y_limits = range;
1707  replot();
1708 }
1709 
1711 {
1712  return _custom_Y_limits;
1713 }
1714 
1716 {
1717  bool tracker_enabled = _tracker->isEnabled();
1718  if (tracker_enabled)
1719  {
1720  this->enableTracker(false);
1721  replot();
1722  }
1723 
1724  auto documentRect = this->canvas()->rect();
1725  qDebug() << documentRect;
1726 
1727  QwtPlotRenderer rend;
1728  QPixmap pixmap(documentRect.width(), documentRect.height());
1729  QPainter painter(&pixmap);
1730  rend.render(this, &painter, documentRect);
1731 
1732  QClipboard* clipboard = QGuiApplication::clipboard();
1733  clipboard->setPixmap(pixmap);
1734 
1735  if (tracker_enabled)
1736  {
1737  this->enableTracker(true);
1738  replot();
1739  }
1740 }
1741 
1743 {
1744  QDomDocument doc;
1745  auto root = doc.createElement("PlotWidgetClipBoard");
1746  auto el = xmlSaveState(doc);
1747  doc.appendChild(root);
1748  root.appendChild(el);
1749 
1750  QClipboard *clipboard = QGuiApplication::clipboard();
1751  clipboard->setText( doc.toString() );
1752 }
1753 
1755 {
1756  QClipboard *clipboard = QGuiApplication::clipboard();
1757  QString clipboard_text = clipboard->text();
1758 
1759  QDomDocument doc;
1760  bool valid = doc.setContent(clipboard_text);
1761  if( !valid ){
1762  return;
1763  }
1764  auto root = doc.firstChildElement();
1765  if( root.tagName() != "PlotWidgetClipBoard")
1766  {
1767  return;
1768  }
1769  else{
1770  auto el = root.firstChildElement();
1771  xmlLoadState(el);
1772  clipboard->setText("");
1773  emit undoableChange();
1774  }
1775 }
1776 
1777 bool PlotWidget::eventFilter(QObject* obj, QEvent* event)
1778 {
1779  QwtScaleWidget* bottomAxis = this->axisWidget(xBottom);
1780  QwtScaleWidget* leftAxis = this->axisWidget(yLeft);
1781 
1782  if (_magnifier && (obj == bottomAxis || obj == leftAxis) && !(isXYPlot() && _keep_aspect_ratio))
1783  {
1784  if (event->type() == QEvent::Wheel)
1785  {
1786  auto wheel_event = dynamic_cast<QWheelEvent*>(event);
1787  if (obj == bottomAxis)
1788  {
1790  }
1791  else
1792  {
1794  }
1795  _magnifier->widgetWheelEvent(wheel_event);
1796  }
1797  }
1798 
1799  if (obj == canvas())
1800  {
1801  if (_magnifier)
1802  {
1804  }
1805  return canvasEventFilter(event);
1806  }
1807 
1808  return false;
1809 }
1810 
1812 {
1813  QSettings settings;
1814  QString theme = settings.value("Preferences::theme", "light").toString();
1815  QPixmap pixmap(tr(":/style_%1/move.png").arg(theme));
1816  QApplication::setOverrideCursor(QCursor(pixmap.scaled(24, 24)));
1817 }
1818 
1820 {
1821  switch (event->type())
1822  {
1823  case QEvent::Wheel: {
1824  auto mouse_event = dynamic_cast<QWheelEvent*>(event);
1825 
1826  bool ctrl_modifier = mouse_event->modifiers() == Qt::ControlModifier;
1827  auto legend_rect = _legend->geometry(canvas()->rect());
1828 
1829  if (ctrl_modifier)
1830  {
1831  if (legend_rect.contains(mouse_event->pos()) && _legend->isVisible())
1832  {
1833  int prev_size = _legend->font().pointSize();
1834  int new_size = prev_size;
1835  if (mouse_event->delta() > 0)
1836  {
1837  new_size = std::min(13, prev_size+1);
1838  }
1839  if (mouse_event->delta() < 0)
1840  {
1841  new_size = std::max(7, prev_size-1);
1842  }
1843  if( new_size != prev_size)
1844  {
1845  setLegendSize(new_size);
1846  emit legendSizeChanged(new_size);
1847  }
1848  return true;
1849  }
1850  }
1851  return false;
1852  }
1853 
1854  case QEvent::MouseButtonPress: {
1855  if (_dragging.mode != DragInfo::NONE)
1856  {
1857  return true; // don't pass to canvas().
1858  }
1859 
1860  QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
1861 
1862  if (mouse_event->button() == Qt::LeftButton)
1863  {
1864  const QPoint press_point = mouse_event->pos();
1865  if (mouse_event->modifiers() == Qt::ShiftModifier) // time tracker
1866  {
1867  QPointF pointF(invTransform(xBottom, press_point.x()), invTransform(yLeft, press_point.y()));
1868  emit trackerMoved(pointF);
1869  return true; // don't pass to canvas().
1870  }
1871  else if (mouse_event->modifiers() == Qt::ControlModifier) // panner
1872  {
1874  }
1875  else
1876  {
1877  auto clicked_item = _legend->processMousePressEvent(mouse_event);
1878  if (clicked_item)
1879  {
1880  for (auto& it : _curve_list)
1881  {
1882  if (clicked_item == it.curve)
1883  {
1884  it.curve->setVisible(!it.curve->isVisible());
1885  _tracker->redraw();
1887  replot();
1888  return true;
1889  }
1890  }
1891  }
1892  }
1893  return false; // send to canvas()
1894  }
1895  else if (mouse_event->buttons() == Qt::MidButton && mouse_event->modifiers() == Qt::NoModifier)
1896  {
1898  return false;
1899  }
1900  else if (mouse_event->button() == Qt::RightButton)
1901  {
1902  if (mouse_event->modifiers() == Qt::NoModifier) // show menu
1903  {
1904  canvasContextMenuTriggered(mouse_event->pos());
1905  return true; // don't pass to canvas().
1906  }
1907  }
1908  }
1909  break;
1910  //---------------------------------
1911  case QEvent::MouseMove: {
1912  if (_dragging.mode != DragInfo::NONE)
1913  {
1914  return true; // don't pass to canvas().
1915  }
1916 
1917  QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
1918 
1919  if (mouse_event->buttons() == Qt::LeftButton && mouse_event->modifiers() == Qt::ShiftModifier)
1920  {
1921  const QPoint point = mouse_event->pos();
1922  QPointF pointF(invTransform(xBottom, point.x()), invTransform(yLeft, point.y()));
1923  emit trackerMoved(pointF);
1924  return true;
1925  }
1926  }
1927  break;
1928 
1929  case QEvent::Leave: {
1930  if (_dragging.mode == DragInfo::NONE)
1931  {
1932  return false;
1933  }
1934  }
1935  break;
1936  case QEvent::MouseButtonRelease: {
1937  if (_dragging.mode == DragInfo::NONE)
1938  {
1939  QApplication::restoreOverrideCursor();
1940  return false;
1941  }
1942  }
1943  break;
1944 
1945  case QEvent::Enter: {
1946  // If you think that this code doesn't make sense, you are right.
1947  // This is the workaround I have eventually found to avoid the problem with spurious
1948  // QEvent::DragLeave (I have never found the origin of the bug).
1949  if (_dragging.mode != DragInfo::NONE)
1950  {
1951  dropEvent(nullptr);
1952  }
1953  return true;
1954  }
1955 
1956  default: {
1957  }
1958 
1959  } // end switch
1960 
1961  return false;
1962 }
1963 
1965 {
1966  if (!_curve_list.empty())
1967  {
1970  for (auto& it : _mapped_data.numeric)
1971  {
1972  const PlotData& data = it.second;
1973  if (data.size() > 0)
1974  {
1975  double A = data.front().x;
1976  double B = data.back().x;
1977  min = std::min(A, min);
1978  max = std::max(B, max);
1979  }
1980  }
1982  }
1983  else{
1984  setAxisScale(xBottom, 0.0, 1.0);
1985  }
1986 }
1987 
1989 {
1990  QwtSeriesWrapper* output = nullptr;
1991 
1992  try
1993  {
1994  output = new PointSeriesXY(data_x, data_y);
1995  }
1996  catch (std::runtime_error& ex)
1997  {
1999  {
2000  QMessageBox msgBox(this);
2001  msgBox.setWindowTitle("Warnings");
2002  msgBox.setText(tr("The creation of the XY plot failed with the following message:\n %1").arg(ex.what()));
2003  msgBox.addButton("Continue", QMessageBox::AcceptRole);
2004  msgBox.exec();
2005  }
2006  throw std::runtime_error("Creation of XY plot failed");
2007  }
2008 
2009  output->setTimeOffset(_time_offset);
2010  return output;
2011 }
2012 
2013 QwtSeriesWrapper* PlotWidget::createTimeSeries(const QString& transform_ID, const PlotData* data)
2014 {
2016  output->setTransform(transform_ID);
2017  output->setTimeOffset(_time_offset);
2018  output->updateCache(true);
2019  return output;
2020 }
2021 
2023 {
2024  if (canvasBackground().color() != color)
2025  {
2026  setCanvasBackground(color);
2027  replot();
2028  }
2029 }
2030 
2032 {
2033  auto font = _legend->font();
2034  font.setPointSize(size);
2035  _legend->setFont(font);
2036  replot();
2037 }
2038 
2040 {
2041  return _legend && _legend->isVisible();
2042 }
2043 
2044 void PlotWidget::setLegendAlignment(Qt::Alignment alignment)
2045 {
2046  _legend->setAlignmentInCanvas(Qt::Alignment(Qt::AlignTop | alignment));
2047 }
2048 
2049 void PlotWidget::setZoomEnabled(bool enabled)
2050 {
2051  _zoom_enabled = enabled;
2052  _zoomer->setEnabled(enabled);
2053  _magnifier->setEnabled(enabled);
2054  _panner1->setEnabled(enabled);
2055  _panner2->setEnabled(enabled);
2056 }
2057 
2059 {
2060  return _zoom_enabled;
2061 }
2062 
2064 {
2065  if (_zoomer)
2066  {
2067  _zoomer->setZoomBase(false);
2068  }
2069 
2070  static int replot_count = 0;
2071  QwtPlot::replot();
2072  //qDebug() << replot_count++;
2073 }
const Point & back() const
Definition: plotdata.h:139
void setConstantRatioXY(bool active)
static QStringList builtin_trans
Definition: plotwidget.cpp:121
virtual RangeOpt getVisualizationRangeX()
TimeSeriesTransformPtr transform()
const QwtPlotItem * processMousePressEvent(QMouseEvent *mouse_event)
Definition: plotlegend.cpp:133
CurveTracker * _tracker
Definition: plotwidget.h:224
static const char * Derivative1st
Definition: plotwidget.cpp:117
void on_savePlotToFile()
void on_panned(int dx, int dy)
Definition: plotwidget.cpp:760
Enable antialiasing.
void enableX(bool)
Enable or disable vertical grid lines.
double max
Definition: plotdata.h:24
void removeAllCurves()
Definition: plotwidget.cpp:743
QColor getColorHint(PlotData *data)
Definition: plotwidget.cpp:47
A plot item, that represents a series of points.
FMT_INLINE std::basic_string< Char > format(const S &format_str, Args &&...args)
Definition: core.h:2081
X axis above the canvas.
Definition: qwt_plot.h:106
CurveInfo * addCurveXY(std::string name_x, std::string name_y, QString curve_name="")
Definition: plotwidget.cpp:429
void setFont(const QFont &)
Definition: qwt_text.cpp:306
virtual QwtText label(double v) const
Convert a value into its representing label.
Definition: plotwidget.cpp:103
void updateMaximumZoomArea()
Definition: plotwidget.cpp:989
void replot() override
void setLegendSize(int size)
QDomElement xmlSaveState(QDomDocument &doc) const
Definition: plotwidget.cpp:765
#define nullptr
Definition: backward.hpp:386
void setCanvasBackground(const QBrush &)
Change the background of the plotting area.
Definition: qwt_plot.cpp:857
QAction * _action_removeAllCurves
Definition: plotwidget.h:205
lu_byte right
Definition: lparser.c:1229
PlotLegend * _legend
Definition: plotwidget.h:225
const Point & front() const
Definition: plotdata.h:134
bool isVisible() const
void setAxisEnabled(int axis, bool on)
En/Disable an axis.
void rectChanged(PlotWidget *self, QRectF rect)
QIcon LoadSvgIcon(QString filename, QString style_name="light")
Definition: svg_util.h:17
DragInfo _dragging
Definition: plotwidget.h:247
void on_pasteAction_triggered()
std::map< QString, QColor > getCurveColors() const
void on_changeDateTimeScale(bool enable)
void enableYMin(bool)
Enable or disable minor horizontal grid lines.
bool isXYPlot() const
PlotDataMapRef & _mapped_data
Definition: plotwidget.h:233
void on_externallyResized(const QRectF &new_rect)
void undoableChange()
bool isTrackerEnabled() const
std::list< CurveInfo > _curve_list
Definition: plotwidget.h:203
lu_byte left
Definition: lparser.c:1228
void legendSizeChanged(int new_size)
void setAxisScaleDraw(int axisId, QwtScaleDraw *)
Set a scale draw.
std::unordered_map< std::string, PlotData > numeric
Definition: plotdata.h:495
QRectF canvasBoundingRect() const
Definition: plotwidget.cpp:979
SnippetsMap _snippets
Definition: plotwidget.h:271
bool _use_date_time_scale
Definition: plotwidget.h:228
QwtPlotMarker * marker
Definition: plotwidget.h:37
void setCanvas(QWidget *)
Set the drawing canvas of the plot widget.
Definition: qwt_plot.cpp:210
void setFont(const QFont &)
int _color_index
Definition: plotwidget.h:230
QwtPlotPanner * _panner2
Definition: plotwidget.h:222
Y axis right of the canvas.
Definition: qwt_plot.h:100
PlotMagnifier * _magnifier
Definition: plotwidget.h:220
PlotWidget(PlotDataMapRef &datamap, QWidget *parent=nullptr)
Definition: plotwidget.cpp:123
static l_noret error(LoadState *S, const char *why)
Definition: lundump.c:40
virtual QRect geometry(const QRectF &canvasRect) const
void setColorHint(QColor color)
Definition: plotdata.h:129
virtual void replot()
Redraw the plot.
Definition: qwt_plot.cpp:539
CurveInfo * curveFromTitle(const QString &title)
Definition: plotwidget.cpp:590
double _time_offset
Definition: plotwidget.h:263
bool isLegendVisible() const
void activateLegend(bool activate)
double invTransform(int axisId, int pos) const
PlotZoomer * _zoomer
Definition: plotwidget.h:219
bool isEmpty() const
Definition: plotwidget.cpp:575
QwtSeriesData< T > * data()
A 2-D plotting widget.
Definition: qwt_plot.h:75
A class for drawing symbols.
Definition: qwt_symbol.h:31
Y axis left of the canvas.
Definition: qwt_plot.h:97
const double MAX_DOUBLE
Definition: plotwidget.cpp:114
Renderer for exporting a plot to a document, a printer or anything else, that is supported by QPainte...
void setAlignmentInCanvas(Qt::Alignment)
Set the alignmnet.
QAction * _action_edit
Definition: plotwidget.h:206
const QwtScaleWidget * axisWidget(int axisId) const
virtual bool updateCache(bool reset_old_data)=0
QAction * _action_formula
Definition: plotwidget.h:207
void splitVertical()
void trackerMoved(QPointF pos)
void enableXMin(bool)
Enable or disable minor vertical grid lines.
void setModeXY(bool enable)
virtual bool updateCache(bool reset_old_data) override
bool isZoomEnabled() const
void reloadPlotData()
QBrush canvasBackground() const
void setPen(const QColor &, qreal width=0.0, Qt::PenStyle=Qt::SolidLine)
void enableY(bool)
Enable or disable horizontal grid lines.
QwtPlotPanner * _panner1
Definition: plotwidget.h:221
Range _custom_Y_limits
Definition: plotwidget.h:267
QColor getColorHint() const
Definition: plotdata.h:124
void updateCurves()
virtual void widgetWheelEvent(QWheelEvent *event) override
void zoomOut(bool emit_signal)
QString nameX() const
void on_showPoints_triggered()
void curvesDropped()
void setEnabled(bool)
En/disable the magnifier.
#define min(A, B)
Definition: Log.c:64
#define max(A, B)
Definition: Socket.h:88
QAction * _action_zoomOutVertically
Definition: plotwidget.h:213
virtual void updateLayout()
Adjust plot content to its current size.
Definition: qwt_plot.cpp:571
QAction * _action_zoomOutMaximum
Definition: plotwidget.h:211
void setAxisAutoScale(int axisId, bool on=true)
Enable autoscaling for a specified axis.
void keepAspectRatio(bool doKeep)
Definition: plotzoomer.h:17
bool isEnabled() const
QAction * _action_copy
Definition: plotwidget.h:215
A Widget which contains a scale.
QRectF _max_zoom_rect
Definition: plotwidget.h:277
QwtSeriesWrapper * createCurveXY(const PlotData *data_x, const PlotData *data_y)
void dropEvent(QDropEvent *event) override
Definition: plotwidget.cpp:688
void setDefaultMode(AxisMode mode)
Definition: plotmagnifier.h:32
Ellipse or circle.
Definition: qwt_symbol.h:44
void overrideCursonMove()
QAction * _action_split_vertical
Definition: plotwidget.h:209
void setCustomAxisLimits(Range range)
void setZoomInKey(int key, Qt::KeyboardModifiers=Qt::NoModifier)
A class which draws a coordinate grid.
Definition: qwt_plot_grid.h:33
bool _context_menu_enabled
Definition: plotwidget.h:279
A class representing a text.
Definition: qwt_text.h:51
virtual QwtScaleMap canvasMap(int axisId) const
Definition: qwt_plot.cpp:786
void setAlignCanvasToScales(bool)
Set the align-canvas-to-axis-scales flag for all axes.
SnippetsMap GetSnippetsFromXML(const QString &xml_text)
bool canvasEventFilter(QEvent *event)
CurveInfo * addCurve(const std::string &name, QColor color=Qt::transparent)
Definition: plotwidget.cpp:369
void rescaled(QRectF new_size)
static bool if_xy_plot_failed_show_dialog
Definition: plotwidget.cpp:119
const std::list< CurveInfo > & curveList() const
Definition: plotwidget.cpp:580
virtual void updateLayout() override
Adjust plot content to its current size.
std::string src_name
Definition: plotwidget.h:35
const PlotData * dataX() const
QAction * _action_image_to_clipboard
Definition: plotwidget.h:217
void configureTracker(CurveTracker::Parameter val)
void setFooter(const QString &)
Definition: qwt_plot.cpp:369
void on_zoomOutVertical_triggered(bool emit_signal=true)
void panned(int dx, int dy)
void setTimeOffset(double offset)
virtual void resizeEvent(QResizeEvent *ev) override
const PlotData * dataY() const
QwtPlotGrid * _grid
Definition: plotwidget.h:226
void rescaleEqualAxisScaling()
A scale map.
Definition: qwt_scale_map.h:26
double invTransform(double p) const
enum PlotWidget::DragInfo::@103 mode
const char * name
void setEnabled(bool enable)
void setZoomOutKey(int key, Qt::KeyboardModifiers=Qt::NoModifier)
virtual size_t size() const
Definition: plotdata.h:92
void on_copyToClipboard()
void setEnabled(bool)
En/disable the picker.
Definition: qwt_picker.cpp:387
void setAxisScale(int axisId, double min, double max, double stepSize=0)
Disable autoscaling and specify a fixed scale for a selected axis.
detail::named_arg< Char, T > arg(const Char *name, const T &arg)
Definition: core.h:1656
void onSourceDataRemoved(const std::string &src_name)
Definition: plotwidget.cpp:544
void curveListChanged()
void removeCurve(const QString &title)
Definition: plotwidget.cpp:524
QwtPlotCurve::CurveStyle _curve_style
Definition: plotwidget.h:255
QwtPlotCurve * curve
Definition: plotwidget.h:36
bool xmlLoadState(QDomElement &element)
Definition: plotwidget.cpp:835
void changeBackgroundColor(QColor color)
void on_zoomOutHorizontal_triggered(bool emit_signal=true)
bool eventFilter(QObject *obj, QEvent *event) override
Event filter.
virtual nonstd::optional< QPointF > sampleFromTime(double t)=0
TransformSelector * _transform_select_dialog
Definition: plotwidget.h:269
int top(lua_State *L)
Definition: sol.hpp:10543
virtual void setVisible(bool)
Range customAxisLimit() const
void buildActions()
Definition: plotwidget.cpp:232
virtual ~PlotWidget() override
Definition: plotwidget.cpp:364
static const char * noTransform
Definition: plotwidget.cpp:116
QwtText title() const
Definition: qwt_plot.cpp:348
void setTransform(QString transform_ID)
bool _zoom_enabled
Definition: plotwidget.h:273
virtual void setZoomBase(bool doReplot=true)
QWidget * canvas()
Definition: qwt_plot.cpp:460
void splitHorizontal()
void setTrackerPosition(double abs_time)
const QwtScaleDraw * axisScaleDraw(int axisId) const
Return the scale draw of a specified axis.
void setZoomEnabled(bool enabled)
void attach(QwtPlot *plot)
Attach the item to a plot.
QAction * _action_saveToFile
Definition: plotwidget.h:214
void setAxisLimits(int axis, double lower, double upper)
QwtPlotPanner provides panning of a plot canvas.
bool _keep_aspect_ratio
Definition: plotwidget.h:275
virtual bool event(QEvent *) QWT_OVERRIDE
Adds handling of layout requests.
Definition: qwt_plot.cpp:234
const std::string & name() const
Definition: plotdata.h:87
void on_changeTimeOffset(double offset)
std::enable_if_t< all< Args... >::value, enable_t > enable
Definition: sol.hpp:1726
virtual void resizeEvent(QResizeEvent *) QWT_OVERRIDE
Definition: qwt_plot.cpp:524
void setLegendAlignment(Qt::Alignment alignment)
void setContextMenuEnabled(bool enabled)
Definition: plotwidget.cpp:227
Range getMaximumRangeY(Range range_X) const
double min
Definition: plotdata.h:23
QwtScaleEngine * axisScaleEngine(int axisId)
std::vector< QString > curves
Definition: plotwidget.h:243
QString suggestedName() const
QAction * _action_paste
Definition: plotwidget.h:216
void setMousePattern(MousePatternCode, Qt::MouseButton button, Qt::KeyboardModifiers=Qt::NoModifier)
A class for drawing scales.
void setDefaultRangeX()
void setPosition(const QPointF &pos)
void on_copyAction_triggered()
QwtSeriesWrapper * createTimeSeries(const QString &transform_ID, const PlotData *data)
void transformCustomCurves()
bool _xy_mode
Definition: plotwidget.h:265
char output[3000]
Definition: test1.c:251
void setZoomRectangle(QRectF rect, bool emit_signal)
dictionary data
Definition: mqtt_test.py:22
void setMouseButton(Qt::MouseButton, Qt::KeyboardModifiers=Qt::NoModifier)
Definition: qwt_panner.cpp:110
void updateAvailableTransformers()
static int global_color_index
Definition: plotwidget.h:231
virtual void render(QwtPlot *, QPainter *, const QRectF &plotRect) const
void activateGrid(bool activate)
static const char * Derivative2nd
Definition: plotwidget.cpp:118
void setParameter(Parameter par)
PlotDataMapRef & datamap()
Definition: plotwidget.h:97
void setRubberBandPen(const QPen &)
Definition: qwt_picker.cpp:471
QAction * _action_zoomOutHorizontally
Definition: plotwidget.h:212
void setMouseButton(Qt::MouseButton, Qt::KeyboardModifiers=Qt::NoModifier)
void setAttribute(Attribute, bool on=true)
void zoomed(const QRectF &rect)
void enableTracker(bool enable)
Range getMaximumRangeX() const
void dragEnterEvent(QDragEnterEvent *event) override
Definition: plotwidget.cpp:636
void updateAxes()
Rebuild the axes scales.
QAction * _action_split_horizontal
Definition: plotwidget.h:208
const QPen & pen() const
void setEnabled(bool)
En/disable the panner.
Definition: qwt_panner.cpp:188
void setTrackerPen(const QPen &)
Definition: qwt_picker.cpp:447
void dragLeaveEvent(QDragLeaveEvent *event) override
Definition: plotwidget.cpp:682
QString nameY() const
X axis below the canvas.
Definition: qwt_plot.h:103
QwtPlotLayout * plotLayout()
Definition: qwt_plot.cpp:427
void changeCurveStyle(QwtPlotCurve::CurveStyle style)
A class for drawing markers.
void canvasContextMenuTriggered(const QPoint &pos)
Definition: plotwidget.cpp:305
void on_changeCurveColor(const QString &curve_name, QColor new_color)


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Dec 6 2020 03:48:10