1 /* -*- c++ -*- */
2 /*
3  * Copyright 2014 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/numberdisplayform.h>
24 
25 #include <qwt_color_map.h>
26 #include <QFileDialog>
27 #include <QMessageBox>
28 
29 #include <cmath>
30 #include <iostream>
31 
NumberDisplayForm(int nplots,gr::qtgui::graph_t type,QWidget * parent)32 NumberDisplayForm::NumberDisplayForm(int nplots, gr::qtgui::graph_t type, QWidget* parent)
33     : QWidget(parent)
34 {
35     d_nplots = nplots;
36     d_graph_type = type;
37     d_title = new QLabel(QString(""));
38     d_layout = new QGridLayout(this);
39     for (unsigned int i = 0; i < d_nplots; ++i) {
40         d_min.push_back(+1e32);
41         d_max.push_back(-1e32);
42         d_label.push_back(new QLabel(QString("Data %1").arg(i)));
43         d_unit.emplace_back("");
44         d_factor.push_back(1);
45         d_text_box.push_back(new QLabel(QString("0")));
46 
47         d_indicator.push_back(new QwtThermo());
48         d_indicator[i]->setScale(-1, 1);
49 
50 #if QWT_VERSION < 0x060100
51 #else
52         d_indicator[i]->setOriginMode(QwtThermo::OriginCustom);
53         d_indicator[i]->setOrigin(0.0);
54 #endif /* if QWT_VERSION < 0x060100 */
55 
56         switch (type) {
57         case (gr::qtgui::NUM_GRAPH_HORIZ):
58 #if QWT_VERSION < 0x060100
59             d_indicator[i]->setOrientation(Qt::Horizontal, QwtThermo::BottomScale);
60 #else
61             d_indicator[i]->setOrientation(Qt::Horizontal);
62 #endif /* if QWT_VERSION < 0x060100 */
63             d_layout->addWidget(d_label[i], 2 * i, 0);
64             d_layout->addWidget(d_text_box[i], 2 * i, 1);
65             d_layout->addWidget(d_indicator[i], 2 * i + 1, 1);
66             break;
67         case (gr::qtgui::NUM_GRAPH_VERT):
68 #if QWT_VERSION < 0x060100
69             d_indicator[i]->setOrientation(Qt::Vertical, QwtThermo::LeftScale);
70 #else
71             d_indicator[i]->setOrientation(Qt::Vertical);
72 #endif /* if QWT_VERSION < 0x060100 */
73             d_layout->addWidget(d_label[i], 0, i);
74             d_layout->addWidget(d_text_box[i], 1, i);
75             d_layout->addWidget(d_indicator[i], 2, i);
76             break;
77         case (gr::qtgui::NUM_GRAPH_NONE):
78         default:
79             d_layout->addWidget(d_label[i], 0, i);
80             d_layout->addWidget(d_text_box[i], 1, i);
81         }
82 
83         setColor(i, QColor("black"), QColor("black"));
84     }
85 
86     d_avg = 0.0;
87     d_update_time = 0.1;
88 
89     d_menu_on = true;
90     d_menu = new QMenu(this);
91 
92     // Create a set of actions for the menu
93     d_stop_act = new QAction("Stop", this);
94     d_stop_act->setStatusTip(tr("Start/Stop"));
95     connect(d_stop_act, SIGNAL(triggered()), this, SLOT(setStop()));
96     d_stop_state = false;
97     d_menu->addAction(d_stop_act);
98 
99     // Menu items for each number line
100     for (unsigned int i = 0; i < d_nplots; ++i) {
101         d_label_act.push_back(new LineTitleAction(i, this));
102         connect(d_label_act[i],
103                 SIGNAL(whichTrigger(unsigned int, const QString&)),
104                 this,
105                 SLOT(setLabel(unsigned int, const QString&)));
106 
107         d_label_menu.push_back(new QMenu(tr(""), this));
108         d_label_menu[i]->addAction(d_label_act[i]);
109 
110         d_color_menu.push_back(new NumberColorMapMenu(i, this));
111         connect(d_color_menu[i],
112                 SIGNAL(whichTrigger(unsigned int, const QColor&, const QColor&)),
113                 this,
114                 SLOT(setColor(unsigned int, const QColor&, const QColor&)));
115         d_label_menu[i]->addMenu(d_color_menu[i]);
116 
117         d_factor_act.push_back(new ItemFloatAct(i, "Factor", this));
118         connect(d_factor_act[i],
119                 SIGNAL(whichTrigger(unsigned int, float)),
120                 this,
121                 SLOT(setFactor(unsigned int, float)));
122         d_label_menu[i]->addAction(d_factor_act[i]);
123 
124         d_menu->addMenu(d_label_menu[i]);
125     }
126 
127     d_avg_menu = new FFTAverageMenu(this);
128     d_avg_menu->setTitle("Average");
129     d_avg_menu->setHigh(0.001);
130     d_avg_menu->setMedium(0.01);
131     d_avg_menu->setLow(0.1);
132     connect(d_avg_menu, SIGNAL(whichTrigger(float)), this, SLOT(setAverage(const float)));
133     d_menu->addMenu(d_avg_menu);
134 
135     d_layout_menu = new NumberLayoutMenu(this);
136     connect(d_layout_menu,
137             SIGNAL(whichTrigger(gr::qtgui::graph_t)),
138             this,
139             SLOT(setGraphType(const gr::qtgui::graph_t)));
140     d_menu->addMenu(d_layout_menu);
141 
142     d_autoscale_act = new QAction("Auto Scale", this);
143     d_autoscale_act->setCheckable(true);
144     connect(d_autoscale_act, SIGNAL(triggered(bool)), this, SLOT(autoScale(bool)));
145     d_autoscale_state = false;
146     d_menu->addAction(d_autoscale_act);
147 
148     d_update_time_menu = new PopupMenu("Update Time", this);
149     connect(d_update_time_menu,
150             SIGNAL(whichTrigger(QString)),
151             this,
152             SLOT(setUpdateTime(QString)));
153     d_menu->addAction(d_update_time_menu);
154 
155     d_save_act = new QAction("Save", this);
156     d_save_act->setStatusTip(tr("Save Figure"));
157     connect(d_save_act, SIGNAL(triggered()), this, SLOT(saveFigure()));
158     d_menu->addAction(d_save_act);
159 
160     setLayout(d_layout);
161 }
162 
~NumberDisplayForm()163 NumberDisplayForm::~NumberDisplayForm()
164 {
165     // Qt deletes children when parent is deleted
166 }
167 
mousePressEvent(QMouseEvent * e)168 void NumberDisplayForm::mousePressEvent(QMouseEvent* e)
169 {
170     bool ctrloff = Qt::ControlModifier != QApplication::keyboardModifiers();
171     if ((e->button() == Qt::MidButton) && ctrloff && (d_menu_on)) {
172         if (d_stop_state == false)
173             d_stop_act->setText(tr("Stop"));
174         else
175             d_stop_act->setText(tr("Start"));
176 
177         // Update the line titles if changed externally
178         for (unsigned int i = 0; i < d_nplots; ++i) {
179             d_label_menu[i]->setTitle(label(i).c_str());
180         }
181         d_menu->exec(e->globalPos());
182     }
183 }
184 
setStop(bool on)185 void NumberDisplayForm::setStop(bool on)
186 {
187     if (!on) {
188         d_stop_state = false;
189     } else {
190         d_stop_state = true;
191     }
192 }
193 
setStop()194 void NumberDisplayForm::setStop()
195 {
196     if (d_stop_state == false)
197         setStop(true);
198     else
199         setStop(false);
200 }
201 
saveFigure()202 void NumberDisplayForm::saveFigure()
203 {
204     QPixmap qpix = QPixmap::grabWidget(this);
205 
206     QString types = QString(tr("JPEG file (*.jpg);;Portable Network Graphics file "
207                                "(*.png);;Bitmap file (*.bmp);;TIFF file (*.tiff)"));
208 
209     QString filename, filetype;
210     QFileDialog* filebox = new QFileDialog(0, "Save Image", "./", types);
211     filebox->setViewMode(QFileDialog::Detail);
212     if (filebox->exec()) {
213         filename = filebox->selectedFiles()[0];
214         filetype = filebox->selectedNameFilter();
215     } else {
216         return;
217     }
218 
219     if (filetype.contains(".jpg")) {
220         qpix.save(filename, "JPEG");
221     } else if (filetype.contains(".png")) {
222         qpix.save(filename, "PNG");
223     } else if (filetype.contains(".bmp")) {
224         qpix.save(filename, "BMP");
225     } else if (filetype.contains(".tiff")) {
226         qpix.save(filename, "TIFF");
227     } else {
228         qpix.save(filename, "JPEG");
229     }
230 
231     delete filebox;
232 }
233 
newData(const QEvent * updateEvent)234 void NumberDisplayForm::newData(const QEvent* updateEvent)
235 {
236     if (!d_stop_state) {
237         NumberUpdateEvent* tevent = (NumberUpdateEvent*)updateEvent;
238         const std::vector<float> samples = tevent->getSamples();
239 
240         for (unsigned int i = 0; i < d_nplots; ++i) {
241             float f = d_factor[i] * samples[i];
242             d_text_box[i]->setText(
243                 QString("%1 %2").arg(f, 4, ' ').arg(QString(d_unit[i].c_str())));
244             d_indicator[i]->setValue(f);
245             d_min[i] = std::min(d_min[i], f);
246             d_max[i] = std::max(d_max[i], f);
247 
248             if (d_autoscale_state) {
249                 d_indicator[i]->setScale(d_min[i], d_max[i]);
250             }
251         }
252     }
253 }
254 
customEvent(QEvent * e)255 void NumberDisplayForm::customEvent(QEvent* e)
256 {
257     if (e->type() == NumberUpdateEvent::Type()) {
258         newData(e);
259     }
260 }
261 
setGraphType(const gr::qtgui::graph_t type)262 void NumberDisplayForm::setGraphType(const gr::qtgui::graph_t type)
263 {
264     int off = 0;
265 
266     // Remove all widgets from the layout
267     QLayoutItem* item;
268     while ((item = d_layout->takeAt(0)) != NULL) {
269         d_layout->removeItem(item);
270     }
271 
272     // If we have a title, add it at the 0,0 grid point (top left)
273     // set off = 1 to offset the rest of the widgets
274     if (d_title->text().length() > 0) {
275         d_layout->addWidget(d_title, 0, 0);
276         off = 1;
277     }
278 
279     d_graph_type = type;
280     for (unsigned int i = 0; i < d_nplots; ++i) {
281         switch (d_graph_type) {
282         case (gr::qtgui::NUM_GRAPH_HORIZ):
283 #if QWT_VERSION < 0x060100
284             d_indicator[i]->setOrientation(Qt::Horizontal, QwtThermo::BottomScale);
285 #else
286             d_indicator[i]->setOrientation(Qt::Horizontal);
287 #endif /* if QWT_VERSION < 0x060100 */
288             d_indicator[i]->setVisible(true);
289             d_layout->addWidget(d_label[i], 2 * i + off, 0);
290             d_layout->addWidget(d_text_box[i], 2 * i + off, 1);
291             d_layout->addWidget(d_indicator[i], 2 * i + 1 + off, 1);
292             break;
293         case (gr::qtgui::NUM_GRAPH_VERT):
294 #if QWT_VERSION < 0x060100
295             d_indicator[i]->setOrientation(Qt::Vertical, QwtThermo::LeftScale);
296 #else
297             d_indicator[i]->setOrientation(Qt::Vertical);
298 #endif /* if QWT_VERSION < 0x060100 */
299             d_indicator[i]->setVisible(true);
300             d_layout->addWidget(d_label[i], 0 + off, i);
301             d_layout->addWidget(d_text_box[i], 1 + off, i);
302             d_layout->addWidget(d_indicator[i], 2 + off, i);
303             break;
304         case (gr::qtgui::NUM_GRAPH_NONE):
305         default:
306             d_indicator[i]->setVisible(false);
307             d_layout->addWidget(d_label[i], 0 + off, i);
308             d_layout->addWidget(d_text_box[i], 1 + off, i);
309             break;
310         }
311     }
312 }
313 
setColor(unsigned int which,const QColor & min,const QColor & max)314 void NumberDisplayForm::setColor(unsigned int which, const QColor& min, const QColor& max)
315 {
316     QwtLinearColorMap* map = new QwtLinearColorMap();
317     map->setColorInterval(min, max);
318 
319 #if QWT_VERSION < 0x060000
320     d_indicator[which]->setFillColor(max);
321 #else
322     d_indicator[which]->setColorMap(map);
323 #endif /* QWT_VERSION < 0x060000 */
324 }
325 
setColorMin(unsigned int which,QString min)326 void NumberDisplayForm::setColorMin(unsigned int which, QString min)
327 {
328     setColor(which, QColor(min), colorMax(which));
329 }
330 
setColorMax(unsigned int which,QString max)331 void NumberDisplayForm::setColorMax(unsigned int which, QString max)
332 {
333     setColor(which, colorMin(which), QColor(max));
334 }
335 
setLabel(unsigned int which,const std::string & label)336 void NumberDisplayForm::setLabel(unsigned int which, const std::string& label)
337 {
338     d_label[which]->setText(label.c_str());
339 }
340 
setLabel(unsigned int which,QString label)341 void NumberDisplayForm::setLabel(unsigned int which, QString label)
342 {
343     d_label[which]->setText(label);
344 }
345 
setAverage(const float avg)346 void NumberDisplayForm::setAverage(const float avg) { d_avg = avg; }
347 
setUpdateTime(const float time)348 void NumberDisplayForm::setUpdateTime(const float time) { d_update_time = time; }
349 
setUpdateTime(QString time)350 void NumberDisplayForm::setUpdateTime(QString time) { setUpdateTime(time.toFloat()); }
351 
setScale(unsigned int which,int min,int max)352 void NumberDisplayForm::setScale(unsigned int which, int min, int max)
353 {
354     d_min[which] = min;
355     d_max[which] = max;
356     d_indicator[which]->setScale(min, max);
357 #if QWT_VERSION < 0x060100
358     d_indicator[which]->setRange(min, max);
359 #endif
360 }
361 
setScaleMin(unsigned int which,int min)362 void NumberDisplayForm::setScaleMin(unsigned int which, int min)
363 {
364     setScale(which, min, d_max[which]);
365 }
366 
setScaleMax(unsigned int which,int max)367 void NumberDisplayForm::setScaleMax(unsigned int which, int max)
368 {
369     setScale(which, d_min[which], max);
370 }
371 
graphType() const372 gr::qtgui::graph_t NumberDisplayForm::graphType() const { return d_graph_type; }
373 
colorMin(unsigned int which) const374 QColor NumberDisplayForm::colorMin(unsigned int which) const
375 {
376 #if QWT_VERSION < 0x060000
377     return d_indicator[which]->fillColor();
378 #else
379     QwtLinearColorMap* map =
380         static_cast<QwtLinearColorMap*>(d_indicator[which]->colorMap());
381     return map->color1();
382 #endif /* QWT_VERSION < 0x060000 */
383 }
384 
colorMax(unsigned int which) const385 QColor NumberDisplayForm::colorMax(unsigned int which) const
386 {
387 #if QWT_VERSION < 0x060000
388     return d_indicator[which]->fillColor();
389 #else
390     QwtLinearColorMap* map =
391         static_cast<QwtLinearColorMap*>(d_indicator[which]->colorMap());
392     return map->color2();
393 #endif /* QWT_VERSION < 0x060000 */
394 }
395 
label(unsigned int which) const396 std::string NumberDisplayForm::label(unsigned int which) const
397 {
398     return d_label[which]->text().toStdString();
399 }
400 
average() const401 float NumberDisplayForm::average() const { return d_avg; }
402 
updateTime() const403 float NumberDisplayForm::updateTime() const { return d_update_time; }
404 
scaleMin(unsigned int which)405 int NumberDisplayForm::scaleMin(unsigned int which) { return d_min[which]; }
406 
scaleMax(unsigned int which)407 int NumberDisplayForm::scaleMax(unsigned int which) { return d_max[which]; }
408 
autoScale(bool on)409 void NumberDisplayForm::autoScale(bool on)
410 {
411     d_autoscale_state = on;
412     d_autoscale_act->setChecked(on);
413 
414     // Reset the autoscale limits
415     for (unsigned int i = 0; i < d_nplots; ++i) {
416         d_min[i] = +1e32;
417         d_max[i] = -1e32;
418     }
419 }
420 
title() const421 std::string NumberDisplayForm::title() const { return d_title->text().toStdString(); }
422 
setTitle(const std::string & title)423 void NumberDisplayForm::setTitle(const std::string& title)
424 {
425     std::string t = title;
426     if (t.length() > 0)
427         t = "<b><FONT SIZE=4>" + title + "</b>";
428     d_title->setText(QString(t.c_str()));
429     setGraphType(d_graph_type);
430 }
431 
unit(unsigned int which) const432 std::string NumberDisplayForm::unit(unsigned int which) const
433 {
434     if (static_cast<size_t>(which) >= d_unit.size())
435         throw std::runtime_error("NumberDisplayForm::units: invalid 'which'.\n");
436 
437     return d_unit[which];
438 }
439 
setUnit(unsigned int which,const std::string & unit)440 void NumberDisplayForm::setUnit(unsigned int which, const std::string& unit)
441 {
442     if (static_cast<size_t>(which) >= d_unit.size())
443         throw std::runtime_error("NumberDisplayForm::setUnits: invalid 'which'.\n");
444 
445     d_unit[which] = unit;
446 }
447 
factor(unsigned int which) const448 float NumberDisplayForm::factor(unsigned int which) const
449 {
450     if (static_cast<size_t>(which) >= d_factor.size())
451         throw std::runtime_error("NumberDisplayForm::factor: invalid 'which'.\n");
452 
453     return d_factor[which];
454 }
455 
setFactor(unsigned int which,float factor)456 void NumberDisplayForm::setFactor(unsigned int which, float factor)
457 {
458     if (static_cast<size_t>(which) >= d_factor.size())
459         throw std::runtime_error("NumberDisplayForm::setFactor: invalid 'which'.\n");
460 
461     d_factor[which] = factor;
462 }
463