dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
scopegadgetwidget.cpp
Go to the documentation of this file.
1 
16 /*
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25  * for more details.
26  *
27  * You should have received a copy of the GNU General Public License along
28  * with this program; if not, see <http://www.gnu.org/licenses/>
29  *
30  * Additional note on redistribution: The copyright and license notices above
31  * must be maintained in each individual source file that is a derivative work
32  * of this source file; otherwise redistribution is prohibited.
33  */
34 
35 #include <QDir>
36 
37 #include "scopegadgetwidget.h"
39 
43 #include "uavobjects/uavobject.h"
44 #include "coreplugin/icore.h"
46 
47 #include "qwt/src/qwt_legend.h"
48 #include "qwt/src/qwt_legend_label.h"
49 #include "qwt/src/qwt_scale_widget.h"
50 
51 #include <iostream>
52 #include <math.h>
53 #include <QDebug>
54 #include <QColor>
55 #include <QStringList>
56 #include <QWidget>
57 #include <QVBoxLayout>
58 #include <QPushButton>
59 #include <QWheelEvent>
60 #include <QMenu>
61 #include <QAction>
62 #include <QClipboard>
63 #include <QApplication>
64 
65 QTimer *ScopeGadgetWidget::replotTimer = nullptr;
66 
68  : QwtPlot(parent)
69  , m_refreshInterval(50)
70  , // Arbitrary 50ms refresh timer
71  m_scope(nullptr)
72  , m_xWindowSize(60) // This is an arbitrary 1 minute window
73 {
74  m_grid = new QwtPlotGrid;
75 
76  setMouseTracking(true);
77  // canvas()->setMouseTracking(true);
78 
79  // Set up the timer that replots data. Only set up one timer for entire class.
80  if (replotTimer == NULL)
81  replotTimer = new QTimer();
82  connect(replotTimer, &QTimer::timeout, this, &ScopeGadgetWidget::replotNewData);
83 
84  // Listen to telemetry connection/disconnection events, no point in
85  // running the scopes if we are not connected and not replaying logs.
86  // Also listen to disconnect actions from the user
89  &ScopeGadgetWidget::stopPlotting);
90  connect(cm, &Core::ConnectionManager::deviceConnected, this, &ScopeGadgetWidget::startPlotting);
91 
92  setContextMenuPolicy(Qt::CustomContextMenu);
93  connect(this, &QWidget::customContextMenuRequested, this, &ScopeGadgetWidget::popUpMenu);
94 }
95 
100 {
101  // Get the object to de-monitor
102  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
103  UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
104  foreach (QString uavObjName, m_connectedUAVObjects) {
105  UAVDataObject *obj = dynamic_cast<UAVDataObject *>(objManager->getObject(uavObjName));
106  disconnect(obj, &UAVObject::objectUpdated, this, &ScopeGadgetWidget::uavObjectReceived);
107  }
108 
109  // Clear the plot
110  clearPlotWidget();
111 }
112 
113 // ******************************************************************
114 
115 void ScopeGadgetWidget::popUpMenu(const QPoint &mousePosition)
116 {
117  Q_UNUSED(mousePosition);
118 
119  QMenu menu;
120  QAction *action = menu.addAction(tr("Clear"));
121 
122  // Add clear plot item to menu
123  connect(action, &QAction::triggered, this, &ScopeGadgetWidget::clearPlot);
124  action = menu.addAction(tr("Copy to Clipboard"));
125 
126  // Add copy to clipboard item to menu
127  connect(action, &QAction::triggered, this, &ScopeGadgetWidget::copyToClipboardAsImage);
128  menu.addSeparator();
129 
130  // Add options dialog to clipboard
131  action = menu.addAction(tr("Options..."));
132  connect(action, &QAction::triggered, this, &ScopeGadgetWidget::showOptionDialog);
133 
134  // Show the menu
135  menu.exec(QCursor::pos());
136 }
137 
141 void ScopeGadgetWidget::clearPlot()
142 {
143  if (m_scope) {
144  // Clear the plots
145  foreach (PlotData *plotData, m_dataSources.values()) {
146  plotData->clearPlots();
147  }
148  }
149 }
150 
154 void ScopeGadgetWidget::copyToClipboardAsImage()
155 {
156  QPixmap pixmap = QWidget::grab();
157  if (pixmap.isNull()) {
158  qDebug("Failed to capture the plot");
159  return;
160  }
161  QClipboard *clipboard = QApplication::clipboard();
162  clipboard->setPixmap(pixmap);
163 }
164 
168 void ScopeGadgetWidget::showOptionDialog()
169 {
170  // This directly opens the preferences dialog and goes straight to the chosen tab.
171  Core::ICore::instance()->showOptionsDialog("ScopeGadget", scopeName);
172 }
173 
179 {
180  QwtPlot::mousePressEvent(e);
181 }
182 
188 {
189  QwtPlot::mouseReleaseEvent(e);
190 }
191 
198 {
199  // On double-click, toggle legend
200  if (legend())
201  deleteLegend();
202  else
203  addLegend();
204 
205  // On double-click, reset plot zoom
206  setAxisAutoScale(QwtPlot::yLeft, true);
207 
208  update();
209 
210  QwtPlot::mouseDoubleClickEvent(e);
211 }
212 
218 {
219  QwtPlot::mouseMoveEvent(e);
220 }
221 
227 {
228  // Change zoom on scroll wheel event
229  QwtInterval yInterval = axisInterval(QwtPlot::yLeft);
230  if (yInterval.minValue() != yInterval.maxValue()) // Make sure that the two values are never the
231  // same. Sometimes axisInterval returns (0,0)
232  {
233  // Determine what y value to zoom about. NOTE, this approach has a bug that the in that
234  // the value returned by Qt includes the legend, whereas the value transformed by Qwt
235  // does *not*. Thus, when zooming with a legend, there will always be a small bias error.
236  // In practice, this seems not to be a UI problem.
237  QPoint mouse_pos = e->pos(); // Get the mouse coordinate in the frame
238  double zoomLine = invTransform(
239  QwtPlot::yLeft, mouse_pos.y()); // Transform the y mouse coordinate into a frame value.
240 
241  double zoomScale = 1.1; // THIS IS AN ARBITRARY CONSTANT, AND PERHAPS SHOULD BE IN A DEFINE
242  // INSTEAD OF BURIED HERE
243 
244  // Set the scale
245  if (e->delta() < 0) {
246  setAxisScale(QwtPlot::yLeft, (yInterval.minValue() - zoomLine) * zoomScale + zoomLine,
247  (yInterval.maxValue() - zoomLine) * zoomScale + zoomLine);
248  } else {
249  setAxisScale(QwtPlot::yLeft, (yInterval.minValue() - zoomLine) / zoomScale + zoomLine,
250  (yInterval.maxValue() - zoomLine) / zoomScale + zoomLine);
251  }
252  }
253  QwtPlot::wheelEvent(e);
254 }
255 
259 void ScopeGadgetWidget::startPlotting()
260 {
261  if (!replotTimer)
262  return;
263 
264  if (!replotTimer->isActive())
265  replotTimer->start(m_refreshInterval);
266 }
267 
271 void ScopeGadgetWidget::stopPlotting()
272 {
273  if (!replotTimer)
274  return;
275 
276  replotTimer->stop();
277 }
278 
283 {
284  if (!legend())
285  return;
286 
287  delete m_legend;
288  m_legend = NULL;
289  insertLegend(NULL, QwtPlot::TopLegend);
290 }
291 
296 {
297  if (legend())
298  return;
299 
300  // Show a legend at the top
301  m_legend = new QwtLegend(this);
302  m_legend->setDefaultItemMode(QwtLegendData::Checkable);
303  m_legend->setFrameStyle(QFrame::Box | QFrame::Sunken);
304  m_legend->setToolTip(tr("Click legend to show/hide scope trace"));
305 
306  QPalette pal = m_legend->palette();
307  pal.setColor(m_legend->backgroundRole(), QColor(100, 100, 100)); // background colour
308  pal.setColor(QPalette::Text, QColor(0, 0, 0)); // text colour
309  m_legend->setPalette(pal);
310 
311  insertLegend(m_legend, QwtPlot::TopLegend);
312 
313  // Update the checked/unchecked state of the legend items
314  // -> this is necessary when hiding a legend where some plots are
315  // not visible, and the un-hiding it.
316  foreach (QwtPlotItem *item, this->itemList()) {
317  bool on = item->isVisible();
318  QVariant itemInfo = QwtPlot::itemToInfo(item);
319  QWidget *w = m_legend->legendWidget(itemInfo);
320  if (w && w->inherits("QwtLegendLabel"))
321  (dynamic_cast<QwtLegendLabel *>(w))->setChecked(!on);
322  }
323 
324  connect(m_legend, &QwtLegend::checked, this, &ScopeGadgetWidget::showCurve);
325 }
326 
332 void ScopeGadgetWidget::showCurve(const QVariant &itemInfo, bool on, int index)
333 {
334  Q_UNUSED(index);
335  QwtPlotItem *item = QwtPlot::infoToItem(itemInfo);
336  if (item)
337  item->setVisible(!on);
338 
339  replot();
340 }
341 
346 void ScopeGadgetWidget::uavObjectReceived(UAVObject *obj)
347 {
348  foreach (PlotData *plotdData, m_dataSources.values()) {
349  bool ret = plotdData->append(obj);
350  if (ret)
351  plotdData->setUpdatedFlagToTrue();
352  }
353 }
354 
358 void ScopeGadgetWidget::replotNewData()
359 {
360  // If the plot is not visible or there is no scope, do not replot
361  if (!isVisible() || m_scope == NULL)
362  return;
363 
364  // Update the data in the scopes
365  foreach (PlotData *plotData, m_dataSources.values()) {
366  plotData->plotNewData(plotData, m_scope, this);
367  }
368 
369  // Repaint the scopes
370  replot();
371 }
372 
377 {
378  if (m_grid) {
379  m_grid->detach();
380  }
381  if (m_scope) {
382  // Clear the plots
383  foreach (PlotData *plotData, m_dataSources.values()) {
384  plotData->deletePlots(plotData);
385  }
386 
387  // Clear the data
388  m_dataSources.clear();
389  }
390 }
391 
398 QString ScopeGadgetWidget::getUavObjectFieldUnits(QString uavObjectName, QString uavObjectFieldName)
399 {
400  // Get the uav object
401  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
402  UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
403  UAVDataObject *obj = dynamic_cast<UAVDataObject *>(objManager->getObject(uavObjectName));
404  if (!obj) {
405  qDebug() << "In scope gadget, UAVObject " << uavObjectName << " is missing";
406  return "";
407  }
408  UAVObjectField *field = obj->getField(uavObjectFieldName);
409  if (!field) {
410  qDebug() << "In scope gadget, in fields loaded from GCS config file, field"
411  << uavObjectFieldName << " of UAVObject " << uavObjectName << " is missing";
412  return "";
413  }
414 
415  // Get the units
416  QString units = field->getUnits();
417  if (units == nullptr)
418  units = QString();
419 
420  return units;
421 }
422 
427 void ScopeGadgetWidget::showEvent(QShowEvent *event)
428 {
429  replotNewData();
430  QwtPlot::showEvent(event);
431 }
432 
439 {
440  // Link to the new signal data only if this UAVObject has not been connected yet
441  if (!m_connectedUAVObjects.contains(obj->getName())) {
442  m_connectedUAVObjects.append(obj->getName());
443  connect(obj, &UAVObject::objectUpdated, this, &ScopeGadgetWidget::uavObjectReceived);
444  }
445 }
446 
451 void ScopeGadgetWidget::startTimer(int refreshInterval)
452 {
453  m_refreshInterval = refreshInterval;
454 
455  // Only start the timer if we are already connected
457  if (cm->getCurrentConnection() && replotTimer) {
458  if (!replotTimer->isActive())
459  replotTimer->start(refreshInterval);
460  else
461  replotTimer->setInterval(refreshInterval);
462  }
463 }
legend(s1, s2, s3, s4)
virtual bool append(UAVObject *obj)=0
void mouseDoubleClickEvent(QMouseEvent *e)
ScopeGadgetWidget::mouseDoubleClickEvent Turn legend on and off, then pass double-click even to QwtPl...
void connectUAVO(UAVDataObject *obj)
ScopeGadgetWidget::connectUAVO Connects UAVO update signal, but only if it hasn't yet been connected...
QIODevice * getCurrentConnection()
~ScopeGadgetWidget()
ScopeGadgetWidget::~ScopeGadgetWidget Destructor.
virtual void plotNewData(PlotData *, ScopeConfig *, ScopeGadgetWidget *)=0
Core plugin system that manages the plugins, their life cycle and their registered objects...
Definition: pluginmanager.h:53
void deleteLegend()
ScopeGadgetWidget::deleteLegend Delete legend from plot.
virtual ConnectionManager * connectionManager() const =0
void objectUpdated(UAVObject *obj)
Signal sent whenever any field of the object is updated.
void wheelEvent(QWheelEvent *e)
ScopeGadgetWidget::wheelEvent Zoom in or out, then pass mouse wheel event to QwtPlot.
void addLegend()
ScopeGadgetWidget::addLegend Add legend to plot.
ScopeGadgetWidget(QWidget *parent=nullptr)
virtual bool showOptionsDialog(const QString &group=QString(), const QString &page=QString(), QWidget *parent=nullptr)=0
Opens the application options/preferences dialog with preselected page in a specified group...
void mouseReleaseEvent(QMouseEvent *e)
ScopeGadgetWidget::mouseReleaseEvent Pass mouse release event to QwtPlot.
virtual void setUpdatedFlagToTrue()=0
static ICore * instance()
Definition: coreimpl.cpp:46
QString getUavObjectFieldUnits(QString uavObjectName, QString uavObjectFieldName)
ScopeGadgetWidget::getUavObjectFieldUnits Gets the UAVOs units, as defined in the XML...
QString getUnits() const
virtual void clearPlots()=0
QString getName()
Definition: uavobject.cpp:131
void clearPlotWidget()
ScopeGadgetWidget::clearPlotWidget.
void startTimer(int)
ScopeGadgetWidget::startTimer Starts timer.
void mousePressEvent(QMouseEvent *e)
ScopeGadgetWidget::mousePressEvent Pass mouse press event to QwtPlot.
Scope Plugin Gadget Widget.
void deviceConnected(QIODevice *device)
UAVObject * getObject(const QString &name, quint32 instId=0)
void showEvent(QShowEvent *event)
ScopeGadgetWidget::showEvent Reimplemented from QwtPlot.
e
Definition: OPPlots.m:99
virtual void deletePlots(PlotData *)=0
QwtPlotGrid * m_grid
void mouseMoveEvent(QMouseEvent *e)
ScopeGadgetWidget::mouseMoveEvent Pass mouse move event to QwtPlot.