lua_editor.cpp
Go to the documentation of this file.
1 #include "lua_editor.h"
2 #include "ui_lua_editor.h"
3 #include <QSettings>
4 #include <QPushButton>
5 #include <QLineEdit>
6 #include <QSettings>
7 #include <memory>
8 #include <QWheelEvent>
9 #include <QMessageBox>
10 
12 #include "PlotJuggler/svg_util.h"
13 
14 #include "QSyntaxStyle"
15 
17 {
18  _widget = new QWidget(nullptr);
19  ui = new Ui::LuaEditor;
20  ui->setupUi(_widget);
21 
22  QString library_default_code = ui->textLibrary->toPlainText();
23 
24  ui->textGlobal->installEventFilter(this);
25  ui->textFunction->installEventFilter(this);
26  ui->textLibrary->installEventFilter(this);
27 
28  ui->labelSemaphore->setToolTipDuration(5000);
29 
30  ui->textGlobal->setAcceptDrops(true);
31  ui->textFunction->setAcceptDrops(true);
32 
33  connect(ui->pushButtonSave, &QPushButton::clicked, this, &ToolboxLuaEditor::onSave);
34 
35  connect(ui->pushButtonDelete, &QPushButton::clicked, this, &ToolboxLuaEditor::onDelete);
36 
37  connect(ui->lineEditFunctionName, &QLineEdit::textChanged, this, [this]() {
38  bool has_name = ui->lineEditFunctionName->text().isEmpty() == false;
39  ui->pushButtonSave->setEnabled(has_name);
40  });
41 
42  connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &ToolboxPlugin::closed);
43 
44  connect(ui->listWidgetRecent, &QListWidget::doubleClicked, this,
46 
47  connect(ui->listWidgetFunctions, &QListWidget::doubleClicked, this,
49 
50  connect(ui->listWidgetFunctions, &QListWidget::itemSelectionChanged, this, [this]() {
51  auto selected = ui->listWidgetFunctions->selectedItems();
52  ui->pushButtonDelete->setEnabled(selected.size() > 0);
53  });
54 
56 
57  connect(ui->textLibrary, &QTextEdit::textChanged,
58  [this]() { _delay_library_check.triggerSignal(250); });
59 
60  connect(ui->pushButtonDefaultLibrary, &QPushButton::clicked,
61  [=]() { ui->textLibrary->setPlainText(library_default_code); });
62 
63  connect(ui->pushButtonApplyLibrary, &QPushButton::clicked, this,
65 
66  ui->textGlobal->setHighlighter(new QLuaHighlighter);
67  ui->textFunction->setHighlighter(new QLuaHighlighter);
68  ui->textLibrary->setHighlighter(new QLuaHighlighter);
69 
70  _completer = new QLuaCompleter(this);
71  ui->textGlobal->setCompleter(_completer);
72  ui->textFunction->setCompleter(_completer);
73  ui->textLibrary->setCompleter(_completer);
74 
75  // restore recent functions
76  QSettings settings;
77  auto previous_functions =
78  settings.value("ToolboxLuaEditor/recent_functions", "").toString();
79  if (previous_functions.isEmpty() == false)
80  {
81  QDomDocument xml_doc;
82  if (xml_doc.setContent(previous_functions))
83  {
84  auto root = xml_doc.firstChild();
85  for (auto elem = root.firstChildElement("function"); !elem.isNull();
86  elem = elem.nextSiblingElement("function"))
87  {
88  auto name = elem.attribute("name");
89  auto item = new QListWidgetItem(name);
90  setItemData(item, name, elem.attribute("global"), elem.attribute("function"));
91  ui->listWidgetRecent->addItem(item);
92  }
93  }
94  }
95 
96  if (settings.contains("ToolboxLuaEditor/library"))
97  {
98  QString code = settings.value("ToolboxLuaEditor/library").toString();
99  ui->textLibrary->setPlainText(code);
100  }
101 
102  _previous_library = ui->textLibrary->toPlainText();
103 
105 }
106 
108 {
109  delete ui;
110 }
111 
112 const char* ToolboxLuaEditor::name() const
113 {
114  return "Reactive Script Editor";
115 }
116 
117 void ToolboxLuaEditor::init(PlotDataMapRef& src_data, TransformsMap& transform_map)
118 {
119  _plot_data = &src_data;
120  _transforms = &transform_map;
121 }
122 
123 std::pair<QWidget*, ToolboxPlugin::WidgetType> ToolboxLuaEditor::providedWidget() const
124 {
125  return { _widget, PJ::ToolboxPlugin::FIXED };
126 }
127 
128 bool ToolboxLuaEditor::xmlSaveState(QDomDocument& doc, QDomElement& parent_element) const
129 {
130  if (ui->listWidgetFunctions->count() > 0)
131  {
132  QString msg = "Do you want to save the current active scripts?\n\n";
133 
134  for (int row = 0; row < ui->listWidgetFunctions->count(); row++)
135  {
136  auto item = ui->listWidgetFunctions->item(row);
137  msg += QString(" - %1\n").arg(item->text());
138  }
139 
140  auto ret = QMessageBox::question(nullptr, this->name(), msg);
141  if (ret == QMessageBox::No)
142  {
143  return false;
144  }
145  }
146 
147  auto library_elem = doc.createElement("library");
148  library_elem.setAttribute("code", ui->textLibrary->toPlainText());
149  parent_element.appendChild(library_elem);
150 
151  auto scripts_elem = doc.createElement("scripts");
152 
153  for (int row = 0; row < ui->listWidgetFunctions->count(); row++)
154  {
155  auto item = ui->listWidgetFunctions->item(row);
156  auto fields = getItemData(item);
157  auto elem = doc.createElement("script");
158  elem.setAttribute("name", fields.name);
159  elem.setAttribute("function", fields.function_code);
160  elem.setAttribute("global", fields.global_code);
161  scripts_elem.appendChild(elem);
162  }
163  parent_element.appendChild(scripts_elem);
164 
165  return true;
166 }
167 
168 bool ToolboxLuaEditor::xmlLoadState(const QDomElement& parent_element)
169 {
170  auto library_elem = parent_element.firstChildElement("library");
171  if (!library_elem.isNull())
172  {
173  ui->textLibrary->setPlainText(library_elem.attribute("code"));
174  }
175 
176  auto scripts_elem = parent_element.firstChildElement("scripts");
177  if (!scripts_elem.isNull())
178  {
179  for (auto elem = scripts_elem.firstChildElement("script"); elem.isNull() == false;
180  elem = elem.nextSiblingElement("script"))
181  {
182  ui->listWidgetFunctions->clear();
183  QString name = elem.attribute("name");
184  QString function = elem.attribute("function");
185  QString global = elem.attribute("global");
186  auto item = new QListWidgetItem(name);
187  setItemData(item, name, global, function);
188  ui->listWidgetFunctions->addItem(item);
189 
190  auto lua_function = std::make_shared<ReactiveLuaFunction>(
191  _plot_data, global, function, ui->textLibrary->toPlainText());
192 
193  (*_transforms)[name.toStdString()] = lua_function;
194  }
195  ui->listWidgetFunctions->sortItems();
196  }
197 
198  return true;
199 }
200 
202 {
203  ui->listWidgetFunctions->clear();
204 
205  // check the already existing functions.
206  for (auto it : *_transforms)
207  {
208  if (auto lua_function = std::dynamic_pointer_cast<ReactiveLuaFunction>(it.second))
209  {
210  QString name = QString::fromStdString(it.first);
211  auto item = new QListWidgetItem(name);
212  setItemData(item, name, lua_function->getGlobalCode(),
213  lua_function->getFunctionCode());
214  ui->listWidgetFunctions->addItem(item);
215  }
216  ui->listWidgetFunctions->sortItems();
217  }
218 
219  QSettings settings;
220  QString theme = settings.value("StyleSheet::theme", "light").toString();
221 
222  ui->pushButtonDelete->setIcon(LoadSvg(":/resources/svg/clear.svg", theme));
223 
224  _font_size = settings.value("ToolboxLuaEditor/fonts_size", 12).toInt();
225 
226  QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
227  fixedFont.setPointSize(_font_size);
228 
229  ui->textGlobal->setFont(fixedFont);
230  ui->textFunction->setFont(fixedFont);
231  ui->textLibrary->setFont(fixedFont);
232 
233  auto style_path = (theme == "light") ? ":/resources/lua_style_light.xml" :
234  ":/resources/lua_style_dark.xml";
235 
236  QFile fl(style_path);
237  if (fl.open(QIODevice::ReadOnly))
238  {
239  auto style = new QSyntaxStyle(this);
240  if (style->load(fl.readAll()))
241  {
242  ui->textGlobal->setSyntaxStyle(style);
243  ui->textFunction->setSyntaxStyle(style);
244  ui->textLibrary->setSyntaxStyle(style);
245  }
246  }
247 
248  return true;
249 }
250 
252 {
253  auto name = ui->lineEditFunctionName->text();
254  if (ui->listWidgetFunctions->findItems(name, Qt::MatchExactly).size() > 0)
255  {
256  QMessageBox msgBox(_widget);
257  msgBox.setWindowTitle("Warning");
258  msgBox.setText(tr("A dfunction with the same name exists already.\n"
259  " Do you want to overwrite it?\n"));
260  msgBox.addButton(QMessageBox::Cancel);
261  QPushButton* button = msgBox.addButton(tr("Overwrite"), QMessageBox::YesRole);
262  msgBox.setDefaultButton(button);
263 
264  int res = msgBox.exec();
265  if (res < 0 || res == QMessageBox::Cancel)
266  {
267  return;
268  }
269  }
270 
271  try
272  {
273  auto lua_function = std::make_shared<ReactiveLuaFunction>(
274  _plot_data, ui->textGlobal->toPlainText(), ui->textFunction->toPlainText(),
275  ui->textLibrary->toPlainText());
276 
277  (*_transforms)[name.toStdString()] = lua_function;
278 
279  if (ui->listWidgetFunctions->findItems(name, Qt::MatchExactly).empty())
280  {
281  ui->listWidgetFunctions->addItem(name);
282  ui->listWidgetFunctions->sortItems();
283  }
284 
285  auto item = ui->listWidgetFunctions->findItems(name, Qt::MatchExactly).first();
286  setItemData(item, name, ui->textGlobal->toPlainText(),
287  ui->textFunction->toPlainText());
288 
289  for (auto& new_name : lua_function->createdCurves())
290  {
291  emit plotCreated(new_name);
292  }
293  }
294  catch (std::runtime_error& err)
295  {
296  QMessageBox::warning(nullptr, "Error in Lua code", QString(err.what()),
297  QMessageBox::Cancel);
298  }
299 
300  auto prev_items = ui->listWidgetRecent->findItems(name, Qt::MatchExactly);
301  // new name, delete oldest and append
302  if (prev_items.empty())
303  {
304  while (ui->listWidgetRecent->count() >= 10)
305  {
306  delete ui->listWidgetRecent->takeItem(0);
307  }
308  }
309  else
310  {
311  // overwrite item with same name
312  auto row = ui->listWidgetRecent->row(prev_items.first());
313  delete ui->listWidgetRecent->takeItem(row);
314  }
315 
316  // save recent functions
317  auto new_item = new QListWidgetItem(name);
318  setItemData(new_item, name, ui->textGlobal->toPlainText(),
319  ui->textFunction->toPlainText());
320  ui->listWidgetRecent->addItem(new_item);
321 
322  QDomDocument xml_doc;
323  auto root = xml_doc.createElement("functions");
324 
325  for (int row = 0; row < ui->listWidgetRecent->count(); row++)
326  {
327  auto item = ui->listWidgetRecent->item(row);
328  auto fields = getItemData(item);
329 
330  auto elem = xml_doc.createElement("function");
331  elem.setAttribute("name", fields.name);
332  elem.setAttribute("global", fields.global_code);
333  elem.setAttribute("function", fields.function_code);
334  root.appendChild(elem);
335  }
336  xml_doc.appendChild(root);
337 
338  QSettings settings;
339  settings.setValue("ToolboxLuaEditor/recent_functions", xml_doc.toString());
340 }
341 
343 {
344  for (auto item : ui->listWidgetFunctions->selectedItems())
345  {
346  _transforms->erase(item->text().toStdString());
347 
348  int row = ui->listWidgetFunctions->row(item);
349  delete ui->listWidgetFunctions->takeItem(row);
350  }
351 }
352 
353 void ToolboxLuaEditor::restoreRecent(const QModelIndex& index)
354 {
355  auto item = ui->listWidgetRecent->item(index.row());
356  auto fields = getItemData(item);
357  ui->lineEditFunctionName->setText(fields.name);
358  ui->textGlobal->setPlainText(fields.global_code);
359  ui->textFunction->setPlainText(fields.function_code);
360 }
361 
362 void ToolboxLuaEditor::restoreFunction(const QModelIndex& index)
363 {
364  auto item = ui->listWidgetFunctions->item(index.row());
365  auto fields = getItemData(item);
366  ui->lineEditFunctionName->setText(fields.name);
367  ui->textGlobal->setPlainText(fields.global_code);
368  ui->textFunction->setPlainText(fields.function_code);
369 }
370 
372 {
373  if (ui->textLibrary->toPlainText() == _previous_library)
374  {
375  ui->pushButtonApplyLibrary->setEnabled(false);
376  return;
377  }
378 
379  QString svg_name = ":/resources/svg/green_circle.svg";
380  try
381  {
382  ReactiveLuaFunction tmp(_plot_data, "", "", ui->textLibrary->toPlainText());
383  ui->labelSemaphore->setToolTip("Everything is fine :)");
384  int active_series = ui->listWidgetFunctions->count();
385  ui->pushButtonApplyLibrary->setEnabled(active_series > 0);
386  _previous_library = ui->textLibrary->toPlainText();
387 
388  QSettings settings;
389  settings.setValue("ToolboxLuaEditor/library", ui->textLibrary->toPlainText());
390  }
391  catch (std::runtime_error& ex)
392  {
393  QString error_msg = ex.what();
394  ui->labelSemaphore->setToolTip(error_msg);
395  svg_name = ":/resources/svg/red_circle.svg";
396  ui->pushButtonApplyLibrary->setEnabled(false);
397  }
398 
399  QFile file(svg_name);
400  file.open(QFile::ReadOnly | QFile::Text);
401  QByteArray content(file.readAll());
402  QSvgRenderer rr(content);
403  QImage image(ui->labelSemaphore->width(), ui->labelSemaphore->height(),
404  QImage::Format_ARGB32);
405  QPainter painter(&image);
406  image.fill(Qt::transparent);
407  rr.render(&painter);
408  ui->labelSemaphore->setPixmap(QPixmap::fromImage(image));
409 }
410 
412 {
413  for (int row = 0; row < ui->listWidgetFunctions->count(); row++)
414  {
415  auto item = ui->listWidgetFunctions->item(row);
416  auto name = item->text();
417  auto fields = getItemData(item);
418  try
419  {
420  auto lua_function = std::make_shared<ReactiveLuaFunction>(
421  _plot_data, fields.global_code, fields.function_code,
422  ui->textLibrary->toPlainText());
423 
424  (*_transforms)[fields.name.toStdString()] = lua_function;
425  }
426  catch (std::runtime_error& err)
427  {
428  QMessageBox::warning(nullptr, "Error in Lua code", QString(err.what()),
429  QMessageBox::Cancel);
430  }
431  }
432  ui->pushButtonApplyLibrary->setEnabled(false);
433 }
434 
435 bool ToolboxLuaEditor::eventFilter(QObject* obj, QEvent* ev)
436 {
437  if (obj != ui->textGlobal && obj != ui->textFunction && obj != ui->textLibrary)
438  {
439  return false;
440  }
441 
442  if (ev->type() == QEvent::DragEnter)
443  {
444  _dragging_curves.clear();
445  auto event = static_cast<QDragEnterEvent*>(ev);
446  const QMimeData* mimeData = event->mimeData();
447  QStringList mimeFormats = mimeData->formats();
448  for (const QString& format : mimeFormats)
449  {
450  QByteArray encoded = mimeData->data(format);
451  QDataStream stream(&encoded, QIODevice::ReadOnly);
452 
453  if (format != "curveslist/add_curve")
454  {
455  return false;
456  }
457 
458  while (!stream.atEnd())
459  {
460  QString curve_name;
461  stream >> curve_name;
462  if (!curve_name.isEmpty())
463  {
464  _dragging_curves.push_back(curve_name);
465  }
466  }
467  if (!_dragging_curves.empty())
468  {
469  event->acceptProposedAction();
470  }
471  }
472  return true;
473  }
474  else if (ev->type() == QEvent::Drop)
475  {
476  auto text_edit = qobject_cast<QPlainTextEdit*>(obj);
477  for (const auto& name : _dragging_curves)
478  {
479  text_edit->insertPlainText(QString("\"%1\"\n").arg(name));
480  }
481  _dragging_curves.clear();
482  return true;
483  }
484  else if (ev->type() == QEvent::Wheel)
485  {
486  QWheelEvent* wheel_event = dynamic_cast<QWheelEvent*>(ev);
487  bool ctrl_modifier_pressed =
488  (QGuiApplication::keyboardModifiers() == Qt::ControlModifier);
489 
490  if (ctrl_modifier_pressed)
491  {
492  int prev_size = _font_size;
493  if (wheel_event->delta() < 0)
494  {
495  _font_size = std::max(8, prev_size - 1);
496  }
497  else if (wheel_event->delta() > 0)
498  {
499  _font_size = std::min(14, prev_size + 1);
500  }
501  if (_font_size != prev_size)
502  {
503  auto font = ui->textGlobal->font();
504  font.setPointSize(_font_size);
505  ui->textGlobal->setFont(font);
506  ui->textFunction->setFont(font);
507  ui->textLibrary->setFont(font);
508 
509  QSettings settings;
510  settings.setValue("ToolboxLuaEditor/fonts_size", _font_size);
511  }
512  return true;
513  }
514  }
515  return false;
516 }
517 
519 ToolboxLuaEditor::getItemData(const QListWidgetItem* item) const
520 {
521  auto fields = item->data(Qt::UserRole).toStringList();
522  SavedData data;
523  data.name = fields[0];
524  data.global_code = fields[1];
525  data.function_code = fields[2];
526  return data;
527 }
528 
529 void ToolboxLuaEditor::setItemData(QListWidgetItem* item, QString name,
530  QString global_code, QString function_code)
531 {
532  QStringList save_fields;
533  save_fields.push_back(name);
534  save_fields.push_back(global_code);
535  save_fields.push_back(function_code);
536  item->setData(Qt::UserRole, save_fields);
537 }
ToolboxLuaEditor::ToolboxLuaEditor
ToolboxLuaEditor()
Definition: lua_editor.cpp:16
ToolboxLuaEditor::~ToolboxLuaEditor
~ToolboxLuaEditor() override
Definition: lua_editor.cpp:107
lua_editor.h
ToolboxLuaEditor::_widget
QWidget * _widget
Definition: lua_editor.h:59
ToolboxLuaEditor::providedWidget
std::pair< QWidget *, WidgetType > providedWidget() const override
Definition: lua_editor.cpp:123
LoadSvg
const QPixmap & LoadSvg(QString filename, QString style_name="light")
Definition: svg_util.h:26
ToolboxLuaEditor::onSave
void onSave()
Definition: lua_editor.cpp:251
ToolboxLuaEditor::_plot_data
PJ::PlotDataMapRef * _plot_data
Definition: lua_editor.h:62
PJ::ToolboxPlugin::plotCreated
void plotCreated(std::string plot_name, bool is_custom=true)
arg
auto arg(const Char *name, const T &arg) -> detail::named_arg< Char, T >
Definition: core.h:1875
ToolboxLuaEditor::restoreFunction
void restoreFunction(const QModelIndex &index)
Definition: lua_editor.cpp:362
QLuaCompleter
Class, that describes completer with glsl specific types and functions.
Definition: QLuaCompleter.hpp:10
ToolboxLuaEditor::name
const char * name() const override
Name of the plugin type, NOT the particular instance.
Definition: lua_editor.cpp:112
ToolboxLuaEditor::_delay_library_check
DelayedCallback _delay_library_check
Definition: lua_editor.h:71
mqtt_test_proto.msg
msg
Definition: mqtt_test_proto.py:43
PJ::TransformsMap
std::unordered_map< std::string, std::shared_ptr< TransformFunction > > TransformsMap
Definition: transform_function.h:85
PJ::ReactiveLuaFunction
Definition: reactive_function.h:66
ToolboxLuaEditor::onDelete
void onDelete()
Definition: lua_editor.cpp:342
PJ::ToolboxPlugin::FIXED
@ FIXED
Definition: toolbox_base.h:34
ToolboxLuaEditor::xmlSaveState
bool xmlSaveState(QDomDocument &doc, QDomElement &parent_element) const override
Override this method to save the status of the plugin to XML.
Definition: lua_editor.cpp:128
reactive_function.h
ToolboxLuaEditor::xmlLoadState
bool xmlLoadState(const QDomElement &parent_element) override
Override this method to load the status of the plugin from XML.
Definition: lua_editor.cpp:168
ToolboxLuaEditor::setItemData
void setItemData(QListWidgetItem *item, QString name, QString global_code, QString function_code)
Definition: lua_editor.cpp:529
ToolboxLuaEditor::init
void init(PJ::PlotDataMapRef &src_data, PJ::TransformsMap &transform_map) override
Definition: lua_editor.cpp:117
ToolboxLuaEditor::ui
Ui::LuaEditor * ui
Definition: lua_editor.h:60
ToolboxLuaEditor::SavedData
Definition: lua_editor.h:75
format
auto format(const text_style &ts, const S &format_str, const Args &... args) -> std::basic_string< Char >
Definition: color.h:543
ToolboxLuaEditor::eventFilter
bool eventFilter(QObject *obj, QEvent *event) override
Definition: lua_editor.cpp:435
ToolboxLuaEditor::onLibraryUpdated
void onLibraryUpdated()
Definition: lua_editor.cpp:371
QSyntaxStyle
Class, that describes Qt style parser for QCodeEditor.
Definition: QSyntaxStyle.hpp:13
ToolboxLuaEditor::_previous_library
QString _previous_library
Definition: lua_editor.h:73
ToolboxLuaEditor::onReloadLibrary
void onReloadLibrary()
Definition: lua_editor.cpp:411
ToolboxLuaEditor::_font_size
int _font_size
Definition: lua_editor.h:70
ToolboxLuaEditor::restoreRecent
void restoreRecent(const QModelIndex &index)
Definition: lua_editor.cpp:353
mqtt_test.data
dictionary data
Definition: mqtt_test.py:22
ToolboxLuaEditor::_transforms
PJ::TransformsMap * _transforms
Definition: lua_editor.h:63
QLuaHighlighter
Class, that describes C++ code highlighter.
Definition: QLuaHighlighter.hpp:19
ToolboxLuaEditor::_dragging_curves
QStringList _dragging_curves
Definition: lua_editor.h:66
PJ::PlotDataMapRef
Definition: plotdata.h:34
PJ::DelayedCallback::connectCallback
void connectCallback(Function callback)
Definition: delayed_callback.hpp:32
ToolboxLuaEditor::_completer
QLuaCompleter * _completer
Definition: lua_editor.h:68
ToolboxLuaEditor::onShowWidget
bool onShowWidget() override
Definition: lua_editor.cpp:201
mqtt_test.ret
ret
Definition: mqtt_test.py:30
svg_util.h
ToolboxLuaEditor::getItemData
SavedData getItemData(const QListWidgetItem *item) const
Definition: lua_editor.cpp:519


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Aug 11 2024 02:24:23