RTXI  3.0.0
The Real-Time eXperiment Interface Reference Manual
widgets.cpp
Go to the documentation of this file.
1 
2 #include <QApplication>
3 #include <QCloseEvent>
4 #include <QDoubleValidator>
5 #include <QIntValidator>
6 #include <QMdiArea>
7 #include <QScrollArea>
8 #include <QTimer>
9 #include <algorithm>
10 #include <any>
11 #include <memory>
12 #include <sstream>
13 
14 #include "widgets.hpp"
15 
16 #include <dlfcn.h>
17 #include <qmdisubwindow.h>
18 
19 #include "debug.hpp"
20 #include "rtxiConfig.h"
21 
23 {
24  std::string result;
25  switch (state) {
26  case RT::State::INIT:
27  result = std::string("INIT");
28  break;
29  case RT::State::MODIFY:
30  result = std::string("MODIFIED PARAMETERS");
31  break;
32  case RT::State::PERIOD:
33  result = std::string("PERIOD CHANGE");
34  break;
35  case RT::State::PAUSE:
36  result = std::string("PLUGIN PAUSED");
37  break;
38  case RT::State::UNPAUSE:
39  result = std::string("PLUGIN UNPAUSED");
40  break;
41  case RT::State::EXIT:
42  result = std::string("EXIT");
43  break;
44  default:
45  result = std::string("UNKNOWN STATE");
46  break;
47  }
48  return result;
49 }
50 
53 {
54  std::string result;
55  switch (type) {
57  result = std::string("INTEGER");
58  break;
60  result = std::string("DOUBLE");
61  break;
63  result = std::string("UNSIGNED INTEGER");
64  break;
66  result = std::string("STATE");
67  break;
68  default:
69  result = std::string("UNKNOWN PARAMETER TYPE");
70  break;
71  }
72  return result;
73 }
74 
76  : QLineEdit(parent)
77 {
78  QObject::connect(
79  this, SIGNAL(textChanged(const QString&)), this, SLOT(redden()));
80 }
81 
83 {
84  palette.setBrush(this->foregroundRole(),
85  QApplication::palette().color(QPalette::WindowText));
86  this->setPalette(palette);
87  setModified(false);
88 }
89 
91 {
92  if (isModified()) {
93  palette.setBrush(this->foregroundRole(), Qt::red);
94  this->setPalette(palette);
95  }
96 }
97 
99  Widgets::Plugin* hplugin,
100  const std::string& mod_name,
101  const std::vector<IO::channel_t>& channels,
102  const std::vector<Widgets::Variable::Info>& variables)
103  : RT::Thread(mod_name, channels)
104  , hostPlugin(hplugin)
105 {
106  for (const auto& var : variables) {
107  if (var.id != parameters.size()) {
108  ERROR_MSG("Error parsing variables in module \"{}\" while loading",
109  this->getName());
110  ERROR_MSG("Variable {} has id {} but was inserted in position {}",
111  var.name,
112  var.id,
113  parameters.size());
114  return;
115  }
116  this->parameters.push_back(var);
117  }
118 }
119 
120 std::string Widgets::Component::getDescription(const size_t& var_id)
121 {
122  return this->parameters[var_id].description;
123 }
124 
125 std::string Widgets::Component::getValueString(const size_t& var_id)
126 {
127  std::string value;
128  switch (this->parameters[var_id].vartype) {
130  value =
131  std::to_string(std::get<uint64_t>(this->parameters[var_id].value));
132  break;
134  value = std::to_string(std::get<int64_t>(this->parameters[var_id].value));
135  break;
137  value = std::to_string(std::get<double>(this->parameters[var_id].value));
138  break;
140  value = "";
141  break;
143  value = std::get<std::string>(this->parameters[var_id].value);
144  break;
146  value = "UNKNOWN";
147  break;
148  default:
149  value = "ERROR";
150  break;
151  }
152  return value;
153 }
154 
155 Widgets::Panel::Panel(const std::string& mod_name,
156  QMainWindow* mw,
157  Event::Manager* ev_manager)
158  : QWidget(mw)
159  , main_window(mw)
160  , m_name(mod_name)
161  , event_manager(ev_manager)
162 {
163  setWindowTitle(QString(mod_name.c_str()));
164 
165  auto* central_widget = dynamic_cast<QMdiArea*>(mw->centralWidget());
166  this->m_subwindow = central_widget->addSubWindow(this);
167  this->m_subwindow->setWindowIcon(
168  QIcon("/usr/share/rtxi/RTXI-widget-icon.png"));
169  this->setAttribute(Qt::WA_DeleteOnClose);
170  this->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint
171  | Qt::WindowMinimizeButtonHint);
172  qRegisterMetaType<RT::State::state_t>();
173  QObject::connect(this,
175  this,
177 }
178 
179 void Widgets::Panel::closeEvent(QCloseEvent* event)
180 {
181  event->accept();
182  this->exit();
183 }
184 
186  const std::vector<Widgets::Variable::Info>& vars,
187  const std::vector<Widgets::Variable::Id>& skip_ids)
188 {
189  // Create main layout
190  auto* main_layout = new QVBoxLayout;
191 
192  // Create child widget and it's layout
193  auto* customParamArea = new QGroupBox;
194  auto* customParamLayout = new QGridLayout;
195  int param_count = 0;
196  for (const auto& varinfo : vars) {
197  // Skip any unwanted ids
198  if (std::count(skip_ids.begin(), skip_ids.end(), varinfo.id) != 0) {
199  continue;
200  }
201  param_t param;
202  param.label = new QLabel(QString(varinfo.name.c_str()), customParamArea);
203  param.edit = new DefaultGUILineEdit(customParamArea);
204  param.str_value = QString();
205  param.type = varinfo.vartype;
206  param.info = varinfo;
207  switch (varinfo.vartype) {
209  param.edit->setValidator(new QDoubleValidator(param.edit));
210  param.edit->setText(QString::number(std::get<double>(varinfo.value)));
211  break;
213  param.edit->setValidator(new QIntValidator(param.edit));
214  param.edit->setText(QString::number(std::get<uint64_t>(varinfo.value)));
215  break;
217  param.edit->setValidator(new QIntValidator(param.edit));
218  param.edit->setText(QString::number(std::get<int64_t>(varinfo.value)));
219  break;
221  param.edit->setReadOnly(true);
222  palette.setBrush(param.edit->foregroundRole(), Qt::darkGray);
223  param.edit->setPalette(palette);
224  break;
226  break;
228  ERROR_MSG("Variable {} in Widget {} is of category UNKNOWN",
229  varinfo.name,
230  this->getName());
231  break;
232  default:
233  ERROR_MSG("Variable {} in Widget {} has undefined or broken category",
234  varinfo.name,
235  this->getName());
236  }
237  param.label->setToolTip(QString(varinfo.description.c_str()));
238  param.edit->setToolTip(QString(varinfo.description.c_str()));
239  param.str_value = param.edit->text();
240  parameter[varinfo.name] = param;
241  customParamLayout->addWidget(param.label, param_count, 0);
242  customParamLayout->addWidget(param.edit, param_count, 1);
243  param_count++;
244  }
245 
246  customParamArea->setLayout(customParamLayout);
247  // Create child widget
248  auto* buttonGroup = new QGroupBox;
249  auto* buttonLayout = new QHBoxLayout;
250 
251  // Create elements
252  pauseButton = new QPushButton("Pause", this);
253  pauseButton->setCheckable(true);
254  QObject::connect(pauseButton, SIGNAL(toggled(bool)), this, SLOT(pause(bool)));
255  buttonLayout->addWidget(pauseButton);
256 
257  modifyButton = new QPushButton("Modify", this);
258  QObject::connect(modifyButton, SIGNAL(clicked()), this, SLOT(modify()));
259  buttonLayout->addWidget(modifyButton);
260 
261  unloadButton = new QPushButton("Unload", this);
262  QObject::connect(
263  unloadButton, SIGNAL(clicked()), parentWidget(), SLOT(close()));
264  buttonLayout->addWidget(unloadButton);
265 
266  buttonGroup->setLayout(buttonLayout);
267 
268  main_layout->addWidget(customParamArea, 0);
269 
270  main_layout->addWidget(buttonGroup, 1);
271 
272  this->setLayout(main_layout);
273 }
274 
276 {
277  Widgets::Plugin* hplugin = this->getHostPlugin();
278  hplugin->setComponentState(flag);
279 }
280 
282 {
283  m_subwindow->adjustSize();
284 }
285 
287 {
288  this->event_manager->unregisterHandler(this->hostPlugin);
290  event.setParam("pluginPointer",
291  std::any(static_cast<Widgets::Plugin*>(this->hostPlugin)));
292  this->event_manager->postEvent(&event);
293  // this->m_subwindow->close();
294 }
295 
297 {
299  double double_value = 0.0;
300  int64_t int_value = 0;
301  uint64_t uint_value = 0ULL;
302  std::stringstream sstream;
303  for (auto& i : this->parameter) {
304  switch (i.second.type) {
306  i.second.edit->setText(i.second.str_value);
307  palette.setBrush(i.second.edit->foregroundRole(), Qt::darkGray);
308  i.second.edit->setPalette(palette);
309  break;
311  param_id = static_cast<Widgets::Variable::Id>(i.second.info.id);
312  uint_value = this->hostPlugin->getComponentUIntParameter(param_id);
313  sstream << uint_value;
314  i.second.edit->setText(QString(sstream.str().c_str()));
315  break;
317  param_id = static_cast<Widgets::Variable::Id>(i.second.info.id);
318  int_value = this->hostPlugin->getComponentIntParameter(param_id);
319  sstream << int_value;
320  i.second.edit->setText(QString(sstream.str().c_str()));
321  break;
323  param_id = static_cast<Widgets::Variable::Id>(i.second.info.id);
324  double_value = this->hostPlugin->getComponentDoubleParameter(param_id);
325  sstream << double_value;
326  i.second.edit->setText(QString(sstream.str().c_str()));
327  break;
328  default:
329  ERROR_MSG("Unable to determine refresh type for component {}",
330  this->getName());
331  }
332  }
333 
334  // Make sure we actually have a pauseButton object (default constructed)
335  if (this->pauseButton != nullptr) {
336  pauseButton->setChecked(!(this->hostPlugin->getActive()));
337  }
338 }
339 
341 {
343  double double_value = 0.0;
344  int int_value = 0;
345  uint64_t uint_value = 0ULL;
346  std::stringstream sstream;
347  this->update_state(RT::State::PAUSE);
348  for (auto& var : this->parameter) {
349  if (!var.second.edit->isModified()) {
350  continue;
351  }
352  switch (var.second.type) {
354  param_id = static_cast<Widgets::Variable::Id>(var.second.info.id);
355  uint_value = var.second.edit->text().toUInt();
356  this->hostPlugin->setComponentParameter<uint64_t>(param_id, uint_value);
357  sstream << uint_value;
358  var.second.edit->setText(QString(sstream.str().c_str()));
359  var.second.edit->blacken();
360  break;
362  param_id = static_cast<Widgets::Variable::Id>(var.second.info.id);
363  int_value = var.second.edit->text().toInt();
364  this->hostPlugin->setComponentParameter<int>(param_id, int_value);
365  sstream << int_value;
366  var.second.edit->setText(QString(sstream.str().c_str()));
367  var.second.edit->blacken();
368  break;
370  param_id = static_cast<Widgets::Variable::Id>(var.second.info.id);
371  double_value = var.second.edit->text().toDouble();
372  this->hostPlugin->setComponentParameter(param_id, double_value);
373  sstream << double_value;
374  var.second.edit->setText(QString(sstream.str().c_str()));
375  var.second.edit->blacken();
376  break;
377  default:
378  ERROR_MSG("Unable to determine refresh type for component {}",
379  this->getName());
380  }
381  }
382  this->update_state(RT::State::MODIFY);
383 }
384 
385 // NOLINTNEXTLINE
386 void Widgets::Panel::setComment(const QString& var_name, const QString& comment)
387 {
388  auto n = parameter.find(var_name.toStdString());
389  if (n != parameter.end() && (n->second.type == Widgets::Variable::COMMENT)) {
390  n->second.edit->setText(comment);
391  const QByteArray textData = comment.toLatin1();
392  const char* text = textData.constData();
393  auto param_id = static_cast<Widgets::Variable::Id>(n->second.info.id);
394  this->hostPlugin->setComponentParameter<std::string>(param_id, text);
395  }
396 }
397 
398 void Widgets::Panel::setParameter(const QString& var_name, double value)
399 {
400  auto n = parameter.find(var_name.toStdString());
401  if ((n != parameter.end())
402  && (n->second.type == Widgets::Variable::DOUBLE_PARAMETER))
403  {
404  n->second.edit->setText(QString::number(value));
405  n->second.str_value = n->second.edit->text();
406  auto param_id = static_cast<Widgets::Variable::Id>(n->second.info.id);
407  this->hostPlugin->setComponentParameter<double>(param_id, value);
408  // setValue(n->second.index, n->second.edit->text().toDouble());
409  }
410 }
411 
412 void Widgets::Panel::setParameter(const QString& var_name, int value)
413 {
414  auto n = parameter.find(var_name.toStdString());
415  if ((n != parameter.end())
416  && (n->second.type == Widgets::Variable::INT_PARAMETER))
417  {
418  n->second.edit->setText(QString::number(value));
419  n->second.str_value = n->second.edit->text();
420  auto param_id = static_cast<Widgets::Variable::Id>(n->second.info.id);
421  this->hostPlugin->setComponentParameter<int>(param_id, value);
422  // setValue(n->second.index, n->second.edit->text().toDouble());
423  }
424 }
425 
426 void Widgets::Panel::setParameter(const QString& var_name, uint64_t value)
427 {
428  auto n = parameter.find(var_name.toStdString());
429  if ((n != parameter.end())
430  && (n->second.type == Widgets::Variable::UINT_PARAMETER))
431  {
432  n->second.edit->setText(QString::number(value));
433  n->second.str_value = n->second.edit->text();
434  auto param_id = static_cast<Widgets::Variable::Id>(n->second.info.id);
435  this->hostPlugin->setComponentParameter<uint64_t>(param_id, value);
436  // setValue(n->second.index, n->second.edit->text().toDouble());
437  }
438 }
439 
441 {
442  if (pauseButton->isChecked() != p) {
443  pauseButton->setDown(p);
444  }
445  // const int result = this->hostPlugin->setActive(!p);
446  // if (result != 0) {
447  // ERROR_MSG("Unable to pause/Unpause Plugin {} ", this->getName());
448  // return;
449  // }
450  if (p) {
451  this->update_state(RT::State::PAUSE);
452  } else {
453  this->update_state(RT::State::UNPAUSE);
454  }
455 }
456 
457 Widgets::Plugin::Plugin(Event::Manager* ev_manager, std::string mod_name)
458  : event_manager(ev_manager)
459  , name(std::move(mod_name))
460 {
461 }
462 
464 {
465  if (this->plugin_component != nullptr) {
467  unplug_block_event.setParam(
468  "thread",
469  std::any(static_cast<RT::Thread*>(this->plugin_component.get())));
470  this->event_manager->postEvent(&unplug_block_event);
471  }
472 }
473 
475 {
476  if (this->plugin_component == nullptr) {
477  return;
478  }
480  event.setParam("thread",
481  static_cast<RT::Thread*>(this->plugin_component.get()));
482  this->event_manager->postEvent(&event);
483 }
484 
486 {
487  if (!this->plugin_component) {
488  return;
489  }
491  event.setParam(
492  "component",
493  std::any(static_cast<Widgets::Component*>(this->plugin_component.get())));
494  event.setParam("state", std::any(state));
495  this->event_manager->postEvent(&event);
496 }
497 
498 std::vector<Widgets::Variable::Info>
500 {
501  return this->plugin_component->getParametersInfo();
502 }
503 
505  std::unique_ptr<Widgets::Component> component)
506 {
507  // If there is a component already attached or invalid
508  // componnet pointer provided then cancel attach
509  if (this->plugin_component != nullptr || component == nullptr) {
510  return;
511  }
512  this->plugin_component = std::move(component);
513  // Let's set the component as active before registering it to rt thread
514  // this avoids unnecessary use of event firing which is much slower
515  this->plugin_component->setActive(/*act=*/true);
516  this->registerComponent();
517 }
518 
520 {
521  this->widget_panel = panel;
522  panel->setHostPlugin(this);
523 }
524 
526  const Widgets::Variable::Id& parameter_id)
527 {
528  return this->plugin_component->getValue<int64_t>(parameter_id);
529 }
530 
532  const Widgets::Variable::Id& parameter_id)
533 {
534  return this->plugin_component->getValue<uint64_t>(parameter_id);
535 }
536 
538  const Widgets::Variable::Id& parameter_id)
539 {
540  return this->plugin_component->getValue<double>(parameter_id);
541 }
542 
544 {
545  // We provide base functionality for handling period changes
546  switch (event->getType()) {
548  if (this->widget_panel != nullptr) {
549  this->widget_panel->signal_state_change(RT::State::PERIOD);
550  }
551  break;
552  default:
553  break;
554  }
555 }
556 
558 {
559  bool active = false;
560  // some plugins may not have a valid component pointer
561  if (this->plugin_component != nullptr) {
562  active = this->plugin_component->getActive();
563  }
564  return active;
565 }
566 
568 {
569  const int result = 0;
570  Event::Type event_type = Event::Type::NOOP;
571  if (state) {
573  } else {
575  }
576  Event::Object event(event_type);
577  event.setParam(
578  "thread",
579  std::any(static_cast<RT::Thread*>(this->plugin_component.get())));
580  this->event_manager->postEvent(&event);
581  return result;
582 }
583 
585 {
586  return this->plugin_component.get();
587 }
588 
590 {
591  return this->event_manager;
592 }
593 
595 {
596  return this->widget_panel;
597 }
void setParam(const std::string &param_name, const std::any &param_value)
Definition: event.cpp:191
Event::Type getType() const
Definition: event.cpp:228
std::string getName() const
Definition: io.hpp:108
std::string getDescription(const size_t &var_id)
Definition: widgets.cpp:120
Component(Widgets::Plugin *hplugin, const std::string &mod_name, const std::vector< IO::channel_t > &channels, const std::vector< Widgets::Variable::Info > &variables)
Definition: widgets.cpp:98
std::string getValueString(const size_t &var_id)
Definition: widgets.cpp:125
DefaultGUILineEdit(QWidget *parent)
Definition: widgets.cpp:75
void setHostPlugin(Widgets::Plugin *hplugin)
Definition: widgets.hpp:310
void signal_state_change(RT::State::state_t state)
Panel(const std::string &mod_name, QMainWindow *mw, Event::Manager *ev_manager)
Definition: widgets.cpp:155
virtual void createGUI(const std::vector< Widgets::Variable::Info > &vars, const std::vector< Widgets::Variable::Id > &skip_ids)
Definition: widgets.cpp:185
virtual void refresh()
Definition: widgets.cpp:296
void setComment(const QString &var_name, const QString &comment)
Definition: widgets.cpp:386
virtual void pause(bool p)
Definition: widgets.cpp:440
void closeEvent(QCloseEvent *event) override
Definition: widgets.cpp:179
virtual void exit()
Definition: widgets.cpp:286
virtual void update_state(RT::State::state_t flag)
Definition: widgets.cpp:275
void resizeMe()
Definition: widgets.cpp:281
void setParameter(const QString &var_name, double value)
Definition: widgets.cpp:398
virtual void modify()
Definition: widgets.cpp:340
int64_t getComponentIntParameter(const Variable::Id &parameter_id)
Definition: widgets.cpp:525
bool getActive()
Definition: widgets.cpp:557
virtual std::vector< Widgets::Variable::Info > getComponentParametersInfo()
Definition: widgets.cpp:499
double getComponentDoubleParameter(const Variable::Id &parameter_id)
Definition: widgets.cpp:537
~Plugin() override
Definition: widgets.cpp:463
void attachComponent(std::unique_ptr< Widgets::Component > component)
Definition: widgets.cpp:504
void setComponentState(RT::State::state_t state)
Definition: widgets.cpp:485
Plugin(Event::Manager *ev_manager, std::string mod_name)
Definition: widgets.cpp:457
void receiveEvent(Event::Object *event) override
Definition: widgets.cpp:543
uint64_t getComponentUIntParameter(const Variable::Id &parameter_id)
Definition: widgets.cpp:531
Widgets::Panel * getPanel()
Definition: widgets.cpp:594
void attachPanel(Widgets::Panel *panel)
Definition: widgets.cpp:519
int setActive(bool state)
Definition: widgets.cpp:567
Event::Manager * getEventManager()
Definition: widgets.cpp:589
Widgets::Component * getComponent()
Definition: widgets.cpp:584
void registerComponent()
Definition: widgets.cpp:474
void ERROR_MSG(const std::string &errmsg, Args... args)
Definition: debug.hpp:36
Type
Definition: event.hpp:55
@ RT_THREAD_PAUSE_EVENT
Definition: event.hpp:62
@ RT_PERIOD_EVENT
Definition: event.hpp:56
@ RT_THREAD_UNPAUSE_EVENT
Definition: event.hpp:63
@ RT_WIDGET_STATE_CHANGE_EVENT
Definition: event.hpp:69
@ PLUGIN_REMOVE_EVENT
Definition: event.hpp:78
@ RT_THREAD_INSERT_EVENT
Definition: event.hpp:60
@ NOOP
Definition: event.hpp:93
@ RT_THREAD_REMOVE_EVENT
Definition: event.hpp:61
state_t
Definition: rt.hpp:53
@ EXIT
Definition: rt.hpp:60
@ UNPAUSE
Definition: rt.hpp:59
@ PAUSE
Definition: rt.hpp:58
@ INIT
Definition: rt.hpp:54
@ MODIFY
Definition: rt.hpp:56
@ PERIOD
Definition: rt.hpp:57
Definition: fifo.cpp:31
std::string vartype2string(variable_t type)
Definition: widgets.cpp:51
constexpr Id INVALID_ID
Definition: widgets.hpp:48
std::string state2string(RT::State::state_t state)
Definition: widgets.cpp:22