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


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon Jun 19 2023 03:01:38