LP-56 - Better txpid option namings, fix tabs-spaces, tooltips. headers, variable...
[librepilot.git] / ground / openpilotgcs / src / plugins / scope / scopegadgetwidget.cpp
blob9dfc4c7137e9ba2d8d64bc510304d95bcd6256eb
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 {
394 Q_ASSERT(m_plotType == ChronoPlot);
395 plotData = new ChronoPlotData(object, field, element, scaleFactor,
396 meanSamples, mathFunction, m_plotDataSize,
397 pen, antialiased);
399 connect(this, SIGNAL(visibilityChanged(QwtPlotItem *)), plotData, SLOT(visibilityChanged(QwtPlotItem *)));
400 plotData->attach(this);
402 // Keep the curve details for later
403 m_curvesData.insert(plotData->plotName(), plotData);
405 // Link to the new signal data only if this UAVObject has not been connected yet
406 if (!m_connectedUAVObjects.contains(object->getName())) {
407 m_connectedUAVObjects.append(object->getName());
408 connect(object, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(uavObjectReceived(UAVObject *)));
411 m_mutex.lock();
412 replot();
413 m_mutex.unlock();
416 void ScopeGadgetWidget::uavObjectReceived(UAVObject *obj)
418 foreach(PlotData * plotData, m_curvesData.values()) {
419 if (plotData->append(obj)) {
420 m_csvLoggingDataUpdated = 1;
423 csvLoggingAddData();
426 void ScopeGadgetWidget::replotNewData()
428 if (!isVisible()) {
429 return;
432 QMutexLocker locker(&m_mutex);
433 foreach(PlotData * plotData, m_curvesData.values()) {
434 plotData->removeStaleData();
435 plotData->updatePlotData();
438 QDateTime NOW = QDateTime::currentDateTime();
439 double toTime = NOW.toTime_t();
440 toTime += NOW.time().msec() / 1000.0;
441 if (m_plotType == ChronoPlot) {
442 setAxisScale(QwtPlot::xBottom, toTime - m_plotDataSize, toTime);
445 csvLoggingInsertData();
447 replot();
450 void ScopeGadgetWidget::clearCurvePlots()
452 foreach(PlotData * plotData, m_curvesData.values()) {
453 delete plotData;
456 m_curvesData.clear();
459 void ScopeGadgetWidget::saveState(QSettings *qSettings)
461 // plot state
462 int i = 1;
464 foreach(PlotData * plotData, m_curvesData.values()) {
465 bool plotVisible = plotData->isVisible();
467 if (!plotVisible) {
468 qSettings->setValue(QString("plot%1").arg(i), plotVisible);
470 i++;
472 // legend state
473 qSettings->setValue("legendVisible", legend() != NULL);
476 void ScopeGadgetWidget::restoreState(QSettings *qSettings)
478 // plot state
479 int i = 1;
481 foreach(PlotData * plotData, m_curvesData.values()) {
482 plotData->setVisible(qSettings->value(QString("plot%1").arg(i), true).toBool());
483 i++;
485 // legend state
486 bool legendVisible = qSettings->value("legendVisible", true).toBool();
487 if (legendVisible) {
488 addLegend();
489 } else {
490 deleteLegend();
495 int csvLoggingEnable;
496 int csvLoggingHeaderSaved;
497 int csvLoggingDataSaved;
498 QString csvLoggingPath;
499 QFile csvLoggingFile;
501 int ScopeGadgetWidget::csvLoggingStart()
503 if (!m_csvLoggingStarted) {
504 if (m_csvLoggingEnabled) {
505 if ((!m_csvLoggingNewFileOnConnect) || (m_csvLoggingNewFileOnConnect && m_csvLoggingConnected)) {
506 QDateTime NOW = QDateTime::currentDateTime();
507 m_csvLoggingStartTime = NOW;
508 m_csvLoggingHeaderSaved = 0;
509 m_csvLoggingDataSaved = 0;
510 m_csvLoggingBuffer.clear();
511 QDir PathCheck(m_csvLoggingPath);
512 if (!PathCheck.exists()) {
513 PathCheck.mkpath("./");
516 if (m_csvLoggingNameSet) {
517 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")));
518 } else {
519 m_csvLoggingFile.setFileName(QString("%1/Log_%2_%3.csv").arg(m_csvLoggingPath).arg(NOW.toString("yyyy-MM-dd")).arg(NOW.toString("hh-mm-ss")));
521 QDir FileCheck(m_csvLoggingFile.fileName());
522 if (FileCheck.exists()) {
523 m_csvLoggingFile.setFileName("");
524 } else {
525 m_csvLoggingStarted = 1;
526 csvLoggingInsertHeader();
532 return 0;
535 int ScopeGadgetWidget::csvLoggingStop()
537 m_csvLoggingStarted = 0;
539 return 0;
542 int ScopeGadgetWidget::csvLoggingInsertHeader()
544 if (!m_csvLoggingStarted) {
545 return -1;
547 if (m_csvLoggingHeaderSaved) {
548 return -2;
550 if (m_csvLoggingDataSaved) {
551 return -3;
554 m_csvLoggingHeaderSaved = 1;
555 if (m_csvLoggingFile.open(QIODevice::WriteOnly | QIODevice::Append) == false) {
556 qDebug() << "Unable to open " << m_csvLoggingFile.fileName() << " for csv logging Header";
557 } else {
558 QTextStream ts(&m_csvLoggingFile);
559 ts << "date" << ", " << "Time" << ", " << "Sec since start" << ", " << "Connected" << ", " << "Data changed";
561 foreach(PlotData * plotData2, m_curvesData.values()) {
562 ts << ", ";
563 ts << plotData2->objectName();
564 ts << "." << plotData2->field()->getName();
565 if (!plotData2->elementName().isEmpty()) {
566 ts << "." << plotData2->elementName();
569 ts << endl;
570 m_csvLoggingFile.close();
572 return 0;
575 int ScopeGadgetWidget::csvLoggingAddData()
577 if (!m_csvLoggingStarted) {
578 return -1;
580 m_csvLoggingDataValid = false;
581 QDateTime NOW = QDateTime::currentDateTime();
582 QString tempString;
584 QTextStream ss(&tempString);
585 ss << NOW.toString("yyyy-MM-dd") << ", " << NOW.toString("hh:mm:ss.z") << ", ";
587 #if QT_VERSION >= 0x040700
588 ss << (NOW.toMSecsSinceEpoch() - m_csvLoggingStartTime.toMSecsSinceEpoch()) / 1000.00;
589 #else
590 ss << (NOW.toTime_t() - m_csvLoggingStartTime.toTime_t());
591 #endif
592 ss << ", " << m_csvLoggingConnected << ", " << m_csvLoggingDataUpdated;
593 m_csvLoggingDataUpdated = false;
595 foreach(PlotData * plotData2, m_curvesData.values()) {
596 ss << ", ";
597 if (plotData2->hasData()) {
598 ss << plotData2->lastDataAsString();
599 m_csvLoggingDataValid = true;
602 ss << endl;
603 if (m_csvLoggingDataValid) {
604 QTextStream ts(&m_csvLoggingBuffer);
605 ts << tempString;
608 return 0;
611 int ScopeGadgetWidget::csvLoggingInsertData()
613 if (!m_csvLoggingStarted) {
614 return -1;
616 m_csvLoggingDataSaved = 1;
618 if (m_csvLoggingFile.open(QIODevice::WriteOnly | QIODevice::Append) == false) {
619 qDebug() << "Unable to open " << m_csvLoggingFile.fileName() << " for csv logging Data";
620 } else {
621 QTextStream ts(&m_csvLoggingFile);
622 ts << m_csvLoggingBuffer;
623 m_csvLoggingFile.close();
625 m_csvLoggingBuffer.clear();
627 return 0;
630 void ScopeGadgetWidget::csvLoggingSetName(QString newName)
632 m_csvLoggingName = newName;
633 m_csvLoggingNameSet = 1;
636 void ScopeGadgetWidget::csvLoggingConnect()
638 m_csvLoggingConnected = 1;
639 if (m_csvLoggingNewFileOnConnect) {
640 csvLoggingStart();
644 void ScopeGadgetWidget::csvLoggingDisconnect()
646 m_csvLoggingHeaderSaved = 0;
647 m_csvLoggingConnected = 0;
648 if (m_csvLoggingNewFileOnConnect) {
649 csvLoggingStop();
653 void ScopeGadgetWidget::popUpMenu(const QPoint &mousePosition)
655 Q_UNUSED(mousePosition);
657 QMenu menu;
658 QAction *action = menu.addAction(tr("Clear"));
659 connect(action, &QAction::triggered, this, &ScopeGadgetWidget::clearPlot);
660 action = menu.addAction(tr("Copy to Clipboard"));
661 connect(action, &QAction::triggered, this, &ScopeGadgetWidget::copyToClipboardAsImage);
662 menu.addSeparator();
663 action = menu.addAction(tr("Options..."));
664 connect(action, &QAction::triggered, this, &ScopeGadgetWidget::showOptionDialog);
665 menu.exec(QCursor::pos());
668 void ScopeGadgetWidget::clearPlot()
670 m_mutex.lock();
671 foreach(PlotData * plot, m_curvesData.values()) {
672 plot->clear();
674 m_mutex.unlock();
675 replot();
678 void ScopeGadgetWidget::copyToClipboardAsImage()
680 QPixmap pixmap = QWidget::grab();
682 if (pixmap.isNull()) {
683 qDebug("Failed to capture the plot");
684 return;
686 QClipboard *clipboard = QApplication::clipboard();
687 clipboard->setPixmap(pixmap);
690 void ScopeGadgetWidget::showOptionDialog()
692 Core::ICore::instance()->showOptionsDialog("ScopeGadget", objectName());