mupp 1.1.0
Loading...
Searching...
No Matches
PVarDialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2
3 PVarDialog.cpp
4
5 Author: Andreas Suter
6 e-mail: andreas.suter@psi.ch
7
8***************************************************************************/
9
10/***************************************************************************
11 * Copyright (C) 2007-2026 by Andreas Suter *
12 * andreas.suter@psi.ch *
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 * This program is distributed in the hope that it will be useful, *
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22 * GNU General Public License for more details. *
23 * *
24 * You should have received a copy of the GNU General Public License *
25 * along with this program; if not, write to the *
26 * Free Software Foundation, Inc., *
27 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
28 ***************************************************************************/
29
30#include <iostream>
31
32#include <QVBoxLayout>
33#include <QHBoxLayout>
34#include <QSplitter>
35#include <QListWidgetItem>
36#include <QMessageBox>
37#include <QLabel>
38#include <QRegularExpression>
39
40#include "PVarDialog.h"
41
42//--------------------------------------------------------------------------
53{
54 // if fCollName is a path name, extract the fln
55 QString collNameStr(info.fCollName);
56 if (collNameStr.contains("/")) {
57 QStringList tok = collNameStr.split('/', Qt::SkipEmptyParts);
58 collNameStr = tok[tok.count()-1];
59 }
60 QLabel *collName = new QLabel(collNameStr);
61 QLabel *numVars = new QLabel(QString("#variables: %1").arg(info.fVarName.count()));
62 QListWidget *list = new QListWidget();
63 for (int i=0; i<info.fVarName.count(); i++) {
64 QListWidgetItem *newItem = new QListWidgetItem;
65 newItem->setText(QString("%1 : %2").arg(i, 2).arg(info.fVarName[i]));
66 list->addItem(newItem);
67 }
68 QPushButton *done = new QPushButton("Done", this);
69
70 QVBoxLayout *vLayout = new QVBoxLayout;
71 vLayout->addWidget(collName);
72 vLayout->addWidget(numVars);
73 vLayout->addWidget(list);
74 vLayout->addWidget(done);
75
76 connect(done, SIGNAL( clicked() ), this, SLOT( accept() ));
77
78 resize(300, 450);
79 setLayout(vLayout);
80 setModal(false);
81 setWindowTitle("Variable Names");
82
83 QString iconName("");
84 if (true) // <-- NEEDS TO BE PROPERLY IMPLEMENTED
85 iconName = QString(":/icons/varEdit-dark.svg");
86 else
87 iconName = QString(":/icons/varEdit-plain.svg");
88 setWindowIcon( QIcon( QPixmap(iconName) ) );
89}
90
91//--------------------------------------------------------------------------
113PVarDialog::PVarDialog(QVector<PCollInfo> collection_list, bool darkTheme,
114 QWidget *parent, Qt::WindowFlags f) :
115 QDialog(parent, f), fCollList(collection_list)
116{
117 fVarEdit = std::make_unique<QPlainTextEdit>();
118 fCollectionView = std::make_unique<QListWidget>();
119 fCancel = std::make_unique<QPushButton>("&Cancel", this);
120 fCheck = std::make_unique<QPushButton>("Chec&k", this);
121 fAdd = std::make_unique<QPushButton>("&Add", this);
122 fHelp = std::make_unique<QPushButton>("&Help", this);
123 fShowVarName = std::make_unique<QPushButton>("Show&VarName", this);
124
125 // fill collection view
126 for (int i=0; i<fCollList.count(); i++) {
127 QListWidgetItem *newItem = new QListWidgetItem;
128 newItem->setText(fCollList[i].fCollName);
129 fCollectionView->addItem(newItem);
130 }
131 fCollectionView->setCurrentRow(0, QItemSelectionModel::Select);
132 fCollectionView->setSelectionMode(QAbstractItemView::ExtendedSelection);
133
134 QHBoxLayout *hLayout0 = new QHBoxLayout;
135 hLayout0->addWidget(fShowVarName.get());
136 hLayout0->addWidget(fHelp.get());
137
138 QHBoxLayout *hLayout1 = new QHBoxLayout;
139 hLayout1->addWidget(fCancel.get());
140 hLayout1->addWidget(fCheck.get());
141 hLayout1->addWidget(fAdd.get());
142
143 QLabel *varLabel = new QLabel("Edit Variables:");
144 QVBoxLayout *varVLayout = new QVBoxLayout;
145 varVLayout->addWidget(varLabel);
146 varVLayout->addWidget(fVarEdit.get());
147
148 QLabel *collLabel = new QLabel("Collections:");
149 QVBoxLayout *collVLayout = new QVBoxLayout;
150 collVLayout->addWidget(collLabel);
151 collVLayout->addWidget(fCollectionView.get());
152
153 QWidget *varWidget = new QWidget(this); // only needed since splitter needs a QWidget
154 varWidget->setLayout(varVLayout);
155 QWidget *collWidget = new QWidget(this); // only needed since splitter needs a QWidget
156 collWidget->setLayout(collVLayout);
157
158 QSplitter *splitter = new QSplitter(Qt::Vertical, this);
159 splitter->addWidget(varWidget);
160 splitter->addWidget(collWidget);
161
162 QVBoxLayout *vLayout = new QVBoxLayout;
163 vLayout->addWidget(splitter);
164 vLayout->addLayout(hLayout0);
165 vLayout->addLayout(hLayout1);
166
167 fVarEdit->resize(600, 300);
168 setLayout(vLayout);
169 resize(600, 450);
170
171 connect(fCancel.get(), SIGNAL( clicked() ), this, SLOT( reject() ));
172 connect(fCheck.get(), SIGNAL( clicked() ), this, SLOT( check() ));
173 connect(fAdd.get(), SIGNAL( clicked() ), this, SLOT( add() ));
174 connect(fHelp.get(), SIGNAL( clicked() ), this, SLOT( help() ));
175 connect(fShowVarName.get(), SIGNAL( clicked() ), this, SLOT( showVarNames() ));
176
177 QString iconName("");
178 if (darkTheme)
179 iconName = QString(":/icons/varEdit-dark.svg");
180 else
181 iconName = QString(":/icons/varEdit-plain.svg");
182 setWindowIcon( QIcon( QPixmap(iconName) ) );
183}
184
185//--------------------------------------------------------------------------
195{
196 if (!basic_check())
197 return;
198
200 return;
201
202 // create the collection index vector
203 QVector<int> idx;
204 QList<QListWidgetItem *> selected = fCollectionView->selectedItems();
205 for (int i=0; i<selected.count(); i++) {
206 idx.push_back(fCollectionView->row(selected[i]));
207 }
208 emit check_request(fVarEdit->toPlainText(), idx);
209}
210
211//--------------------------------------------------------------------------
221{
222 if (fVarEdit->toPlainText().isEmpty()) {
223 QMessageBox::critical(this, "**ERROR**", "No input available.");
224 return;
225 }
226 if (fCollectionView->selectedItems().count() == 0) {
227 QMessageBox::critical(this, "**ERROR**", "One or more collection(s) need to linked to the variable(s).");
228 return;
229 }
230
231 if (!basic_check())
232 return;
233
235 return;
236
237 // create the collection index vector
238 QVector<int> idx;
239 QList<QListWidgetItem *> selected = fCollectionView->selectedItems();
240 for (int i=0; i<selected.count(); i++) {
241 idx.push_back(fCollectionView->row(selected[i]));
242 }
243
244 emit add_request(fVarEdit->toPlainText(), idx);
245}
246
247//--------------------------------------------------------------------------
258{
259 QMessageBox::information(this, "Var Help",
260 "Syntax: var <var_name> = <expr>.\n"\
261 "<expr> can contain identifiers defined in the collections.\n"\
262 "An identifier is an addressed variable which is defined\n"\
263 "by a preceeding '$' before the variable name.\n"\
264 "Example: variable sigma -> identifier $sigma.\n"\
265 "Example:\nvar sigSC = pow(abs(pow($sigma,2.0)-pow(0.11,2.0)),0.5)\n"\
266 "\n"\
267 "Python mode (optional):\n"\
268 "Declare the variables with '= python', then provide the\n"\
269 "calculation inside a <python> ... </python> block.\n"\
270 "Collection parameters are available as bare-name lists (one\n"\
271 "value per run); the error of a parameter 'p' is 'pErr'. Names\n"\
272 "that clash with Python (e.g. 'lambda') are reachable via\n"\
273 "par['lambda'] / parErr['lambda']. The script must assign the\n"\
274 "variable and its error (<var_name>Err). Example:\n"\
275 "var sigSC = python\n"\
276 "var sigSCErr = python\n"\
277 "<python>\n"\
278 "import numpy as np\n"\
279 "T = np.array(sigma)\n"\
280 "sigSC = np.sqrt(np.abs(T**2 - 0.11**2))\n"\
281 "sigSCErr = T/np.where(sigSC==0,1,sigSC) * np.array(sigmaErr)\n"\
282 "</python>");
283}
284
285//--------------------------------------------------------------------------
295{
296 // get the selected collection
297 if (fCollectionView->selectedItems().count() == 0) {
298 QMessageBox::critical(this, "**ERROR**", "At least one collection needs to be selected.");
299 return;
300 }
301 if (fCollectionView->selectedItems().count() > 1) {
302 QMessageBox::critical(this, "**ERROR**", "Currently only the vars of a single collection can be shown.");
303 return;
304 }
305 int idx = fCollectionView->currentRow();
306
307 if (idx >= fCollList.count()) {
308 QMessageBox::critical(this, "**ERROR**", QString("Collection idx=%1 > #Collections=%2. This never should have happened.").arg(idx).arg(fCollList.count()));
309 return;
310 }
311
312 PCollInfo info = fCollList[idx];
313
314 PShowVarNameDialog *dialog = new PShowVarNameDialog(info);
315 dialog->show();
316}
317
318//--------------------------------------------------------------------------
332{
333 QString varStr = fVarEdit->toPlainText();
334 bool ok;
335
336 if (varStr.isEmpty()) {
337 QMessageBox::critical(this, "**ERROR**", "No input available.");
338 return false;
339 }
340 if (fCollectionView->selectedItems().count() == 0) {
341 QMessageBox::critical(this, "**ERROR**", "One or more collection(s) need to linked to the variable(s).");
342 return false;
343 }
344
345 // for a <python> block only the part before the block carries mupp 'var'
346 // declarations; the python code itself must not be tokenized as mupp syntax.
347 QString declStr = varStr;
348 int pyIdx = varStr.indexOf("<python>");
349 if (pyIdx != -1)
350 declStr = varStr.left(pyIdx);
351
352 // tokenize variable input
353 QStringList strList = declStr.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
354
355 // check if there are ANY var definitions
356 ok = false;
357 for (int i=0; i<strList.count(); i++) {
358 if (strList[i] == "var") {
359 ok = true;
360 break;
361 }
362 }
363 if (!ok) {
364 QMessageBox::critical(this, "**ERROR**", "<b>NO</b> 'var' definition found.");
365 return false;
366 }
367
368 // check that for each variable, there is also an Err variable present
369 QStringList varNames = collectVarNames(strList, ok);
370 if (!ok) {
371 QMessageBox::critical(this, "**ERROR**", "found 'var' without <var_name>.");
372 return false;
373 }
374 QString name("");
375 if (!hasErrorDef(varNames, name)) {
376 QMessageBox::critical(this, "**ERROR**", QString("found &lt;var_name&gt;=%1 without corresponding %1Err.<br>Both, a variable <b>and</b> its corresponding error variable needs to be defined.").arg(name));
377 return false;
378 }
379
380 return true;
381}
382
383//--------------------------------------------------------------------------
398{
399 QString varStr = fVarEdit->toPlainText();
400
401 // a python block uses bare parameter names (no '$' identifiers); validity of
402 // those references can only be checked when the script is executed.
403 if (varStr.contains("<python>"))
404 return true;
405
406 // collect all identifiers
407 int idx = 0, idxEnd = 0;
408 QStringList varNames;
409 QVector<int> isIdendifier;
410 char ch;
411 bool done;
412 do {
413 idx = varStr.indexOf("$", idx);
414 if (idx > -1) { // found identifier
415 idxEnd = idx+1;
416 done = false;
417 do {
418 ch = varStr[idxEnd].toLatin1();
419 if (isalnum(ch) || (ch == '_'))
420 idxEnd++;
421 else
422 done = true;
423 } while (!done && (idxEnd <= varStr.length()));
424 varNames << varStr.mid(idx+1, idxEnd-idx-1);
425 isIdendifier.push_back(1);
426 idx++;
427 }
428 } while (idx != -1);
429
430 // collect all the variable names
431 idx = 0;
432 do {
433 idx = varStr.indexOf("var ", idx);
434 if (idx > -1) { // found variable
435 idxEnd = varStr.indexOf("=", idx);
436 if (idxEnd == -1) {
437 return false;
438 }
439 varNames << varStr.mid(idx+4, idxEnd-idx-5);
440 isIdendifier.push_back(0);
441 idx++;
442 }
443 } while (idx != -1);
444
445 // make sure that identifier is found in collection if it is not a variable
446 bool isVar = false;
447 bool found = false;
448 QString str;
449 QList<QListWidgetItem *> selColl = fCollectionView->selectedItems();
450 for (int i=0; i<varNames.count(); i++) {
451 if (isIdendifier[i] == 0) // it is a variable, hence nothing to be done
452 continue;
453 str = varNames[i];
454 if (str.endsWith("Err")) {
455 str = str.left(str.indexOf("Err"));
456 }
457 isVar = false;
458 for (int j=i+1; j<varNames.count(); j++) {
459 if ((varNames[j] == str) && isIdendifier[j] == 0) {
460 isVar = true;
461 break;
462 }
463 }
464 if (isVar)
465 continue;
466
467 for (int j=0; j<selColl.count(); j++) {
468 idx = fCollectionView->row(selColl[j]);
469 found = false;
470 for (int k=0; k<fCollList[idx].fVarName.count(); k++) {
471 if (str == fCollList[idx].fVarName[k]) {
472 found = true;
473 break;
474 }
475 }
476 if (!found) {
477 QMessageBox::critical(this, "**ERROR**", QString("Identifier '%1' not present in selected collection '%2'").arg(str).arg(fCollList[idx].fCollName));
478 return false;
479 }
480 }
481 }
482
483 return true;
484}
485
486//--------------------------------------------------------------------------
498QStringList PVarDialog::collectVarNames(QStringList &list, bool &ok)
499{
500 QStringList name;
501
502 ok = true;
503 for (int i=0; i<list.count(); i++) {
504 if (list[i] == "var") {
505 if (i+1 >= list.count()) {
506 ok = false;
507 } else {
508 name << list[i+1];
509 i++;
510 }
511 }
512 }
513
514 return name;
515}
516
517//--------------------------------------------------------------------------
530bool PVarDialog::hasErrorDef(QStringList &varNames, QString &name)
531{
532 QString errStr;
533 for (int i=0; i<varNames.count(); i++) {
534 if (!varNames[i].contains("Err", Qt::CaseSensitive)) { // not an error variable
535 errStr = varNames[i] + "Err";
536 bool ok = false;
537 for (int j=0; j<varNames.count(); j++) {
538 if (varNames[j] == errStr) {
539 ok = true;
540 break;
541 }
542 }
543 if (!ok) {
544 name = varNames[i];
545 return ok;
546 }
547 }
548 }
549
550 return true;
551}
The PShowVarNameDialog class displays variable names from a collection.
Definition PVarDialog.h:64
PShowVarNameDialog(PCollInfo &info)
Constructor for PShowVarNameDialog.
void check()
Slot to validate variable definitions and emit check_request signal.
bool basic_check()
Performs basic validation checks on variable definitions.
void add()
Slot to validate and add variable definitions, emitting add_request signal.
QVector< PCollInfo > fCollList
vector holding all collection information
Definition PVarDialog.h:117
QStringList collectVarNames(QStringList &list, bool &ok)
Collects all variable names from a tokenized string list.
void showVarNames()
Slot to show a dialog displaying variable names from the selected collection.
void help()
Slot to display help information about variable syntax.
std::unique_ptr< QPushButton > fCheck
check button to validate variable definitions
Definition PVarDialog.h:113
std::unique_ptr< QPushButton > fAdd
add button to add variables to mupp GUI
Definition PVarDialog.h:112
std::unique_ptr< QPushButton > fShowVarName
button to show variable names from selected collection
Definition PVarDialog.h:115
std::unique_ptr< QListWidget > fCollectionView
list widget displaying available collections
Definition PVarDialog.h:110
bool hasErrorDef(QStringList &varNames, QString &name)
Checks that each variable has a corresponding error definition.
void add_request(QString varStr, QVector< int > idx)
Signal emitted when user requests to add variable definitions.
std::unique_ptr< QPushButton > fCancel
cancel button to reject the dialog
Definition PVarDialog.h:111
std::unique_ptr< QPushButton > fHelp
help button to display syntax information
Definition PVarDialog.h:114
void check_request(QString varStr, QVector< int > idx)
Signal emitted when user requests to check variable definitions.
std::unique_ptr< QPlainTextEdit > fVarEdit
text editor for variable definitions
Definition PVarDialog.h:109
bool var_consistency_check()
Validates that all identifiers exist in selected collections.
PVarDialog(QVector< PCollInfo > collection_list, bool darkTheme, QWidget *parent=nullptr, Qt::WindowFlags f=Qt::WindowFlags())
Constructor for PVarDialog.
The PCollInfo struct holds collection information.
Definition PVarDialog.h:50
QStringList fVarName
variable names of the given collection
Definition PVarDialog.h:52
QString fCollName
collection name (may include path)
Definition PVarDialog.h:51