summaryrefslogtreecommitdiff
path: root/gr-qtgui/lib/edit_box_msg_impl.cc
diff options
context:
space:
mode:
authorTom Rondeau <tom@trondeau.com>2016-04-26 14:32:19 -0400
committerTom Rondeau <tom@trondeau.com>2016-04-26 17:51:07 -0400
commit46e49782d92fa759fe22d9092bedd6052c739a8e (patch)
treee7125af34ab519e7014a934018aa25929c6a7839 /gr-qtgui/lib/edit_box_msg_impl.cc
parent9ba484aad02aa0d55c473c7507bc333f7f200c27 (diff)
qtgui: Adds a QTGUI edit box block that emits a message when changed.
Unlike the current QT GUI Entry Widget, this is an actual GNU Radio block with an output message port 'msg'. When the editing is finished (enter or change in scope), it triggers the editing_finished method that publishes the text in the box as a message. Also allows setting of the value through an input message port. Adds example test_qtgui_msg.grc to explore this.
Diffstat (limited to 'gr-qtgui/lib/edit_box_msg_impl.cc')
-rw-r--r--gr-qtgui/lib/edit_box_msg_impl.cc571
1 files changed, 571 insertions, 0 deletions
diff --git a/gr-qtgui/lib/edit_box_msg_impl.cc b/gr-qtgui/lib/edit_box_msg_impl.cc
new file mode 100644
index 0000000000..e0c5f64b3d
--- /dev/null
+++ b/gr-qtgui/lib/edit_box_msg_impl.cc
@@ -0,0 +1,571 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2016 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "edit_box_msg_impl.h"
+#include <gnuradio/io_signature.h>
+#include <gnuradio/prefs.h>
+#include <gnuradio/qtgui/utils.h>
+#include <boost/lexical_cast.hpp>
+
+namespace gr {
+ namespace qtgui {
+
+ edit_box_msg::sptr
+ edit_box_msg::make(data_type_t type, const std::string &label,
+ const std::string &value,
+ bool is_pair, bool is_static,
+ const std::string &key, QWidget* parent)
+ {
+ return gnuradio::get_initial_sptr
+ (new edit_box_msg_impl(type, value, label, is_pair,
+ is_static, key, parent));
+ }
+
+ edit_box_msg_impl::edit_box_msg_impl(data_type_t type, const std::string &label,
+ const std::string &value,
+ bool is_pair, bool is_static,
+ const std::string &key, QWidget* parent)
+ : block("edit_box_msg",
+ io_signature::make(0, 0, 0),
+ io_signature::make(0, 0, 0)),
+ QObject(parent)
+ {
+ // Required now for Qt; argc must be greater than 0 and argv
+ // must have at least one valid character. Must be valid through
+ // life of the qApplication:
+ // http://harmattan-dev.nokia.com/docs/library/html/qt4/qapplication.html
+ d_argc = 1;
+ d_argv = new char;
+ d_argv[0] = '\0';
+
+ if(qApp != NULL) {
+ d_qApplication = qApp;
+ }
+ else {
+#if QT_VERSION >= 0x040500
+ std::string style = prefs::singleton()->get_string("qtgui", "style", "raster");
+ QApplication::setGraphicsSystem(QString(style.c_str()));
+#endif
+ d_qApplication = new QApplication(d_argc, &d_argv);
+ }
+
+ // If a style sheet is set in the prefs file, enable it here.
+ std::string qssfile = prefs::singleton()->get_string("qtgui","qss","");
+ if(qssfile.size() > 0) {
+ QString sstext = get_qt_style_sheet(QString(qssfile.c_str()));
+ d_qApplication->setStyleSheet(sstext);
+ }
+
+ d_is_pair = is_pair;
+ d_is_static = is_static;
+
+ d_val = new QLineEdit();
+ d_val->setObjectName("qtgui_editboxmsg_val"); // used to set background color
+ d_val->setText(QString(value.c_str()));
+
+ set_type(type);
+
+ d_group = new QGroupBox();
+ d_vlayout = new QVBoxLayout(parent);
+ d_hlayout = new QHBoxLayout(parent);
+
+ if(d_is_pair) {
+ d_key = new QLineEdit();
+
+ QString key_text = QString(key.c_str());
+ d_key->setText(key_text);
+
+ // If static, we create the d_key object, which we use later
+ // to be consistent about getting the key string. But we do
+ // not add it to the layout.
+ if(d_is_static) {
+ d_key->setEnabled(false);
+
+ QFontMetrics fm = d_key->fontMetrics();
+ int width = 15 + fm.width(key_text);
+
+ d_key->setFixedWidth(width);
+
+ // Verify that a default key has been set or emit an error
+ if(key.size() == 0) {
+ throw std::runtime_error("When using static + pair mode, please set a default key.");
+ }
+ }
+ else {
+ // Adding it to the layout if in non-static mode so users
+ // can see and update the key.
+ d_hlayout->addWidget(d_key);
+ }
+ }
+
+ d_label = NULL;
+ if(label != "") {
+ d_label = new QLabel(QString(label.c_str()));
+ d_vlayout->addWidget(d_label);
+ }
+
+ d_hlayout->addWidget(d_val);
+
+ if(!d_is_static) {
+ // If not static, we can change the key and the data type of
+ // the value box.
+ d_type_box = new QComboBox();
+ d_type_box->setEditable(false);
+
+ // Items listed in order of enum data_type_t
+ d_type_box->addItem("Int");
+ d_type_box->addItem("Float");
+ d_type_box->addItem("Double");
+ d_type_box->addItem("Complex");
+ d_type_box->addItem("String");
+ d_type_box->addItem("Int (vec)");
+ d_type_box->addItem("Float (vec)");
+ d_type_box->addItem("Double (vec)");
+ d_type_box->addItem("Complex (vec)");
+ d_type_box->setCurrentIndex(d_type);
+ d_hlayout->addWidget(d_type_box);
+
+ QObject::connect(d_type_box, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(set_type(int)));
+ }
+
+ d_vlayout->addItem(d_hlayout);
+ d_group->setLayout(d_vlayout);
+
+ QObject::connect(d_val, SIGNAL(editingFinished()),
+ this, SLOT(edit_finished()));
+
+ d_msg = pmt::PMT_NIL;
+
+ message_port_register_out(pmt::mp("msg"));
+ message_port_register_in(pmt::mp("val"));
+
+ set_msg_handler(pmt::mp("val"),
+ boost::bind(&edit_box_msg_impl::set_value, this, _1));
+ }
+
+ edit_box_msg_impl::~edit_box_msg_impl()
+ {
+ delete d_argv;
+ delete d_group;
+ delete d_hlayout;
+ delete d_vlayout;
+ delete d_val;
+ if(d_is_pair)
+ delete d_key;
+ if(d_label)
+ delete d_label;
+ }
+
+ bool
+ edit_box_msg_impl::start()
+ {
+ QString text = d_val->text();
+ if(!text.isEmpty()) {
+ edit_finished();
+ }
+
+ return block::start();
+ }
+
+ void
+ edit_box_msg_impl::exec_()
+ {
+ d_qApplication->exec();
+ }
+
+ QWidget*
+ edit_box_msg_impl::qwidget()
+ {
+ return (QWidget*)d_group;
+ }
+
+#ifdef ENABLE_PYTHON
+ PyObject*
+ edit_box_msg_impl::pyqwidget()
+ {
+ PyObject *w = PyLong_FromVoidPtr((void*)d_group);
+ PyObject *retarg = Py_BuildValue("N", w);
+ return retarg;
+ }
+#else
+ void *
+ edit_box_msg_impl::pyqwidget()
+ {
+ return NULL;
+ }
+#endif
+
+ void
+ edit_box_msg_impl::set_type(int type)
+ {
+ set_type(static_cast<data_type_t>(type));
+ }
+
+ void
+ edit_box_msg_impl::set_type(gr::qtgui::data_type_t type)
+ {
+ d_type = type;
+
+ switch(d_type) {
+ case INT:
+ case INT_VEC:
+ d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #4CAF50;}");
+ break;
+ case FLOAT:
+ case FLOAT_VEC:
+ d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #F57C00;}");
+ break;
+ case DOUBLE:
+ case DOUBLE_VEC:
+ d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #00BCD4;}");
+ break;
+ case COMPLEX:
+ case COMPLEX_VEC:
+ d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #2196F3;}");
+ break;
+ case STRING:
+ d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #FFFFFF; color: #000000;}");
+ break;
+ }
+ }
+
+ void
+ edit_box_msg_impl::set_value(pmt::pmt_t val)
+ {
+ // If the contents of the new value are the same as we already
+ // had, don't update anything, just exit and move on.
+ if(pmt::eqv(val, d_msg)) {
+ return;
+ }
+
+ int xi;
+ float xf;
+ double xd;
+ std::string xs;
+ gr_complex xc;
+
+ d_msg = val;
+
+ // Only update key if we're expecting a pair
+ if(d_is_pair) {
+ // If we are, make sure that the PMT is actually a pair
+ if(pmt::is_pair(val)) {
+ pmt::pmt_t key = pmt::car(val);
+ std::string skey = pmt::symbol_to_string(key);
+
+ // If static, check to make sure that the key of the
+ // incoming message matches our key. If it doesn't, emit a
+ // warning and exit without changing anything.
+ if(d_is_static) {
+ std::string cur_key = d_key->text().toStdString();
+ if(skey != cur_key) {
+ GR_LOG_WARN(d_logger, boost::format("Got key '%1%' but expected '%2%'") \
+ % skey % cur_key);
+ return;
+ }
+ }
+ val = pmt::cdr(val);
+ d_key->setText(QString(skey.c_str()));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Did not find PMT pair");
+ return;
+ }
+ }
+
+ switch(d_type) {
+ case INT:
+ if(pmt::is_integer(val)) {
+ xi = pmt::to_long(val);
+ d_val->setText(QString::number(xi));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion from integer failed");
+ return;
+ }
+ break;
+ case INT_VEC:
+ if(pmt::is_s32vector(val)) {
+ QStringList text_list;
+ const std::vector<int32_t> xv = pmt::s32vector_elements(val);
+ for(size_t i = 0; i < xv.size(); i++) {
+ text_list.append(QString::number(xv[i]));
+ }
+ d_val->setText(text_list.join(", "));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion from integer vector failed");
+ return;
+ }
+ break;
+ case FLOAT:
+ if(pmt::is_real(val)) {
+ xf = pmt::to_float(val);
+ d_val->setText(QString::number(xf));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion from float failed");
+ return;
+ }
+ break;
+ case FLOAT_VEC:
+ if(pmt::is_f32vector(val)) {
+ QStringList text_list;
+ const std::vector<float> xv = pmt::f32vector_elements(val);
+ for(size_t i = 0; i < xv.size(); i++) {
+ text_list.append(QString::number(xv[i]));
+ }
+ d_val->setText(text_list.join(", "));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion from float vector failed");
+ return;
+ }
+ break;
+ case DOUBLE:
+ if(pmt::is_real(val)) {
+ xd = pmt::to_double(val);
+ d_val->setText(QString::number(xd));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion from double failed");
+ return;
+ }
+ break;
+ case DOUBLE_VEC:
+ if(pmt::is_f64vector(val)) {
+ QStringList text_list;
+ const std::vector<double> xv = pmt::f64vector_elements(val);
+ for(size_t i = 0; i < xv.size(); i++) {
+ text_list.append(QString::number(xv[i]));
+ }
+ d_val->setText(text_list.join(", "));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion from double vector failed");
+ return;
+ }
+ break;
+ case COMPLEX:
+ if(pmt::is_complex(val)) {
+ xc = pmt::to_complex(val);
+ d_val->setText(QString("(%1,%2)").arg(xc.real()).arg(xc.imag()));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion from complex failed");
+ return;
+ }
+ break;
+ case COMPLEX_VEC:
+ if(pmt::is_c32vector(val)) {
+ QStringList text_list;
+ const std::vector<gr_complex> xv = pmt::c32vector_elements(val);
+ for(size_t i = 0; i < xv.size(); i++) {
+ text_list.append(QString("(%1,%2)").arg(xv[i].real()).arg(xv[i].imag()));
+ }
+ d_val->setText(text_list.join(", "));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion from complex vector failed");
+ return;
+ }
+ break;
+ case STRING:
+ if(pmt::is_symbol(val)) {
+ xs = pmt::symbol_to_string(val);
+ d_val->setText(QString(xs.c_str()));
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion from string failed");
+ return;
+ }
+ break;
+ }
+
+ // Emit the new message to pass updates downstream.
+ // Loops are prevented by the early exit if d_msg == val.
+ message_port_pub(pmt::mp("msg"), d_msg);
+ }
+
+ void
+ edit_box_msg_impl::edit_finished()
+ {
+ QString text = d_val->text();
+ bool conv_ok = true;
+ int xi;
+ float xf;
+ double xd;
+ std::string xs;
+ gr_complex xc;
+
+ switch(d_type) {
+ case INT:
+ xi = text.toInt(&conv_ok);
+ if(conv_ok) {
+ d_msg = pmt::from_long(xi);
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion to integer failed");
+ return;
+ }
+ break;
+ case INT_VEC:
+ {
+ std::vector<int32_t> xv;
+ QStringList text_list = text.split(",");
+ for(int i = 0; i < text_list.size(); ++i) {
+ QString s = text_list.at(i);
+ s = s.remove(QChar(' '));
+ int t = s.toInt(&conv_ok);
+ if(conv_ok) {
+ xv.push_back(t);
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion to integer vector failed");
+ return;
+ }
+ }
+ d_msg = pmt::init_s32vector(xv.size(), xv);
+ }
+ break;
+ case FLOAT:
+ xf = text.toFloat(&conv_ok);
+ if(conv_ok) {
+ d_msg = pmt::from_float(xf);
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion to float failed");
+ return;
+ }
+ break;
+ case FLOAT_VEC:
+ {
+ std::vector<float> xv;
+ QStringList text_list = text.split(",");
+ for(int i = 0; i < text_list.size(); ++i) {
+ QString s = text_list.at(i);
+ s = s.remove(QChar(' '));
+ float t = s.toFloat(&conv_ok);
+ if(conv_ok) {
+ xv.push_back(t);
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion to float vector failed");
+ return;
+ }
+ }
+ d_msg = pmt::init_f32vector(xv.size(), xv);
+ }
+ break;
+ case DOUBLE:
+ xd = text.toDouble(&conv_ok);
+ if(conv_ok) {
+ d_msg = pmt::from_double(xd);
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion to double failed");
+ return;
+ }
+ break;
+ case DOUBLE_VEC:
+ {
+ std::vector<double> xv;
+ QStringList text_list = text.split(",");
+ for(int i = 0; i < text_list.size(); ++i) {
+ QString s = text_list.at(i);
+ s = s.remove(QChar(' '));
+ double t = s.toDouble(&conv_ok);
+ if(conv_ok) {
+ xv.push_back(t);
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion to double vector failed");
+ return;
+ }
+ }
+ d_msg = pmt::init_f64vector(xv.size(), xv);
+ }
+ break;
+ case COMPLEX:
+ try {
+ xc = boost::lexical_cast<gr_complex>(text.toStdString());
+ }
+ catch(boost::bad_lexical_cast const & e) {
+ GR_LOG_WARN(d_logger, boost::format("Conversion to complex failed (%1%)") \
+ % e.what());
+ return;
+ }
+ d_msg = pmt::from_complex(xc.real(), xc.imag());
+ break;
+ case COMPLEX_VEC:
+ {
+ std::vector<gr_complex> xv;
+ QStringList text_list = text.split(",");
+ bool even = false;
+ gr_complex c;
+ float re, im;
+ for(int i = 0; i < text_list.size(); ++i) {
+ QString s = text_list.at(i);
+ s = s.remove(QChar(' '));
+ s = s.remove(QChar(')'));
+ s = s.remove(QChar('('));
+ float t = s.toFloat(&conv_ok);
+ if(conv_ok) {
+ if(even) {
+ im = t;
+ xv.push_back(gr_complex(re, im));
+ even = false;
+ }
+ else {
+ re = t;
+ even = true;
+ }
+ }
+ else {
+ GR_LOG_WARN(d_logger, "Conversion to complex vector failed");
+ return;
+ }
+ }
+ d_msg = pmt::init_c32vector(xv.size(), xv);
+ }
+ break;
+ case STRING:
+ xs = text.toStdString();
+ d_msg = pmt::intern(xs);
+ break;
+ }
+
+ if(d_is_pair) {
+ std::string key = d_key->text().toStdString();
+ d_msg = pmt::cons(pmt::intern(key), d_msg);
+ }
+
+ message_port_pub(pmt::mp("msg"), d_msg);
+ }
+
+ } /* namespace qtgui */
+} /* namespace gr */