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 <QSyntaxHighlighter> 63 ui->pushButtonDeleteCurves->setIcon(
LoadSvgIcon(
":/resources/svg/remove_red.svg", theme));
64 ui->buttonLoadFunctions->setIcon(
LoadSvgIcon(
":/resources/svg/import.svg", theme));
65 ui->buttonSaveFunctions->setIcon(
LoadSvgIcon(
":/resources/svg/export.svg", theme));
66 ui->buttonSaveCurrent->setIcon(
LoadSvgIcon(
":/resources/svg/save.svg", theme));
75 ,
ui(new
Ui::FunctionEditor)
83 this->setWindowTitle(
"Create a custom timeseries");
85 QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
86 fixedFont.setPointSize(10);
88 ui->globalVarsTextField->setFont(fixedFont);
89 ui->mathEquation->setFont(fixedFont);
90 ui->snippetPreview->setFont(fixedFont);
92 auto theme = settings.value(
"StyleSheet:theme",
"ligth").toString();
95 QPalette palette =
ui->listAdditionalSources->palette();
96 palette.setBrush(QPalette::Highlight, palette.brush(QPalette::Base));
97 palette.setBrush(QPalette::HighlightedText, palette.brush(QPalette::Text));
98 ui->listAdditionalSources->setPalette(palette);
100 QStringList numericPlotNames;
103 QString
name = QString::fromStdString(p.first);
104 numericPlotNames.push_back(name);
106 numericPlotNames.sort(Qt::CaseInsensitive);
108 QByteArray saved_xml = settings.value(
"AddCustomPlotDialog.recentSnippetsXML", QByteArray()).toByteArray();
109 restoreGeometry(settings.value(
"AddCustomPlotDialog.geometry").toByteArray());
111 if (saved_xml.isEmpty())
113 QFile file(
"://resources/default.snippets.xml");
114 if (!file.open(QIODevice::ReadOnly))
116 throw std::runtime_error(
"problem with default.snippets.xml");
118 saved_xml = file.readAll();
123 ui->snippetsListSaved->setContextMenuPolicy(Qt::CustomContextMenu);
125 connect(
ui->snippetsListSaved, &QListWidget::customContextMenuRequested,
this,
128 ui->globalVarsTextField->setPlainText(settings.value(
"AddCustomPlotDialog.previousGlobals",
"").toString());
130 ui->mathEquation->setPlainText(settings.value(
"AddCustomPlotDialog.previousFunction",
"return value").toString());
132 ui->lineEditSource->installEventFilter(
this);
133 ui->listAdditionalSources->installEventFilter(
this);
135 auto preview_layout =
new QHBoxLayout(
ui->framePlotPreview);
136 preview_layout->setMargin(6);
152 settings.setValue(
"AddCustomPlotDialog.recentSnippetsXML",
exportSnippets());
153 settings.setValue(
"AddCustomPlotDialog.geometry", saveGeometry());
154 settings.setValue(
"AddCustomPlotDialog.previousGlobals",
ui->globalVarsTextField->toPlainText());
155 settings.setValue(
"AddCustomPlotDialog.previousFunction",
ui->mathEquation->toPlainText());
162 ui->lineEditSource->setText(linkedPlotName);
167 ui->lineEditSource->setText(
"");
168 ui->nameLineEdit->setText(
"");
169 ui->listAdditionalSources->setRowCount(0);
175 return ui->lineEditSource->text();
180 return ui->globalVarsTextField->toPlainText();
185 return ui->mathEquation->toPlainText();
190 return ui->nameLineEdit->text();
195 ui->nameLineEdit->setEnabled(
true);
196 ui->lineEditSource->setEnabled(
true);
203 ui->globalVarsTextField->setPlainText(data->snippet().globalVars);
204 ui->mathEquation->setPlainText(data->snippet().function);
206 ui->nameLineEdit->setText(QString::fromStdString(data->name()));
207 ui->nameLineEdit->setEnabled(
false);
211 auto list_widget =
ui->listAdditionalSources;
212 list_widget->setRowCount(0);
214 for (QString curve_name: data->snippet().additionalSources) {
215 if( list_widget->findItems(curve_name, Qt::MatchExactly).isEmpty() &&
216 curve_name !=
ui->lineEditSource->text() )
218 int row = list_widget->rowCount();
219 list_widget->setRowCount(row+1);
220 list_widget->setItem(row,0,
new QTableWidgetItem( QString(
"v%1").
arg(row+1)));
221 list_widget->setItem(row,1,
new QTableWidgetItem(curve_name));
234 if( ev->type() == QEvent::DragEnter )
236 auto event =
static_cast<QDragEnterEvent*
>(ev);
237 const QMimeData* mimeData =
event->mimeData();
238 QStringList mimeFormats = mimeData->formats();
240 for(
const QString&
format : mimeFormats)
242 QByteArray encoded = mimeData->data(
format);
243 QDataStream stream(&encoded, QIODevice::ReadOnly);
245 if (
format !=
"curveslist/add_curve")
252 while (!stream.atEnd())
255 stream >> curve_name;
256 if (!curve_name.isEmpty())
264 event->acceptProposedAction();
269 else if ( ev->type() == QEvent::Drop ) {
270 if( obj ==
ui->lineEditSource )
274 else if ( obj ==
ui->listAdditionalSources )
276 auto list_widget =
ui->listAdditionalSources;
278 if( list_widget->findItems(curve_name, Qt::MatchExactly).isEmpty() &&
279 curve_name !=
ui->lineEditSource->text() )
281 int row = list_widget->rowCount();
282 list_widget->setRowCount(row+1);
283 list_widget->setItem(row,0,
new QTableWidgetItem( QString(
"v%1").
arg(row+1)));
284 list_widget->setItem(row,1,
new QTableWidgetItem(curve_name));
296 ui->snippetsListSaved->clear();
302 ui->snippetsListSaved->addItem(it.first);
307 const auto& math_plot = custom_it.second;
309 snippet.
name = QString::fromStdString(math_plot->name());
311 if (_snipped_saved.count(snippet.
name) > 0)
316 snippet.
globalVars = math_plot->snippet().globalVars;
317 snippet.
function = math_plot->snippet().function;
319 ui->snippetsListSaved->sortItems();
326 doc.appendChild(root);
327 return doc.toByteArray(2);
334 ui->snippetPreview->setPlainText(
"");
337 const auto&
name =
ui->snippetsListSaved->currentItem()->text();
346 preview +=
"function calc(time, value";
350 preview += QString(
", v%1").arg(i);
354 auto function_lines = snippet.
function.split(
"\n");
355 for (
const auto& line: function_lines)
357 preview +=
" " + line +
"\n";
360 ui->snippetPreview->setPlainText(preview);
365 const auto&
name =
ui->snippetsListSaved->item(index.row())->text();
368 ui->globalVarsTextField->setPlainText(snippet.
globalVars);
369 ui->mathEquation->setPlainText(snippet.
function);
374 auto list_saved =
ui->snippetsListSaved;
376 if (list_saved->selectedItems().size() != 1)
383 QAction* rename_item =
new QAction(
"Rename...",
this);
384 menu.addAction(rename_item);
388 QAction* remove_item =
new QAction(
"Remove",
this);
389 menu.addAction(remove_item);
391 connect(remove_item, &QAction::triggered,
this, [list_saved,
this]() {
392 const auto& item = list_saved->selectedItems().first();
394 delete list_saved->takeItem(list_saved->row(item));
397 menu.exec(list_saved->mapToGlobal(pos));
404 ui->pushButtonCreate->setText(
"Create New Timeseries");
408 ui->pushButtonCreate->setText(
"Modify Timeseries");
416 QString directory_path = settings.value(
"AddCustomPlotDialog.loadDirectory", QDir::currentPath()).toString();
419 QFileDialog::getOpenFileName(
this, tr(
"Open Snippet Library"),
421 tr(
"Snippets (*.snippets.xml)"));
422 if (fileName.isEmpty())
427 QFile file(fileName);
429 if (!file.open(QIODevice::ReadOnly))
431 QMessageBox::critical(
this,
"Error", QString(
"Failed to open the file [%1]").
arg(fileName));
435 directory_path = QFileInfo(fileName).absolutePath();
436 settings.setValue(
"AddCustomPlotDialog.loadDirectory", directory_path);
444 QString directory_path = settings.value(
"AddCustomPlotDialog.loadDirectory",
445 QDir::currentPath()).toString();
448 QFileDialog::getSaveFileName(
this, tr(
"Open Snippet Library"),
450 tr(
"Snippets (*.snippets.xml)"));
452 if (fileName.isEmpty())
456 if (!fileName.endsWith(
".snippets.xml"))
458 fileName.append(
".snippets.xml");
461 QFile file(fileName);
462 if (!file.open(QIODevice::WriteOnly))
464 QMessageBox::critical(
this,
"Error", QString(
"Failed to open the file [%1]").
arg(fileName));
477 auto selected_snippets =
ui->snippetsListSaved->selectedItems();
478 if( selected_snippets.size() >= 1 )
480 name = selected_snippets.front()->text();
483 name = QInputDialog::getText(
this, tr(
"Name of the Function"),
484 tr(
"Name:"), QLineEdit::Normal,
487 if (!ok || name.isEmpty()) {
493 snippet.
globalVars =
ui->globalVarsTextField->toPlainText();
494 snippet.
function =
ui->mathEquation->toPlainText();
505 QMessageBox msgBox(
this);
506 msgBox.setWindowTitle(
"Warning");
507 msgBox.setText(tr(
"A function with the same name exists already in the list of saved functions.\n"));
508 msgBox.addButton(QMessageBox::Cancel);
509 QPushButton* button = msgBox.addButton(tr(
"Overwrite"), QMessageBox::YesRole);
510 msgBox.setDefaultButton(button);
512 int res = msgBox.exec();
514 if (res < 0 || res == QMessageBox::Cancel)
521 ui->snippetsListSaved->addItem(name);
522 ui->snippetsListSaved->sortItems();
530 auto list_saved =
ui->snippetsListSaved;
531 auto item = list_saved->selectedItems().first();
532 const auto&
name = item->text();
536 QInputDialog::getText(
this, tr(
"Change the name of the function"), tr(
"New name:"), QLineEdit::Normal,
name, &ok);
538 if (!ok || new_name.isEmpty() || new_name ==
name)
545 snippet.
name = new_name;
548 item->setText(new_name);
549 ui->snippetsListSaved->sortItems();
556 std::string new_plot_name =
getName().toStdString();
560 QMessageBox msgBox(
this);
561 msgBox.setWindowTitle(
"Warning");
562 msgBox.setText(tr(
"A custom time series with the same name exists already.\n" 563 " Do you want to overwrite it?\n"));
564 msgBox.addButton(QMessageBox::Cancel);
565 QPushButton* button = msgBox.addButton(tr(
"Overwrite"), QMessageBox::YesRole);
566 msgBox.setDefaultButton(button);
568 int res = msgBox.exec();
570 if (res < 0 || res == QMessageBox::Cancel)
581 for(
int row = 0; row <
ui->listAdditionalSources->rowCount(); row++)
586 CustomPlotPtr plot = std::make_unique<LuaCustomFunction>(snippet);
589 catch (
const std::runtime_error& e)
591 QMessageBox::critical(
this,
"Error",
"Failed to create math plot : " + QString::fromStdString(e.what()));
608 QString function_text(
"function( time, value");
609 for(
int row = 0; row <
ui->listAdditionalSources->rowCount(); row++)
611 function_text +=
", ";
612 function_text +=
ui->listAdditionalSources->item(row,0)->text();
614 function_text +=
" )";
615 ui->labelFunction->setText(function_text);
622 bool any_selected = !
ui->listAdditionalSources->selectedItems().isEmpty();
623 ui->pushButtonDeleteCurves->setEnabled(any_selected);
628 auto list_sources =
ui->listAdditionalSources;
629 QModelIndexList selected = list_sources->selectionModel()->selectedRows();
630 while( selected.size() > 0 )
632 list_sources->removeRow( selected.first().row() );
633 selected = list_sources->selectionModel()->selectedRows();
635 for(
int row = 0; row < list_sources->rowCount(); row++ )
637 list_sources->item(row,0)->setText( QString(
"v%1").
arg(row+1) );
662 std::string new_plot_name =
ui->nameLineEdit->text().toStdString();
666 if(
ui->lineEditSource->text().toStdString() == new_plot_name ||
667 ui->listAdditionalSources->findItems(
getName(), Qt::MatchExactly).isEmpty() == false )
669 errors +=
"- The name of the new timeseries is the same of one of its dependencies.\n";
673 if( new_plot_name.empty() ) {
674 errors+=
"- Missing name of the new time series.\n";
680 errors+=
"- Plot name already exists and can't be modified.\n";
684 if(
ui->lineEditSource->text().isEmpty() )
686 errors+=
"- Missing source time series.\n";
694 for(
int row = 0; row <
ui->listAdditionalSources->rowCount(); row++)
701 plot = std::make_unique<LuaCustomFunction>(snippet);
702 ui->buttonSaveCurrent->setEnabled(
true);
705 errors+=
"- The Lua function is not valid.\n";
706 ui->buttonSaveCurrent->setEnabled(
false);
712 std::string
name = new_plot_name.empty() ?
"no_name" : new_plot_name;
721 errors+=
"- The Lua function can not compute the result.\n";
726 QFile file(
":/resources/svg/red_circle.svg");
728 if( errors.isEmpty() )
730 errors =
"Everything is fine :)";
731 file.setFileName(
":/resources/svg/green_circle.svg");
732 ui->pushButtonCreate->setEnabled(
true);
735 errors = errors.left( errors.size()-1 );
736 ui->pushButtonCreate->setEnabled(
false);
739 ui->labelSemaphore->setToolTip(errors);
740 ui->labelSemaphore->setToolTipDuration(5000);
742 file.open(QFile::ReadOnly | QFile::Text);
743 auto svg_data = file.readAll();
745 QByteArray content(svg_data);
746 QSvgRenderer rr( content );
747 QImage image(26, 26, QImage::Format_ARGB32);
748 QPainter painter(&image);
749 image.fill(Qt::transparent);
751 ui->labelSemaphore->setPixmap( QPixmap::fromImage(image) );
FMT_INLINE std::basic_string< Char > format(const S &format_str, Args &&...args)
QIcon LoadSvgIcon(QString filename, QString style_name="light")
std::unordered_map< std::string, PlotData > numeric
QDomElement ExportSnippets(const SnippetsMap &snippets, QDomDocument &doc)
std::shared_ptr< CustomFunction > CustomPlotPtr
SnippetsMap GetSnippetsFromXML(const QString &xml_text)
detail::named_arg< Char, T > arg(const Char *name, const T &arg)
std::unordered_map< std::string, CustomPlotPtr > CustomPlotMap
std::unordered_map< std::string, PlotData >::iterator addNumeric(const std::string &name)
QStringList additionalSources