/* -*- 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 { 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 */