OP-1900 have path_progress updated correctly for leg_remaining and error_below end...
[librepilot.git] / ground / openpilotgcs / src / plugins / scope / scopegadgetwidget.cpp
blobd2f1c48b4a1a9981f80460f889a3bc3f9900b9d2
1 /**
2 ******************************************************************************
4 * @file scopegadgetwidget.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * @addtogroup GCSPlugins GCS Plugins
7 * @{
8 * @addtogroup ScopePlugin Scope Gadget Plugin
9 * @{
10 * @brief The scope Gadget, graphically plots the states of UAVObjects
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "scopegadgetwidget.h"
29 #include "utils/stylehelper.h"
31 #include "extensionsystem/pluginmanager.h"
32 #include "uavobjectmanager.h"
33 #include "uavobject.h"
34 #include "coreplugin/icore.h"
35 #include "coreplugin/connectionmanager.h"
36 #include <coreplugin/icore.h>
38 #include "qwt/src/qwt_plot_curve.h"
39 #include "qwt/src/qwt_plot_grid.h"
41 #include <iostream>
42 #include <math.h>
43 #include <QDebug>
44 #include <QDir>
45 #include <QColor>
46 #include <QStringList>
47 #include <QWidget>
48 #include <QVBoxLayout>
49 #include <QPushButton>
50 #include <QMutexLocker>
51 #include <QWheelEvent>
52 #include <QMenu>
53 #include <QAction>
54 #include <QClipboard>
55 #include <QApplication>
57 #include <qwt/src/qwt_legend_label.h>
58 #include <qwt/src/qwt_picker_machine.h>
59 #include <qwt/src/qwt_plot_canvas.h>
60 #include <qwt/src/qwt_plot_layout.h>
62 ScopeGadgetWidget::ScopeGadgetWidget(QWidget *parent) : QwtPlot(parent),
63 m_csvLoggingStarted(false), m_csvLoggingEnabled(false),
64 m_csvLoggingHeaderSaved(false), m_csvLoggingDataSaved(false),
65 m_csvLoggingNameSet(false), m_csvLoggingDataValid(false),
66 m_csvLoggingDataUpdated(false), m_csvLoggingConnected(false),
67 m_csvLoggingNewFileOnConnect(false),
68 m_csvLoggingStartTime(QDateTime::currentDateTime()),
69 m_csvLoggingPath("./csvlogging/"),
70 m_plotLegend(NULL), m_picker(NULL)
72 setMouseTracking(true);
74 QwtPlotCanvas *plotCanvas = dynamic_cast<QwtPlotCanvas *>(canvas());
75 if (plotCanvas) {
76 plotCanvas->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
77 plotCanvas->setBorderRadius(8);
80 axisWidget(QwtPlot::yLeft)->setMargin(2);
81 axisWidget(QwtPlot::xBottom)->setMargin(2);
83 m_picker = new QwtPlotPicker(QwtPlot::xBottom,
84 QwtPlot::yLeft,
85 QwtPlotPicker::HLineRubberBand,
86 QwtPicker::ActiveOnly,
87 canvas());
88 m_picker->setStateMachine(new QwtPickerDragPointMachine());
89 m_picker->setRubberBandPen(QColor(Qt::darkMagenta));
90 m_picker->setTrackerPen(QColor(Qt::green));
92 // Setup the timer that replots data
93 replotTimer = new QTimer(this);
94 connect(replotTimer, SIGNAL(timeout()), this, SLOT(replotNewData()));
96 // Listen to telemetry connection/disconnection events, no point in
97 // running the scopes if we are not connected and not replaying logs.
98 // Also listen to disconnect actions from the user
99 Core::ConnectionManager *cm = Core::ICore::instance()->connectionManager();
100 connect(cm, SIGNAL(deviceAboutToDisconnect()), this, SLOT(stopPlotting()));
101 connect(cm, SIGNAL(deviceConnected(QIODevice *)), this, SLOT(startPlotting()));
103 // Listen to autopilot connection events
104 connect(cm, SIGNAL(deviceAboutToDisconnect()), this, SLOT(csvLoggingDisconnect()));
105 connect(cm, SIGNAL(deviceConnected(QIODevice *)), this, SLOT(csvLoggingConnect()));
107 setContextMenuPolicy(Qt::CustomContextMenu);
108 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(popUpMenu(const QPoint &)));
111 ScopeGadgetWidget::~ScopeGadgetWidget()
113 if (m_picker) {
114 delete m_picker;
115 m_picker = NULL;
118 if (replotTimer) {
119 replotTimer->stop();
120 delete replotTimer;
121 replotTimer = NULL;
124 // Get the object to de-monitor
125 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
126 UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
127 foreach(QString uavObjName, m_connectedUAVObjects) {
128 UAVDataObject *obj = dynamic_cast<UAVDataObject *>(objManager->getObject(uavObjName));
130 disconnect(obj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(uavObjectReceived(UAVObject *)));
133 clearCurvePlots();
136 void ScopeGadgetWidget::mousePressEvent(QMouseEvent *e)
138 QwtPlot::mousePressEvent(e);
141 void ScopeGadgetWidget::mouseReleaseEvent(QMouseEvent *e)
143 QwtPlot::mouseReleaseEvent(e);
146 void ScopeGadgetWidget::mouseDoubleClickEvent(QMouseEvent *e)
148 // On double-click, toggle legend
149 m_mutex.lock();
150 if (legend()) {
151 deleteLegend();
152 } else {
153 addLegend();
155 m_mutex.unlock();
157 // On double-click, reset plot zoom
158 setAxisAutoScale(QwtPlot::yLeft, true);
160 update();
162 QwtPlot::mouseDoubleClickEvent(e);
165 void ScopeGadgetWidget::mouseMoveEvent(QMouseEvent *e)
167 QwtPlot::mouseMoveEvent(e);
170 void ScopeGadgetWidget::wheelEvent(QWheelEvent *e)
172 // Change zoom on scroll wheel event
173 QwtInterval yInterval = axisInterval(QwtPlot::yLeft);
175 if (yInterval.minValue() != yInterval.maxValue()) { // Make sure that the two values are never the same. Sometimes axisInterval returns (0,0)
176 // Determine what y value to zoom about. NOTE, this approach has a bug that the in that
177 // the value returned by Qt includes the legend, whereas the value transformed by Qwt
178 // does *not*. Thus, when zooming with a legend, there will always be a small bias error.
179 // In practice, this seems not to be a UI problem.
180 QPoint mouse_pos = e->pos(); // Get the mouse coordinate in the frame
181 double zoomLine = invTransform(QwtPlot::yLeft, mouse_pos.y()); // Transform the y mouse coordinate into a frame value.
183 double zoomScale = 1.1; // THIS IS AN ARBITRARY CONSTANT, AND PERHAPS SHOULD BE IN A DEFINE INSTEAD OF BURIED HERE
185 m_mutex.lock(); // DOES THIS mutex.lock NEED TO BE HERE? I DON'T KNOW, I JUST COPIED IT FROM THE ABOVE CODE
186 // Set the scale
187 if (e->delta() < 0) {
188 setAxisScale(QwtPlot::yLeft,
189 (yInterval.minValue() - zoomLine) * zoomScale + zoomLine,
190 (yInterval.maxValue() - zoomLine) * zoomScale + zoomLine);
191 } else {
192 setAxisScale(QwtPlot::yLeft,
193 (yInterval.minValue() - zoomLine) / zoomScale + zoomLine,
194 (yInterval.maxValue() - zoomLine) / zoomScale + zoomLine);
196 m_mutex.unlock();
198 QwtPlot::wheelEvent(e);
201 void ScopeGadgetWidget::showEvent(QShowEvent *e)
203 replotNewData();
204 QwtPlot::showEvent(e);
208 * Starts/stops telemetry
210 void ScopeGadgetWidget::startPlotting()
212 if (replotTimer && !replotTimer->isActive()) {
213 foreach(PlotData * plot, m_curvesData.values()) {
214 if (plot->wantsInitialData()) {
215 plot->append(NULL);
218 replotTimer->start(m_refreshInterval);
222 void ScopeGadgetWidget::stopPlotting()
224 if (replotTimer) {
225 replotTimer->stop();
229 void ScopeGadgetWidget::deleteLegend()
231 if (m_plotLegend) {
232 insertLegend(NULL, QwtPlot::TopLegend);
233 m_plotLegend = NULL;
237 void ScopeGadgetWidget::addLegend()
239 if (legend()) {
240 return;
243 // Show a legend at the top
244 m_plotLegend = new QwtLegend(this);
245 m_plotLegend->setDefaultItemMode(QwtLegendData::Checkable);
246 m_plotLegend->setFrameStyle(QFrame::NoFrame);
247 m_plotLegend->setToolTip(tr("Click legend to show/hide scope trace.\nDouble click legend or plot to show/hide legend."));
249 // set colors
250 QPalette pal = m_plotLegend->palette();
251 pal.setColor(m_plotLegend->backgroundRole(), QColor(100, 100, 100));
252 pal.setColor(QPalette::Text, QColor(0, 0, 0));
253 m_plotLegend->setPalette(pal);
255 insertLegend(m_plotLegend, QwtPlot::TopLegend);
257 // Update the checked/unchecked state of the legend items
258 // -> this is necessary when hiding a legend where some plots are
259 // not visible, and then un-hiding it.
260 foreach(QwtPlotItem * plotItem, itemList()) {
261 QWidget *legendWidget = m_plotLegend->legendWidget(QwtPlot::itemToInfo(plotItem));
263 if (legendWidget && legendWidget->inherits("QwtLegendLabel")) {
264 ((QwtLegendLabel *)legendWidget)->setChecked(!plotItem->isVisible());
268 connect(m_plotLegend, SIGNAL(checked(QVariant, bool, int)), this, SLOT(showCurve(QVariant, bool, int)));
271 void ScopeGadgetWidget::preparePlot(PlotType plotType)
273 m_plotType = plotType;
275 clearCurvePlots();
277 setMinimumSize(64, 64);
278 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
280 setCanvasBackground(QColor(64, 64, 64));
282 // Add grid lines
283 QwtPlotGrid *grid = new QwtPlotGrid;
284 grid->setPen(Qt::darkGray, 1, Qt::DotLine);
285 grid->attach(this);
287 // Only start the timer if we are already connected
288 Core::ConnectionManager *cm = Core::ICore::instance()->connectionManager();
289 if (cm->isConnected() && replotTimer) {
290 if (!replotTimer->isActive()) {
291 replotTimer->start(m_refreshInterval);
292 } else {
293 replotTimer->setInterval(m_refreshInterval);
298 void ScopeGadgetWidget::showCurve(QVariant itemInfo, bool visible, int index)
300 Q_UNUSED(index);
301 QwtPlotItem *item = infoToItem(itemInfo);
302 item->setVisible(!visible);
303 emit visibilityChanged(item);
304 if (m_plotLegend) {
305 QWidget *legendItem = legend()->find((WId)item);
306 if (legendItem && legendItem->inherits("QwtLegendLabel")) {
307 ((QwtLegendLabel *)legendItem)->setChecked(visible);
311 m_mutex.lock();
312 replot();
313 m_mutex.unlock();
316 void ScopeGadgetWidget::setupSequentialPlot()
318 preparePlot(SequentialPlot);
320 setAxisScaleDraw(QwtPlot::xBottom, new QwtScaleDraw());
321 setAxisScale(QwtPlot::xBottom, 0, m_plotDataSize);
322 setAxisLabelRotation(QwtPlot::xBottom, 0.0);
323 setAxisLabelAlignment(QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom);
325 // reduce the axis font size
326 QFont fnt(axisFont(QwtPlot::xBottom));
327 fnt.setPointSize(7);
328 setAxisFont(QwtPlot::xBottom, fnt); // x-axis
329 setAxisFont(QwtPlot::yLeft, fnt); // y-axis
332 void ScopeGadgetWidget::setupChronoPlot()
334 preparePlot(ChronoPlot);
336 setAxisScaleDraw(QwtPlot::xBottom, new TimeScaleDraw());
337 uint NOW = QDateTime::currentDateTime().toTime_t();
338 setAxisScale(QwtPlot::xBottom, NOW - m_plotDataSize / 1000, NOW);
339 setAxisLabelRotation(QwtPlot::xBottom, 0.0);
340 setAxisLabelAlignment(QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom);
342 // reduce the axis font size
343 QFont fnt(axisFont(QwtPlot::xBottom));
344 fnt.setPointSize(7);
345 setAxisFont(QwtPlot::xBottom, fnt); // x-axis
346 setAxisFont(QwtPlot::yLeft, fnt); // y-axis
349 void ScopeGadgetWidget::addCurvePlot(QString objectName, QString fieldPlusSubField, int scaleFactor,
350 int meanSamples, QString mathFunction, QPen pen, bool antialiased)
352 QString fieldName = fieldPlusSubField;
353 QString elementName;
354 int element = 0;
356 if (fieldPlusSubField.contains("-")) {
357 QStringList fieldSubfield = fieldName.split("-", QString::SkipEmptyParts);
358 fieldName = fieldSubfield.at(0);
359 elementName = fieldSubfield.at(1);
362 // Get the uav object
363 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
364 UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
365 UAVDataObject *object = dynamic_cast<UAVDataObject *>(objManager->getObject(objectName));
366 if (!object) {
367 qDebug() << "Object" << objectName << "is missing";
368 return;
371 UAVObjectField *field = object->getField(fieldName);
372 if (!field) {
373 qDebug() << "In scope gadget, in fields loaded from GCS config file, field" <<
374 fieldName << "of object" << objectName << "is missing";
375 return;
378 if (!elementName.isEmpty()) {
379 element = field->getElementNames().indexOf(QRegExp(elementName, Qt::CaseSensitive, QRegExp::FixedString));
380 if (element < 0) {
381 qDebug() << "In scope gadget, in fields loaded from GCS config file, field" <<
382 fieldName << "of object" << objectName << "element name" << elementName << "is missing";
383 return;
387 PlotData *plotData;
389 if (m_plotType == SequentialPlot) {
390 plotData = new SequentialPlotData(object, field, element, scaleFactor,
391 meanSamples, mathFunction, m_plotDataSize,
392 pen, antialiased);
393 } else if (m_plotType == ChronoPlot) {
394 plotData = new ChronoPlotData(object, field, element, scaleFactor,
395 meanSamples, mathFunction, m_plotDataSize,
396 pen, antialiased);
398 connect(this, SIGNAL(visibilityChanged(QwtPlotItem *)), plotData, SLOT(visibilityChanged(QwtPlotItem *)));
399 plotData->attach(this);
401 // Keep the curve details for later
402 m_curvesData.insert(plotData->plotName(), plotData);
404 // Link to the new signal data only if this UAVObject has not been connected yet
405 if (!m_connectedUAVObjects.contains(object->getName())) {
406 m_connectedUAVObjects.append(object->getName());
407 connect(object, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(uavObjectReceived(UAVObject *)));
410 m_mutex.lock();
411 replot();
412 m_mutex.unlock();
415 void ScopeGadgetWidget::uavObjectReceived(UAVObject *obj)
417 foreach(PlotData * plotData, m_curvesData.values()) {
418 if (plotData->append(obj)) {
419 m_csvLoggingDataUpdated = 1;
422 csvLoggingAddData();
425 void ScopeGadgetWidget::replotNewData()
427 if (!isVisible()) {
428 return;
431 QMutexLocker locker(&m_mutex);
432 foreach(PlotData * plotData, m_curvesData.values()) {
433 plotData->removeStaleData();
434 plotData->updatePlotData();
437 QDateTime NOW = QDateTime::currentDateTime();
438 double toTime = NOW.toTime_t();
439 toTime += NOW.time().msec() / 1000.0;
440 if (m_plotType == ChronoPlot) {
441 setAxisScale(QwtPlot::xBottom, toTime - m_plotDataSize, toTime);
444 csvLoggingInsertData();
446 replot();
449 void ScopeGadgetWidget::clearCurvePlots()
451 foreach(PlotData * plotData, m_curvesData.values()) {
452 delete plotData;
455 m_curvesData.clear();
458 void ScopeGadgetWidget::saveState(QSettings *qSettings)
460 // plot state
461 int i = 1;
463 foreach(PlotData * plotData, m_curvesData.values()) {
464 bool plotVisible = plotData->isVisible();
466 if (!plotVisible) {
467 qSettings->setValue(QString("plot%1").arg(i), plotVisible);
469 i++;
471 // legend state
472 qSettings->setValue("legendVisible", legend() != NULL);
475 void ScopeGadgetWidget::restoreState(QSettings *qSettings)
477 // plot state
478 int i = 1;
480 foreach(PlotData * plotData, m_curvesData.values()) {
481 plotData->setVisible(qSettings->value(QString("plot%1").arg(i), true).toBool());
482 i++;
484 // legend state
485 bool legendVisible = qSettings->value("legendVisible", true).toBool();
486 if (legendVisible) {
487 addLegend();
488 } else {
489 deleteLegend();
494 int csvLoggingEnable;
495 int csvLoggingHeaderSaved;
496 int csvLoggingDataSaved;
497 QString csvLoggingPath;
498 QFile csvLoggingFile;
500 int ScopeGadgetWidget::csvLoggingStart()
502 if (!m_csvLoggingStarted) {
503 if (m_csvLoggingEnabled) {
504 if ((!m_csvLoggingNewFileOnConnect) || (m_csvLoggingNewFileOnConnect && m_csvLoggingConnected)) {
505 QDateTime NOW = QDateTime::currentDateTime();
506 m_csvLoggingStartTime = NOW;
507 m_csvLoggingHeaderSaved = 0;
508 m_csvLoggingDataSaved = 0;
509 m_csvLoggingBuffer.clear();
510 QDir PathCheck(m_csvLoggingPath);
511 if (!PathCheck.exists()) {
512 PathCheck.mkpath("./");
515 if (m_csvLoggingNameSet) {
516 m_csvLoggingFile.setFileName(QString("%1/%2_%3_%4.csv").arg(m_csvLoggingPath).arg(m_csvLoggingName).arg(NOW.toString("yyyy-MM-dd")).arg(NOW.toString("hh-mm-ss")));
517 } else {
518 m_csvLoggingFile.setFileName(QString("%1/Log_%2_%3.csv").arg(m_csvLoggingPath).arg(NOW.toString("yyyy-MM-dd")).arg(NOW.toString("hh-mm-ss")));
520 QDir FileCheck(m_csvLoggingFile.fileName());
521 if (FileCheck.exists()) {
522 m_csvLoggingFile.setFileName("");
523 } else {
524 m_csvLoggingStarted = 1;
525 csvLoggingInsertHeader();
531 return 0;
534 int ScopeGadgetWidget::csvLoggingStop()
536 m_csvLoggingStarted = 0;
538 return 0;
541 int ScopeGadgetWidget::csvLoggingInsertHeader()
543 if (!m_csvLoggingStarted) {
544 return -1;
546 if (m_csvLoggingHeaderSaved) {
547 return -2;
549 if (m_csvLoggingDataSaved) {
550 return -3;
553 m_csvLoggingHeaderSaved = 1;
554 if (m_csvLoggingFile.open(QIODevice::WriteOnly | QIODevice::Append) == false) {
555 qDebug() << "Unable to open " << m_csvLoggingFile.fileName() << " for csv logging Header";
556 } else {
557 QTextStream ts(&m_csvLoggingFile);
558 ts << "date" << ", " << "Time" << ", " << "Sec since start" << ", " << "Connected" << ", " << "Data changed";
560 foreach(PlotData * plotData2, m_curvesData.values()) {
561 ts << ", ";
562 ts << plotData2->objectName();
563 ts << "." << plotData2->field()->getName();
564 if (!plotData2->elementName().isEmpty()) {
565 ts << "." << plotData2->elementName();
568 ts << endl;
569 m_csvLoggingFile.close();
571 return 0;
574 int ScopeGadgetWidget::csvLoggingAddData()
576 if (!m_csvLoggingStarted) {
577 return -1;
579 m_csvLoggingDataValid = false;
580 QDateTime NOW = QDateTime::currentDateTime();
581 QString tempString;
583 QTextStream ss(&tempString);
584 ss << NOW.toString("yyyy-MM-dd") << ", " << NOW.toString("hh:mm:ss.z") << ", ";
586 #if QT_VERSION >= 0x040700
587 ss << (NOW.toMSecsSinceEpoch() - m_csvLoggingStartTime.toMSecsSinceEpoch()) / 1000.00;
588 #else
589 ss << (NOW.toTime_t() - m_csvLoggingStartTime.toTime_t());
590 #endif
591 ss << ", " << m_csvLoggingConnected << ", " << m_csvLoggingDataUpdated;
592 m_csvLoggingDataUpdated = false;
594 foreach(PlotData * plotData2, m_curvesData.values()) {
595 ss << ", ";
596 if (plotData2->hasData()) {
597 ss << plotData2->lastDataAsString();
598 m_csvLoggingDataValid = true;
601 ss << endl;
602 if (m_csvLoggingDataValid) {
603 QTextStream ts(&m_csvLoggingBuffer);
604 ts << tempString;
607 return 0;
610 int ScopeGadgetWidget::csvLoggingInsertData()
612 if (!m_csvLoggingStarted) {
613 return -1;
615 m_csvLoggingDataSaved = 1;
617 if (m_csvLoggingFile.open(QIODevice::WriteOnly | QIODevice::Append) == false) {
618 qDebug() << "Unable to open " << m_csvLoggingFile.fileName() << " for csv logging Data";
619 } else {
620 QTextStream ts(&m_csvLoggingFile);
621 ts << m_csvLoggingBuffer;
622 m_csvLoggingFile.close();
624 m_csvLoggingBuffer.clear();
626 return 0;
629 void ScopeGadgetWidget::csvLoggingSetName(QString newName)
631 m_csvLoggingName = newName;
632 m_csvLoggingNameSet = 1;
635 void ScopeGadgetWidget::csvLoggingConnect()
637 m_csvLoggingConnected = 1;
638 if (m_csvLoggingNewFileOnConnect) {
639 csvLoggingStart();
643 void ScopeGadgetWidget::csvLoggingDisconnect()
645 m_csvLoggingHeaderSaved = 0;
646 m_csvLoggingConnected = 0;
647 if (m_csvLoggingNewFileOnConnect) {
648 csvLoggingStop();
652 void ScopeGadgetWidget::popUpMenu(const QPoint &mousePosition)
654 Q_UNUSED(mousePosition);
656 QMenu menu;
657 QAction *action = menu.addAction(tr("Clear"));
658 connect(action, &QAction::triggered, this, &ScopeGadgetWidget::clearPlot);
659 action = menu.addAction(tr("Copy to Clipboard"));
660 connect(action, &QAction::triggered, this, &ScopeGadgetWidget::copyToClipboardAsImage);
661 menu.addSeparator();
662 action = menu.addAction(tr("Options..."));
663 connect(action, &QAction::triggered, this, &ScopeGadgetWidget::showOptionDialog);
664 menu.exec(QCursor::pos());
667 void ScopeGadgetWidget::clearPlot()
669 m_mutex.lock();
670 foreach(PlotData * plot, m_curvesData.values()) {
671 plot->clear();
673 m_mutex.unlock();
674 replot();
677 void ScopeGadgetWidget::copyToClipboardAsImage()
679 QPixmap pixmap = QWidget::grab();
681 if (pixmap.isNull()) {
682 qDebug("Failed to capture the plot");
683 return;
685 QClipboard *clipboard = QApplication::clipboard();
686 clipboard->setPixmap(pixmap);
689 void ScopeGadgetWidget::showOptionDialog()
691 Core::ICore::instance()->showOptionsDialog("ScopeGadget", objectName());