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