7 #include <QDomDocument>
9 #include <QFontDatabase>
15 #include <QFileDialog>
19 #include <QInputDialog>
20 #include <QDragEnterEvent>
21 #include <QMouseEvent>
22 #include <QEnterEvent>
24 #include <QTableWidgetItem>
26 #include <QListWidget>
27 #include <QListWidgetItem>
28 #include <QSyntaxHighlighter>
30 #include "QLuaHighlighter"
34 #include "ui_function_editor_help.h"
39 ui->pushButtonDeleteCurves->setIcon(
LoadSvg(
":/resources/svg/trash.svg", theme));
40 ui->buttonLoadFunctions->setIcon(
LoadSvg(
":/resources/svg/import.svg", theme));
41 ui->buttonSaveFunctions->setIcon(
LoadSvg(
":/resources/svg/export.svg", theme));
42 ui->buttonSaveCurrent->setIcon(
LoadSvg(
":/resources/svg/save.svg", theme));
46 ui->globalVarsText->setSyntaxStyle(style);
47 ui->globalVarsTextBatch->setSyntaxStyle(style);
49 ui->functionText->setSyntaxStyle(style);
50 ui->functionTextBatch->setSyntaxStyle(style);
57 , _plot_map_data(plotMapData)
58 , _transform_maps(mapped_custom_plots)
59 , ui(new
Ui::FunctionEditor)
61 , _preview_widget(new
PlotWidget(_local_plot_data, this))
82 this->setWindowTitle(
"Create a custom timeseries");
84 QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
85 fixedFont.setPointSize(10);
87 ui->globalVarsText->setFont(fixedFont);
88 ui->functionText->setFont(fixedFont);
89 ui->globalVarsTextBatch->setFont(fixedFont);
90 ui->functionTextBatch->setFont(fixedFont);
91 ui->snippetPreview->setFont(fixedFont);
93 auto theme = settings.value(
"StyleSheet::theme",
"light").toString();
96 QPalette palette =
ui->listAdditionalSources->palette();
97 palette.setBrush(QPalette::Highlight, palette.brush(QPalette::Base));
98 palette.setBrush(QPalette::HighlightedText, palette.brush(QPalette::Text));
99 ui->listAdditionalSources->setPalette(palette);
101 QStringList numericPlotNames;
104 QString name = QString::fromStdString(p.first);
105 numericPlotNames.push_back(name);
107 numericPlotNames.sort(Qt::CaseInsensitive);
109 QByteArray saved_xml =
110 settings.value(
"FunctionEditorWidget.recentSnippetsXML", QByteArray())
112 restoreGeometry(settings.value(
"FunctionEditorWidget.geometry").toByteArray());
114 if (saved_xml.isEmpty())
116 QFile file(
"://resources/default.snippets.xml");
117 if (!file.open(QIODevice::ReadOnly))
119 throw std::runtime_error(
"problem with default.snippets.xml");
121 saved_xml = file.readAll();
126 ui->snippetsListSaved->setContextMenuPolicy(Qt::CustomContextMenu);
128 connect(
ui->snippetsListSaved, &QListWidget::customContextMenuRequested,
this,
131 ui->globalVarsText->setPlainText(
132 settings.value(
"FunctionEditorWidget.previousGlobals",
"").toString());
133 ui->globalVarsTextBatch->setPlainText(
134 settings.value(
"FunctionEditorWidget.previousGlobalsBatch",
"").toString());
136 ui->functionText->setPlainText(
137 settings.value(
"FunctionEditorWidget.previousFunction",
"return value").toString());
138 ui->functionTextBatch->setPlainText(
139 settings.value(
"FunctionEditorWidget.previousFunctionBatch",
"return value")
142 ui->lineEditSource->installEventFilter(
this);
143 ui->listAdditionalSources->installEventFilter(
this);
144 ui->lineEditTab2Filter->installEventFilter(
this);
146 auto preview_layout =
new QHBoxLayout(
ui->framePlotPreview);
147 preview_layout->setMargin(6);
159 int batch_filter_type = settings.value(
"FunctionEditorWidget.filterType", 2).toInt();
160 switch (batch_filter_type)
163 ui->radioButtonContains->setChecked(
true);
166 ui->radioButtonWildcard->setChecked(
true);
169 ui->radioButtonRegExp->setChecked(
true);
173 bool use_batch_prefix =
174 settings.value(
"FunctionEditorWidget.batchPrefix",
false).toBool();
175 ui->radioButtonPrefix->setChecked(use_batch_prefix);
181 settings.setValue(
"FunctionEditorWidget.recentSnippetsXML",
exportSnippets());
182 settings.setValue(
"FunctionEditorWidget.geometry", saveGeometry());
184 settings.setValue(
"FunctionEditorWidget.previousGlobals",
185 ui->globalVarsText->toPlainText());
186 settings.setValue(
"FunctionEditorWidget.previousGlobalsBatch",
187 ui->globalVarsTextBatch->toPlainText());
189 settings.setValue(
"FunctionEditorWidget.previousFunction",
190 ui->functionText->toPlainText());
191 settings.setValue(
"FunctionEditorWidget.previousFunctionBatch",
192 ui->functionTextBatch->toPlainText());
193 int batch_filter_type = 0;
194 if (
ui->radioButtonContains->isChecked())
196 batch_filter_type = 1;
198 else if (
ui->radioButtonWildcard->isChecked())
200 batch_filter_type = 2;
202 if (
ui->radioButtonRegExp->isChecked())
204 batch_filter_type = 3;
206 settings.setValue(
"FunctionEditorWidget.filterType", batch_filter_type);
208 settings.setValue(
"FunctionEditorWidget.batchPrefix",
209 ui->radioButtonPrefix->isChecked());
223 ui->lineEditSource->setText(linkedPlotName);
228 ui->lineEditSource->setText(
"");
229 ui->nameLineEdit->setText(
"");
230 ui->listAdditionalSources->setRowCount(0);
232 ui->suffixLineEdit->setText(
"");
233 ui->listBatchSources->clear();
234 ui->lineEditTab2Filter->setText(
"");
239 return ui->lineEditSource->text();
244 ui->nameLineEdit->setEnabled(
true);
245 ui->lineEditSource->setEnabled(
true);
251 ui->globalVarsText->setPlainText(
data->snippet().global_vars);
252 ui->functionText->setPlainText(
data->snippet().function);
254 ui->nameLineEdit->setText(
data->aliasName());
255 ui->nameLineEdit->setEnabled(
false);
259 auto list_widget =
ui->listAdditionalSources;
260 list_widget->setRowCount(0);
262 for (QString curve_name :
data->snippet().additional_sources)
264 if (list_widget->findItems(curve_name, Qt::MatchExactly).isEmpty() &&
265 curve_name !=
ui->lineEditSource->text())
267 int row = list_widget->rowCount();
268 list_widget->setRowCount(row + 1);
269 list_widget->setItem(row, 0,
new QTableWidgetItem(QString(
"v%1").
arg(row + 1)));
270 list_widget->setItem(row, 1,
new QTableWidgetItem(curve_name));
283 if (ev->type() == QEvent::DragEnter)
285 auto event =
static_cast<QDragEnterEvent*
>(ev);
286 const QMimeData* mimeData =
event->mimeData();
287 QStringList mimeFormats = mimeData->formats();
289 for (
const QString&
format : mimeFormats)
291 QByteArray encoded = mimeData->data(
format);
292 QDataStream stream(&encoded, QIODevice::ReadOnly);
294 if (
format !=
"curveslist/add_curve")
301 while (!stream.atEnd())
304 stream >> curve_name;
305 if (!curve_name.isEmpty())
314 event->acceptProposedAction();
319 else if (ev->type() == QEvent::Drop)
321 if (obj ==
ui->lineEditSource)
325 else if (obj ==
ui->lineEditTab2Filter)
329 else if (obj ==
ui->listAdditionalSources)
331 auto list_widget =
ui->listAdditionalSources;
334 if (list_widget->findItems(curve_name, Qt::MatchExactly).isEmpty() &&
335 curve_name !=
ui->lineEditSource->text())
337 int row = list_widget->rowCount();
338 list_widget->setRowCount(row + 1);
339 list_widget->setItem(row, 0,
new QTableWidgetItem(QString(
"v%1").
arg(row + 1)));
340 list_widget->setItem(row, 1,
new QTableWidgetItem(curve_name));
352 ui->snippetsListSaved->clear();
358 ui->snippetsListSaved->addItem(it.first);
376 snippet.
global_vars = math_plot->snippet().global_vars;
377 snippet.
function = math_plot->snippet().function;
379 ui->snippetsListSaved->sortItems();
386 doc.appendChild(root);
387 return doc.toByteArray(2);
394 ui->snippetPreview->setPlainText(
"");
397 const auto& name =
ui->snippetsListSaved->currentItem()->text();
406 preview +=
"function calc(time, value";
410 preview += QString(
", v%1").arg(i);
414 auto function_lines = snippet.
function.split(
"\n");
415 for (
const auto& line : function_lines)
417 preview +=
" " + line +
"\n";
420 ui->snippetPreview->setPlainText(preview);
425 const auto& name =
ui->snippetsListSaved->item(index.row())->text();
429 ui->functionText->setPlainText(snippet.
function);
434 auto list_saved =
ui->snippetsListSaved;
436 if (list_saved->selectedItems().size() != 1)
443 QAction* rename_item =
new QAction(
"Rename...",
this);
444 menu.addAction(rename_item);
448 QAction* remove_item =
new QAction(
"Remove",
this);
449 menu.addAction(remove_item);
451 connect(remove_item, &QAction::triggered,
this, [list_saved,
this]() {
452 const auto& item = list_saved->selectedItems().
first();
454 delete list_saved->takeItem(list_saved->row(item));
457 menu.exec(list_saved->mapToGlobal(pos));
464 ui->pushButtonCreate->setText(
"Create New Timeseries");
468 ui->pushButtonCreate->setText(
"Modify Timeseries");
476 QString directory_path =
477 settings.value(
"AddCustomPlotDialog.loadDirectory", QDir::currentPath()).toString();
479 QString fileName = QFileDialog::getOpenFileName(
480 this, tr(
"Open Snippet Library"), directory_path, tr(
"Snippets (*.snippets.xml)"));
481 if (fileName.isEmpty())
486 QFile file(fileName);
488 if (!file.open(QIODevice::ReadOnly))
490 QMessageBox::critical(
this,
"Error",
491 QString(
"Failed to open the file [%1]").
arg(fileName));
495 directory_path = QFileInfo(fileName).absolutePath();
496 settings.setValue(
"AddCustomPlotDialog.loadDirectory", directory_path);
504 QString directory_path =
505 settings.value(
"AddCustomPlotDialog.loadDirectory", QDir::currentPath()).toString();
507 QString fileName = QFileDialog::getSaveFileName(
508 this, tr(
"Open Snippet Library"), directory_path, tr(
"Snippets (*.snippets.xml)"));
510 if (fileName.isEmpty())
514 if (!fileName.endsWith(
".snippets.xml"))
516 fileName.append(
".snippets.xml");
519 QFile file(fileName);
520 if (!file.open(QIODevice::WriteOnly))
522 QMessageBox::critical(
this,
"Error",
523 QString(
"Failed to open the file [%1]").
arg(fileName));
531 directory_path = QFileInfo(fileName).absolutePath();
532 settings.setValue(
"AddCustomPlotDialog.loadDirectory", directory_path);
539 auto selected_snippets =
ui->snippetsListSaved->selectedItems();
540 if (selected_snippets.size() >= 1)
542 name = selected_snippets.front()->text();
545 name = QInputDialog::getText(
this, tr(
"Name of the Function"), tr(
"Name:"),
546 QLineEdit::Normal, name, &
ok);
548 if (!
ok || name.isEmpty())
556 snippet.
function =
ui->functionText->toPlainText();
567 QMessageBox msgBox(
this);
568 msgBox.setWindowTitle(
"Warning");
569 msgBox.setText(tr(
"A function with the same name exists already in the list of saved "
571 msgBox.addButton(QMessageBox::Cancel);
572 QPushButton* button = msgBox.addButton(tr(
"Overwrite"), QMessageBox::YesRole);
573 msgBox.setDefaultButton(button);
575 int res = msgBox.exec();
577 if (res < 0 || res == QMessageBox::Cancel)
584 ui->snippetsListSaved->addItem(name);
585 ui->snippetsListSaved->sortItems();
593 auto list_saved =
ui->snippetsListSaved;
594 auto item = list_saved->selectedItems().first();
595 const auto& name = item->text();
598 QString new_name = QInputDialog::getText(
this, tr(
"Change the name of the function"),
599 tr(
"New name:"), QLineEdit::Normal, name, &
ok);
601 if (!
ok || new_name.isEmpty() || new_name == name)
611 item->setText(new_name);
612 ui->snippetsListSaved->sortItems();
617 std::vector<CustomPlotPtr> created_plots;
621 if (
ui->tabWidget->currentIndex() == 0)
623 std::string new_plot_name =
ui->nameLineEdit->text().toStdString();
627 QMessageBox msgBox(
this);
628 msgBox.setWindowTitle(
"Warning");
629 msgBox.setText(tr(
"A custom time series with the same name exists already.\n"
630 " Do you want to overwrite it?\n"));
631 msgBox.addButton(QMessageBox::Cancel);
632 QPushButton* button = msgBox.addButton(tr(
"Overwrite"), QMessageBox::YesRole);
633 msgBox.setDefaultButton(button);
635 int res = msgBox.exec();
637 if (res < 0 || res == QMessageBox::Cancel)
644 snippet.
function =
ui->functionText->toPlainText();
648 for (
int row = 0; row <
ui->listAdditionalSources->rowCount(); row++)
651 ui->listAdditionalSources->item(row, 1)->text());
653 created_plots.push_back(std::make_unique<LuaCustomFunction>(snippet));
657 for (
int row = 0; row <
ui->listBatchSources->count(); row++)
660 snippet.
function =
ui->functionTextBatch->toPlainText();
661 snippet.
global_vars =
ui->globalVarsTextBatch->toPlainText();
663 if (
ui->radioButtonPrefix->isChecked())
671 created_plots.push_back(std::make_unique<LuaCustomFunction>(snippet));
678 catch (
const std::runtime_error& e)
680 QMessageBox::critical(
this,
"Error",
681 "Failed to create math plot : " +
682 QString::fromStdString(e.what()));
698 QString function_text(
"function( time, value");
699 for (
int row = 0; row <
ui->listAdditionalSources->rowCount(); row++)
701 function_text +=
", ";
702 function_text +=
ui->listAdditionalSources->item(row, 0)->text();
704 function_text +=
" )";
705 ui->labelFunction->setText(function_text);
712 bool any_selected = !
ui->listAdditionalSources->selectedItems().isEmpty();
713 ui->pushButtonDeleteCurves->setEnabled(any_selected);
718 auto list_sources =
ui->listAdditionalSources;
719 QModelIndexList selected = list_sources->selectionModel()->selectedRows();
720 while (selected.size() > 0)
722 list_sources->removeRow(selected.first().row());
723 selected = list_sources->selectionModel()->selectedRows();
725 for (
int row = 0; row < list_sources->rowCount(); row++)
727 list_sources->item(row, 0)->setText(QString(
"v%1").
arg(row + 1));
746 QFile file(
":/resources/svg/red_circle.svg");
748 if (errors.isEmpty())
750 errors =
"Everything is fine :)";
751 file.setFileName(
":/resources/svg/green_circle.svg");
752 ui->pushButtonCreate->setEnabled(
true);
756 errors = errors.left(errors.size() - 1);
757 ui->pushButtonCreate->setEnabled(
false);
760 semaphore->setToolTip(errors);
761 semaphore->setToolTipDuration(5000);
763 file.open(QFile::ReadOnly | QFile::Text);
764 auto svg_data = file.readAll();
766 QByteArray content(svg_data);
767 QSvgRenderer rr(content);
768 QImage image(26, 26, QImage::Format_ARGB32);
769 QPainter painter(&image);
770 image.fill(Qt::transparent);
772 semaphore->setPixmap(QPixmap::fromImage(image));
778 std::string new_plot_name =
ui->nameLineEdit->text().toStdString();
782 QString new_name =
ui->nameLineEdit->text();
783 if (
ui->lineEditSource->text().toStdString() == new_plot_name ||
784 !
ui->listAdditionalSources->findItems(new_name, Qt::MatchExactly).isEmpty())
786 errors +=
"- The name of the new timeseries is the same of one of its "
791 if (new_plot_name.empty())
793 errors +=
"- Missing name of the new time series.\n";
801 errors +=
"- Plot name already exists and can't be modified.\n";
805 if (
ui->lineEditSource->text().isEmpty())
807 errors +=
"- Missing source time series.\n";
811 snippet.
function =
ui->functionText->toPlainText();
815 for (
int row = 0; row <
ui->listAdditionalSources->rowCount(); row++)
823 lua_function = std::make_unique<LuaCustomFunction>(snippet);
824 ui->buttonSaveCurrent->setEnabled(
true);
826 catch (std::runtime_error& err)
828 errors += QString(
"- Error in Lua script: %1").arg(err.what());
829 ui->buttonSaveCurrent->setEnabled(
false);
836 std::string name = new_plot_name.empty() ?
"no_name" : new_plot_name;
840 std::vector<PlotData*> out_vector = { &out_data };
842 lua_function->calculate();
848 catch (std::runtime_error& err)
850 errors += QString(
"- Error in Lua script: %1").arg(err.what());
861 if (
ui->suffixLineEdit->text().isEmpty())
863 errors +=
"- Missing prefix/suffix.\n";
866 if (
ui->listBatchSources->count() == 0)
868 errors +=
"- No input series.\n";
872 snippet.
function =
ui->functionTextBatch->toPlainText();
873 snippet.
global_vars =
ui->globalVarsTextBatch->toPlainText();
877 auto lua_function = std::make_unique<LuaCustomFunction>(snippet);
879 catch (std::runtime_error& err)
881 errors += QString(
"- Error in Lua script: %1").arg(err.what());
889 QDialog* dialog =
new QDialog(
this);
890 auto ui =
new Ui_FunctionEditorHelp();
893 dialog->setAttribute(Qt::WA_DeleteOnClose);
899 QString filter_text =
ui->lineEditTab2Filter->text();
900 ui->listBatchSources->clear();
902 if (
ui->radioButtonRegExp->isChecked() ||
ui->radioButtonWildcard->isChecked())
904 QRegExp rx(filter_text);
905 if (
ui->radioButtonWildcard->isChecked())
907 rx.setPatternSyntax(QRegExp::Wildcard);
912 auto qname = QString::fromStdString(name);
913 if (rx.exactMatch(qname))
915 ui->listBatchSources->addItem(qname);
925 auto qname = QString::fromStdString(name);
926 for (
const auto& part : spaced_items)
928 if (qname.contains(part) ==
false)
936 ui->listBatchSources->addItem(qname);
940 ui->listBatchSources->sortItems();