1 /* -*- c++ -*- */
2 /*
3  * Copyright 2012 Free Software Foundation, Inc.
4  *
5  * This file is part of GNU Radio
6  *
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  *
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include <gnuradio/qtgui/DisplayPlot.h>
24 
25 #include <qwt_legend.h>
26 #include <qwt_scale_draw.h>
27 #include <QColor>
28 #include <QDebug>
29 #include <cmath>
30 #include <iostream>
31 #include <stdexcept>
32 
DisplayPlot(int nplots,QWidget * parent)33 DisplayPlot::DisplayPlot(int nplots, QWidget* parent)
34     : QwtPlot(parent), d_nplots(nplots), d_stop(false)
35 {
36     qRegisterMetaType<QColorList>("QColorList");
37     resize(parent->width(), parent->height());
38 
39     d_autoscale_state = false;
40 
41     // Disable polygon clipping
42 #if QWT_VERSION < 0x060000
43     QwtPainter::setDeviceClipping(false);
44 #else
45     QwtPainter::setPolylineSplitting(false);
46 #endif
47 
48 #if QWT_VERSION < 0x060000
49     // We don't need the cache here
50     canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false);
51     canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false);
52 #endif
53 
54     QColor default_palette_color = QColor("white");
55     setPaletteColor(default_palette_color);
56 
57     d_panner = new QwtPlotPanner(canvas());
58     d_panner->setAxisEnabled(QwtPlot::yRight, false);
59     d_panner->setMouseButton(Qt::MidButton, Qt::ControlModifier);
60 
61     // emit the position of clicks on widget
62     d_picker = new QwtDblClickPlotPicker(canvas());
63 
64 #if QWT_VERSION < 0x060000
65     connect(d_picker,
66             SIGNAL(selected(const QwtDoublePoint&)),
67             this,
68             SLOT(onPickerPointSelected(const QwtDoublePoint&)));
69 #else
70     d_picker->setStateMachine(new QwtPickerDblClickPointMachine());
71     connect(d_picker,
72             SIGNAL(selected(const QPointF&)),
73             this,
74             SLOT(onPickerPointSelected6(const QPointF&)));
75 #endif
76 
77     // Configure magnify on mouse wheel
78     d_magnifier = new QwtPlotMagnifier(canvas());
79     d_magnifier->setAxisEnabled(QwtPlot::xBottom, false);
80 
81     // Avoid jumping when labels with more/less digits
82     // appear/disappear when scrolling vertically
83 
84     const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font());
85     QwtScaleDraw* sd = axisScaleDraw(QwtPlot::yLeft);
86     sd->setMinimumExtent(fm.width("100.00"));
87 
88     QwtLegend* legendDisplay = new QwtLegend(this);
89 
90 #if QWT_VERSION < 0x060100
91     legendDisplay->setItemMode(QwtLegend::CheckableItem);
92     insertLegend(legendDisplay);
93     connect(this,
94             SIGNAL(legendChecked(QwtPlotItem*, bool)),
95             this,
96             SLOT(legendEntryChecked(QwtPlotItem*, bool)));
97 #else  /* QWT_VERSION < 0x060100 */
98     legendDisplay->setDefaultItemMode(QwtLegendData::Checkable);
99     insertLegend(legendDisplay);
100     connect(legendDisplay,
101             SIGNAL(checked(const QVariant&, bool, int)),
102             this,
103             SLOT(legendEntryChecked(const QVariant&, bool, int)));
104 #endif /* QWT_VERSION < 0x060100 */
105 }
106 
~DisplayPlot()107 DisplayPlot::~DisplayPlot()
108 {
109     // d_zoomer and d_panner deleted when parent deleted
110 }
111 
disableLegend()112 void DisplayPlot::disableLegend()
113 {
114     // Haven't found a good way to toggle it on/off
115     insertLegend(NULL);
116 }
117 
setYaxis(double min,double max)118 void DisplayPlot::setYaxis(double min, double max)
119 {
120     setAxisScale(QwtPlot::yLeft, min, max);
121     if (!d_autoscale_state)
122         d_zoomer->setZoomBase();
123 }
124 
setXaxis(double min,double max)125 void DisplayPlot::setXaxis(double min, double max)
126 {
127     setAxisScale(QwtPlot::xBottom, min, max);
128     d_zoomer->setZoomBase();
129 }
130 
setLineLabel(unsigned int which,QString label)131 void DisplayPlot::setLineLabel(unsigned int which, QString label)
132 {
133     if (which >= d_plot_curve.size())
134         throw std::runtime_error("DisplayPlot::setLineLabel: index out of bounds");
135     d_plot_curve[which]->setTitle(label);
136 }
137 
getLineLabel(unsigned int which)138 QString DisplayPlot::getLineLabel(unsigned int which)
139 {
140     if (which >= d_plot_curve.size())
141         throw std::runtime_error("DisplayPlot::getLineLabel: index out of bounds");
142     return d_plot_curve[which]->title().text();
143 }
144 
setLineColor(unsigned int which,QColor color)145 void DisplayPlot::setLineColor(unsigned int which, QColor color)
146 {
147     if (which < d_nplots) {
148         // Set the color of the pen
149         QPen pen(d_plot_curve[which]->pen());
150         pen.setColor(color);
151         d_plot_curve[which]->setPen(pen);
152         // And set the color of the markers
153 #if QWT_VERSION < 0x060000
154         // d_plot_curve[which]->setBrush(QBrush(QColor(color)));
155         d_plot_curve[which]->setPen(pen);
156         QwtSymbol sym = (QwtSymbol)d_plot_curve[which]->symbol();
157         setLineMarker(which, sym.style());
158 #else
159         QwtSymbol* sym = (QwtSymbol*)d_plot_curve[which]->symbol();
160         if (sym) {
161             sym->setColor(color);
162             sym->setPen(pen);
163             d_plot_curve[which]->setSymbol(sym);
164         }
165 #endif
166     }
167 }
168 
getLineColor(unsigned int which) const169 QColor DisplayPlot::getLineColor(unsigned int which) const
170 {
171     // If that plot doesn't exist then return black.
172     if (which < d_nplots)
173         return d_plot_curve[which]->pen().color();
174     return QColor("black");
175 }
176 
177 // Use a preprocessor macro to create a bunch of hooks for Q_PROPERTY and hence the
178 // stylesheet.
179 #define SETUPLINE(i, im1)                                                               \
180     void DisplayPlot::setLineColor##i(QColor c) { setLineColor(im1, c); }               \
181     const QColor DisplayPlot::getLineColor##i() const { return getLineColor(im1); }     \
182     void DisplayPlot::setLineWidth##i(int width) { setLineWidth(im1, width); }          \
183     int DisplayPlot::getLineWidth##i() const { return getLineWidth(im1); }              \
184     void DisplayPlot::setLineStyle##i(Qt::PenStyle ps) { setLineStyle(im1, ps); }       \
185     const Qt::PenStyle DisplayPlot::getLineStyle##i() const                             \
186     {                                                                                   \
187         return getLineStyle(im1);                                                       \
188     }                                                                                   \
189     void DisplayPlot::setLineMarker##i(QwtSymbol::Style ms) { setLineMarker(im1, ms); } \
190     const QwtSymbol::Style DisplayPlot::getLineMarker##i() const                        \
191     {                                                                                   \
192         return getLineMarker(im1);                                                      \
193     }                                                                                   \
194     void DisplayPlot::setMarkerAlpha##i(int alpha) { setMarkerAlpha(im1, alpha); }      \
195     int DisplayPlot::getMarkerAlpha##i() const { return getMarkerAlpha(im1); }
196 SETUPLINE(1, 0)
197 SETUPLINE(2, 1)
198 SETUPLINE(3, 2)
199 SETUPLINE(4, 3)
200 SETUPLINE(5, 4)
201 SETUPLINE(6, 5)
202 SETUPLINE(7, 6)
203 SETUPLINE(8, 7)
204 SETUPLINE(9, 8)
205 
setZoomerColor(QColor c)206 void DisplayPlot::setZoomerColor(QColor c)
207 {
208     d_zoomer->setRubberBandPen(c);
209     d_zoomer->setTrackerPen(c);
210 }
211 
getZoomerColor() const212 QColor DisplayPlot::getZoomerColor() const { return d_zoomer->rubberBandPen().color(); }
213 
setPaletteColor(QColor c)214 void DisplayPlot::setPaletteColor(QColor c)
215 {
216     QPalette palette;
217     palette.setColor(canvas()->backgroundRole(), c);
218     canvas()->setPalette(palette);
219 }
220 
getPaletteColor() const221 QColor DisplayPlot::getPaletteColor() const
222 {
223     return canvas()->palette().color(canvas()->backgroundRole());
224 }
225 
setAxisLabelFontSize(int axisId,int fs)226 void DisplayPlot::setAxisLabelFontSize(int axisId, int fs)
227 {
228     QwtText axis_title = QwtText(axisWidget(axisId)->title());
229     QFont font = QFont(axis_title.font());
230     font.setPointSize(fs);
231     axis_title.setFont(font);
232     axisWidget(axisId)->setTitle(axis_title);
233 }
234 
getAxisLabelFontSize(int axisId) const235 int DisplayPlot::getAxisLabelFontSize(int axisId) const
236 {
237     return axisWidget(axisId)->title().font().pointSize();
238 }
239 
setYaxisLabelFontSize(int fs)240 void DisplayPlot::setYaxisLabelFontSize(int fs)
241 {
242     setAxisLabelFontSize(QwtPlot::yLeft, fs);
243 }
244 
getYaxisLabelFontSize() const245 int DisplayPlot::getYaxisLabelFontSize() const
246 {
247     int fs = getAxisLabelFontSize(QwtPlot::yLeft);
248     return fs;
249 }
250 
setXaxisLabelFontSize(int fs)251 void DisplayPlot::setXaxisLabelFontSize(int fs)
252 {
253     setAxisLabelFontSize(QwtPlot::xBottom, fs);
254 }
255 
getXaxisLabelFontSize() const256 int DisplayPlot::getXaxisLabelFontSize() const
257 {
258     int fs = getAxisLabelFontSize(QwtPlot::xBottom);
259     return fs;
260 }
261 
setAxesLabelFontSize(int fs)262 void DisplayPlot::setAxesLabelFontSize(int fs)
263 {
264     setAxisLabelFontSize(QwtPlot::yLeft, fs);
265     setAxisLabelFontSize(QwtPlot::xBottom, fs);
266 }
267 
getAxesLabelFontSize() const268 int DisplayPlot::getAxesLabelFontSize() const
269 {
270     // Returns 0 if all axes do not have the same font size.
271     int fs = getAxisLabelFontSize(QwtPlot::yLeft);
272     if (getAxisLabelFontSize(QwtPlot::xBottom) != fs)
273         return 0;
274     return fs;
275 }
276 
setLineWidth(unsigned int which,int width)277 void DisplayPlot::setLineWidth(unsigned int which, int width)
278 {
279     if (which < d_nplots) {
280         // Set the new line width
281         QPen pen(d_plot_curve[which]->pen());
282         pen.setWidth(width);
283         d_plot_curve[which]->setPen(pen);
284 
285         // Scale the marker size proportionally
286 #if QWT_VERSION < 0x060000
287         QwtSymbol sym = (QwtSymbol)d_plot_curve[which]->symbol();
288         sym.setSize(7 + 10 * log10(1.0 * width), 7 + 10 * log10(1.0 * width));
289         d_plot_curve[which]->setSymbol(sym);
290 #else
291         QwtSymbol* sym = (QwtSymbol*)d_plot_curve[which]->symbol();
292         if (sym) {
293             sym->setSize(7 + 10 * log10(1.0 * width), 7 + 10 * log10(1.0 * width));
294             d_plot_curve[which]->setSymbol(sym);
295         }
296 #endif
297     }
298 }
299 
getLineWidth(unsigned int which) const300 int DisplayPlot::getLineWidth(unsigned int which) const
301 {
302     if (which < d_nplots) {
303         return d_plot_curve[which]->pen().width();
304     } else {
305         return 0;
306     }
307 }
308 
setLineStyle(unsigned int which,Qt::PenStyle style)309 void DisplayPlot::setLineStyle(unsigned int which, Qt::PenStyle style)
310 {
311     if (which < d_nplots) {
312         QPen pen(d_plot_curve[which]->pen());
313         pen.setStyle(style);
314         d_plot_curve[which]->setPen(pen);
315     }
316 }
317 
getLineStyle(unsigned int which) const318 const Qt::PenStyle DisplayPlot::getLineStyle(unsigned int which) const
319 {
320     if (which < d_nplots) {
321         return d_plot_curve[which]->pen().style();
322     } else {
323         return Qt::SolidLine;
324     }
325 }
326 
setLineMarker(unsigned int which,QwtSymbol::Style marker)327 void DisplayPlot::setLineMarker(unsigned int which, QwtSymbol::Style marker)
328 {
329     if (which < d_nplots) {
330 #if QWT_VERSION < 0x060000
331         QwtSymbol sym = (QwtSymbol)d_plot_curve[which]->symbol();
332         QPen pen(d_plot_curve[which]->pen());
333         QBrush brush(pen.color());
334         sym.setStyle(marker);
335         sym.setPen(pen);
336         sym.setBrush(brush);
337         d_plot_curve[which]->setSymbol(sym);
338 #else
339         QwtSymbol* sym = (QwtSymbol*)d_plot_curve[which]->symbol();
340         if (sym) {
341             sym->setStyle(marker);
342             d_plot_curve[which]->setSymbol(sym);
343         }
344 #endif
345     }
346 }
347 
getLineMarker(unsigned int which) const348 const QwtSymbol::Style DisplayPlot::getLineMarker(unsigned int which) const
349 {
350     if (which < d_nplots) {
351 #if QWT_VERSION < 0x060000
352         QwtSymbol sym = (QwtSymbol)d_plot_curve[which]->symbol();
353         return sym.style();
354 #else
355         QwtSymbol* sym = (QwtSymbol*)d_plot_curve[which]->symbol();
356         return sym->style();
357 #endif
358     } else {
359         return QwtSymbol::NoSymbol;
360     }
361 }
362 
setMarkerAlpha(unsigned int which,int alpha)363 void DisplayPlot::setMarkerAlpha(unsigned int which, int alpha)
364 {
365     if (which < d_nplots) {
366         // Get the pen color
367         QPen pen(d_plot_curve[which]->pen());
368         QColor color = pen.color();
369 
370         // Set new alpha and update pen
371         color.setAlpha(alpha);
372         pen.setColor(color);
373         d_plot_curve[which]->setPen(pen);
374 
375         // And set the new color for the markers
376 #if QWT_VERSION < 0x060000
377         QwtSymbol sym = (QwtSymbol)d_plot_curve[which]->symbol();
378         setLineMarker(which, sym.style());
379 #else
380         QwtSymbol* sym = (QwtSymbol*)d_plot_curve[which]->symbol();
381         if (sym) {
382             sym->setColor(color);
383             sym->setPen(pen);
384             d_plot_curve[which]->setSymbol(sym);
385         }
386 #endif
387     }
388 }
389 
getMarkerAlpha(unsigned int which) const390 int DisplayPlot::getMarkerAlpha(unsigned int which) const
391 {
392     if (which < d_nplots) {
393         return d_plot_curve[which]->pen().color().alpha();
394     } else {
395         return 0;
396     }
397 }
398 
setStop(bool on)399 void DisplayPlot::setStop(bool on) { d_stop = on; }
400 
resizeSlot(QSize * s)401 void DisplayPlot::resizeSlot(QSize* s)
402 {
403     // -10 is to spare some room for the legend and x-axis label
404     resize(s->width() - 10, s->height() - 10);
405 }
406 
legendEntryChecked(QwtPlotItem * plotItem,bool on)407 void DisplayPlot::legendEntryChecked(QwtPlotItem* plotItem, bool on)
408 {
409     plotItem->setVisible(!on);
410     replot();
411 }
412 
legendEntryChecked(const QVariant & plotItem,bool on,int index)413 void DisplayPlot::legendEntryChecked(const QVariant& plotItem, bool on, int index)
414 {
415 #if QWT_VERSION < 0x060100
416     std::runtime_error("DisplayPlot::legendEntryChecked with QVariant not enabled in "
417                        "this version of QWT.\n");
418 #else
419     QwtPlotItem* p = infoToItem(plotItem);
420     legendEntryChecked(p, on);
421 #endif /* QWT_VERSION < 0x060100 */
422 }
423 
onPickerPointSelected(const QwtDoublePoint & p)424 void DisplayPlot::onPickerPointSelected(const QwtDoublePoint& p)
425 {
426     QPointF point = p;
427     // fprintf(stderr,"onPickerPointSelected %f %f\n", point.x(), point.y());
428     emit plotPointSelected(point);
429 }
430 
onPickerPointSelected6(const QPointF & p)431 void DisplayPlot::onPickerPointSelected6(const QPointF& p)
432 {
433     QPointF point = p;
434     // fprintf(stderr,"onPickerPointSelected %f %f\n", point.x(), point.y());
435     emit plotPointSelected(point);
436 }
437 
setAxisLabels(bool en)438 void DisplayPlot::setAxisLabels(bool en)
439 {
440     enableAxis(0, en);
441     enableAxis(2, en);
442 }
443