Salome HOME
AutoGIL.hxx has been factorized to KERNEL PythonCppUtils.hxx
[modules/yacs.git] / src / ydfx_gui / YDFXGUISeqInit.cxx
1 // Copyright (C) 2016-2021  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // Author : Anthony Geay (EDF R&D)
20
21 #include "Python.h"
22 #include "YDFXGUISeqInit.hxx"
23
24 #include <QFile>
25 #include <QFrame>
26 #include <QPainter>
27 #include <QTextEdit>
28 #include <QComboBox>
29 #include <QPushButton>
30 #include <QScrollArea>
31 #include <QHBoxLayout>
32 #include <QFileDialog>
33 #include <QApplication>
34
35
36 #include "PythonCppUtils.hxx"
37
38 #include "YDFXGUIWrap.hxx"
39 #include "YDFXGUIPyThreadSaver.hxx"
40
41 #include "YACSEvalPort.hxx"
42 #include "YACSEvalSeqAny.hxx"
43
44 #include <iostream>
45 #include <limits>
46
47 const char YDFXGUIStatus::OK_STR[]="OK !";
48
49 ////////////////////////////
50
51 void YDFXGUIDoubleVectHolder::applyOnInput(YACSEvalInputPort *inp) const
52 {
53   YACSEvalSeqAny *val(new YACSEvalSeqAnyDouble(_vect));
54   inp->setSequenceOfValuesToEval(val);
55   delete val;
56 }
57
58 ////////////////////////////
59
60 YDFXGUISeqSetterP::YDFXGUISeqSetterP(QWidget *parent):QPushButton(parent),_isIn(false)
61 {
62   setText("Open...");
63   connect(this,SIGNAL(clicked()),this,SLOT(selectAFile()));
64 }
65
66 void YDFXGUISeqSetterP::loadState(const QString& state)
67 {
68   _fileName=state;
69   setToolTip(_fileName);
70 }
71
72 QString YDFXGUISeqSetterP::saveState() const
73 {
74   return _fileName;
75 }
76
77 bool YDFXGUISeqSetterP::executeScript(int& sz)
78 {
79   QObject *zeBoss(parent()->parent());
80   YDFXGUISeqLine *zeBossc(qobject_cast<YDFXGUISeqLine *>(zeBoss));
81   if(!zeBossc)
82     return false;
83   //
84   if(_fileName.isEmpty())
85     {
86       Q_EMIT problemDetected(QString("For \"%1\" : no file defined !").arg(zeBossc->getName()));
87       return false;
88     }
89   QFile file(_fileName);
90   if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
91     return false;
92   int i=0;
93   _vect.clear();
94   while(!file.atEnd())
95     {
96       QByteArray line(file.readLine());
97       QString line2(line.data());
98       bool isOK(false);
99       double v(line2.toDouble(&isOK));
100       if(!isOK)
101         {
102           Q_EMIT problemDetected(QString("For \"%1\" : At line %2 it is not a float !").arg(zeBossc->getName()).arg(i));
103           return false;
104         }
105       _vect.push_back(v);
106       i++;
107     }
108   sz=_vect.size();
109   return true;
110 }
111
112 void YDFXGUISeqSetterP::enterEvent(QEvent *event)
113 {
114   _isIn=true;
115   update();
116 }
117
118 void YDFXGUISeqSetterP::leaveEvent(QEvent *event)
119 {
120   _isIn=false;
121   update();
122 }
123
124 void YDFXGUISeqSetterP::paintEvent(QPaintEvent *event)
125 {
126   if(_isIn || _fileName.isEmpty())
127     {
128       QPushButton::paintEvent(event);
129       return ;
130     }
131   QFileInfo fi(_fileName);
132   QString txt(fi.fileName());
133   QRect refRect(rect());
134   QFont ft(font());
135   QFontMetrics fm(ft);
136   QSize refRect2(fm.boundingRect(txt).size());
137   //
138   QPainter painter(this);
139   painter.drawText(QPoint((refRect.width()-refRect2.width())/2,
140                           refRect.height()/2+refRect2.height()/2-fm.descent()),txt);
141 }
142
143 void YDFXGUISeqSetterP::selectAFile()
144 {
145   QFileDialog fd(this,QString("Select a file containing list of floats."));
146   fd.setFileMode(QFileDialog::ExistingFile);
147   if(fd.exec())
148     {
149       QStringList files(fd.selectedFiles());
150       if(files.size()>=1)
151         {
152           _fileName=files[0];
153         }
154       setToolTip(_fileName);
155       update();
156     }
157 }
158
159 ////////////////////////////
160
161 void YDFXGUISeqSetterT::loadState(const QString& state)
162 {
163   setText(state);
164 }
165
166 QString YDFXGUISeqSetterT::saveState() const
167 {
168   return toPlainText();
169 }
170
171 bool YDFXGUISeqSetterT::executeScript(int& sz)
172 {
173   QObject *zeBoss(parent()->parent());
174   YDFXGUISeqLine *zeBossc(qobject_cast<YDFXGUISeqLine *>(zeBoss));
175   if(!zeBossc)
176     return false;
177   //
178   std::string name(zeBossc->getName().toStdString());
179   //
180   static const char TMP_FILENAME[]="/tmp/TMP";
181   std::string txt(toPlainText().toStdString());
182   YDFXGUIPyThreadSaver::SaveContext(QApplication::instance()->thread());
183   {
184     AutoGIL gal;
185     AutoPyRef code(Py_CompileString(txt.c_str(),TMP_FILENAME, Py_file_input));
186     if(code.get() == NULL)
187       {
188         Q_EMIT problemDetected(QString("For \"%1\" : python code is invalid !").arg(zeBossc->getName()));
189         return false;
190       }
191     AutoPyRef context(PyDict_New());
192     PyDict_SetItemString( context, "__builtins__", PyEval_GetBuiltins() );
193     AutoPyRef res(PyEval_EvalCode(code.get(), context, context));
194     PyObject *item(PyDict_GetItemString(context,name.c_str()));
195     //
196     if(!item)
197       {
198         Q_EMIT problemDetected(QString("For \"%1\" : Py var %1 is not defined !").arg(zeBossc->getName()));
199         return false;
200       }
201     if(!PyList_Check(item))
202       {
203         Q_EMIT problemDetected(QString("For \"%1\" : Py var %1 must be a list !").arg(zeBossc->getName()));
204         return false;
205       }
206     sz=PyList_Size(item);
207     _vect.clear() ; _vect.resize(sz);
208     for(int i=0;i<sz;i++)
209       {
210         PyObject *val(PyList_GetItem(item,i));
211         if(!PyFloat_Check(val))
212           {
213             Q_EMIT problemDetected(QString("For \"%1\" : At pos %2 of python list, it is not a float !").arg(zeBossc->getName()).arg(i));
214             return false;
215           }
216         _vect[i]=PyFloat_AS_DOUBLE(val);
217       }
218   }
219   return true;
220 }
221
222 ////////////////////////////
223
224 YDFXGUICombo::YDFXGUICombo(QWidget *parent):QComboBox(parent),_isIn(false)
225 {
226   setFocusPolicy(Qt::TabFocus);
227 }
228
229 QString YDFXGUICombo::getName()
230 {
231   QString ret;
232   YDFXGUISeqLine *parentc(qobject_cast<YDFXGUISeqLine *>(parent()));
233   if(parentc)
234     ret=parentc->getName();
235   return ret;
236 }
237
238 void YDFXGUICombo::enterEvent(QEvent *event)
239 {
240   _isIn=true;
241   update();
242 }
243
244 void YDFXGUICombo::leaveEvent(QEvent *event)
245 {
246   _isIn=false;
247   update();
248 }
249
250 void YDFXGUICombo::paintEvent(QPaintEvent *event)
251 {
252   if(_isIn)
253     {
254       QComboBox::paintEvent(event);
255       return ;
256     }
257   QRect refRect(rect());
258   QFont ft(font());
259   ft.setBold(true);
260   QFontMetrics fm(ft);
261   QSize refRect2(fm.boundingRect(getName()).size());
262   QPainter painter(this);
263   painter.setFont(ft);
264   QPen pen(painter.pen());
265   pen.setColor(Qt::red);
266   painter.setPen(pen);
267   painter.drawText(QPoint((refRect.width()-refRect2.width())/2,refRect.height()/2+refRect2.height()/2-fm.descent()),getName());
268 }
269
270 ////////////////////////////
271
272 YDFXGUISeqSetter::YDFXGUISeqSetter(QWidget *parent, const QString& name):QWidget(parent),_textEdit(0),_push(0),_curType(0)
273 {
274   QVBoxLayout *verticalLayout(new QVBoxLayout(this));
275   _textEdit=new YDFXGUISeqSetterT(this); verticalLayout->addWidget(_textEdit);
276   _push=new YDFXGUISeqSetterP(this); verticalLayout->addWidget(_push);
277   _textEdit->setText(QString("import math\n%1=[math.sqrt(float(elt)+0.) for elt in range(4)]").arg(name));
278   _textEdit->hide();
279   _push->hide();
280 }
281
282 int YDFXGUISeqSetter::loadState(const QString& state)
283 {
284   if(state.isEmpty() || state.size()<3)
285     return 0;
286   QString sw(state.mid(0,3));
287   if(sw=="@0@")
288     { 
289       _push->loadState(state.mid(3));
290       return 0;
291     }
292   if(sw=="@1@")
293     {
294       
295       _textEdit->loadState(state.mid(3));
296       return 1;
297     }
298   return 0;
299 }
300
301 QString YDFXGUISeqSetter::saveState() const
302 {
303   YDFXGUISeqLine *parentc(qobject_cast<YDFXGUISeqLine *>(parent()));
304   if(!parentc)
305     return QString();
306   int pos(parentc->getPositionOfCombo());
307   if(pos==0)
308     {
309       QString ret("@0@");
310       ret+=_push->saveState();
311       return ret;
312     }
313   if(pos==1)
314     {
315       QString ret("@1@");
316       ret+=_textEdit->saveState();
317       return ret;
318     }
319   return QString();
320 }
321
322 QSize YDFXGUISeqSetter::sizeHint() const
323 {
324   QSize sz,sz2(QWidget::sizeHint());
325   if(_curType==0)
326     sz=_push->sizeHint();
327   else
328     sz=_textEdit->sizeHint();
329   sz.rwidth()+=sz2.rwidth();
330   sz.rheight()+=sz2.rheight();
331   return sz;
332 }
333
334 QSize YDFXGUISeqSetter::minimumSizeHint() const
335 {
336   QSize sz,sz2(QWidget::minimumSizeHint());
337   if(_curType==0)
338     sz=_push->minimumSizeHint();
339   else
340     sz=_textEdit->minimumSizeHint();
341   sz.rwidth()+=sz2.rwidth();
342   sz.rheight()+=sz2.rheight();
343   return sz;
344 }
345
346 bool YDFXGUISeqSetter::checkOK(int& sz)
347 {
348   if(_curType==0)
349     return _push->executeScript(sz);
350   else
351     return _textEdit->executeScript(sz);
352 }
353
354 void YDFXGUISeqSetter::applyOnInput(YACSEvalInputPort *inp)
355 {
356   if(_curType==0)
357     _push->applyOnInput(inp);
358   else
359     _textEdit->applyOnInput(inp);
360 }
361
362 void YDFXGUISeqSetter::typeOfAssignmentChanged(int newType)
363 {
364   _curType=newType;
365   if(newType==0)
366     {
367       _textEdit->hide();
368       _push->show();
369       disconnect(_textEdit,SIGNAL(problemDetected(const QString&)),this,SIGNAL(problemDetected(const QString&)));
370       connect(_push,SIGNAL(problemDetected(const QString&)),this,SIGNAL(problemDetected(const QString&)));
371     }
372   else
373     {
374       _textEdit->show();
375       _push->hide();
376       disconnect(_push,SIGNAL(problemDetected(const QString&)),this,SIGNAL(problemDetected(const QString&)));
377       connect(_textEdit,SIGNAL(problemDetected(const QString&)),this,SIGNAL(problemDetected(const QString&)));
378     }
379 }
380
381 ////////////////////////////
382
383 YDFXGUISeqLine::YDFXGUISeqLine(QWidget *parent, YACSEvalInputPort *inp):_combo(0),_setter(0),_inp(inp)
384 {
385   QHBoxLayout *horizontalLayout(new QHBoxLayout(this));
386   _combo=new YDFXGUICombo(this);
387   _combo->insertItem(0,QString("%1 from file").arg(getName()));
388   _combo->insertItem(1,QString("%1 from PyScript").arg(getName()));
389   horizontalLayout->addWidget(_combo);
390   _setter=new YDFXGUISeqSetter(this,getName());
391   connect(_combo,SIGNAL(currentIndexChanged(int)),this,SLOT(typeOfAssignmentChanged(int)));
392   horizontalLayout->addWidget(_setter);
393   _combo->setCurrentIndex(0);
394   Q_EMIT _combo->currentIndexChanged(0);//to be sure to sync widgets
395 }
396
397 void YDFXGUISeqLine::loadState(const QMap<QString,QString>& state)
398 {
399   QString name(getName());
400   QMap<QString,QString>::const_iterator it(state.find(name));
401   if(it==state.end())
402     return ;
403   int pos(_setter->loadState(it.value()));
404   _combo->setCurrentIndex(pos);
405 }
406
407 void YDFXGUISeqLine::saveState(QMap<QString,QString>& state) const
408 {
409   state[getName()]=_setter->saveState();
410 }
411
412 QString YDFXGUISeqLine::getName() const
413 {
414   return QString(_inp->getName().c_str());
415 }
416
417 int YDFXGUISeqLine::getPositionOfCombo() const
418 {
419   return _combo->currentIndex();
420 }
421
422 bool YDFXGUISeqLine::checkOK(int& sz)
423 {
424   return _setter->checkOK(sz);
425 }
426
427 void YDFXGUISeqLine::connectToStatus(YDFXGUIStatus *status)
428 {
429   connect(_setter,SIGNAL(problemDetected(const QString&)),status,SLOT(displayInfo(const QString&)));
430 }
431
432 void YDFXGUISeqLine::applyOnInput()
433 {
434   _setter->applyOnInput(_inp);
435 }
436
437 void YDFXGUISeqLine::typeOfAssignmentChanged(int newType)
438 {
439   _setter->typeOfAssignmentChanged(newType);
440   _setter->updateGeometry();
441   _setter->update();
442 }
443
444 ////////////////////////////
445
446 YDFXGUIStatus::YDFXGUIStatus(QWidget *parent):QWidget(parent)
447 {
448 }
449
450 void YDFXGUIStatus::paintEvent(QPaintEvent *event)
451 {
452   QRect refRect(rect());
453   QFont ft(font());
454   QFontMetrics fm(ft);
455   QSize refRect2(fm.boundingRect(_text).size());
456   //
457   QPainter painter(this);
458   QPen pen(painter.pen());
459   pen.setColor(_text==OK_STR?Qt::green:Qt::red);
460   painter.setPen(pen);
461   painter.drawText(QPoint((refRect.width()-refRect2.width())/2,
462                           refRect.height()/2+refRect2.height()/2-fm.descent()),_text);
463 }
464
465 QSize YDFXGUIStatus::sizeHint() const
466 {
467   QFont ft(font());
468   QFontMetrics fm(ft);
469   QSize ret(fm.boundingRect(_text).size());
470   return ret;
471 }
472
473 QSize YDFXGUIStatus::minimumSizeHint() const
474 {
475   return sizeHint();
476 }
477
478 void YDFXGUIStatus::declareOK(bool isOK)
479 {
480   if(!isOK)
481     return ;
482   _text=OK_STR;
483   updateGeometry();
484   update();
485 }
486
487 void YDFXGUIStatus::displayInfo(const QString& txt)
488 {
489   _text=txt;
490   updateGeometry();
491   update();
492 }
493
494 ////////////////////////////
495
496 YDFXGUISeqInitEff::YDFXGUISeqInitEff(QWidget *parent, YACSEvalYFXWrap *efx):QWidget(parent)
497 {
498   QVBoxLayout *verticalLayout(new QVBoxLayout(this));
499   std::vector< YACSEvalInputPort * > inputs(efx->getFreeInputPorts());
500   foreach(YACSEvalInputPort *input,inputs)
501     {
502       if(!input->isRandomVar())
503         continue;
504       YDFXGUISeqLine *line(new YDFXGUISeqLine(this,input));
505       verticalLayout->addWidget(line);
506       _lines.push_back(line);
507     }
508 }
509
510 void YDFXGUISeqInitEff::loadState(const QMap<QString,QString>& state)
511 {
512   foreach(YDFXGUISeqLine *line,_lines)
513     line->loadState(state);
514 }
515
516 QMap<QString,QString> YDFXGUISeqInitEff::saveState() const
517 {
518   QMap<QString,QString> ret;
519   foreach(YDFXGUISeqLine *line,_lines)
520     line->saveState(ret);
521   return ret;
522 }
523
524 void YDFXGUISeqInitEff::connectToStatus(YDFXGUIStatus *status)
525 {
526   foreach(YDFXGUISeqLine *line,_lines)
527     {
528       line->connectToStatus(status);
529     }
530 }
531
532 void YDFXGUISeqInitEff::assignButtonClicked()
533 {
534   int sz;
535   bool verdict(checkConsistency(sz));
536   Q_EMIT configurationIsOK(verdict);
537 }
538
539 void YDFXGUISeqInitEff::applyOnEFX()
540 {
541   foreach(YDFXGUISeqLine *line,_lines)
542     line->applyOnInput();
543 }
544
545 bool YDFXGUISeqInitEff::checkConsistency(int& sz)
546 {
547   int refSz(std::numeric_limits<int>::max());
548   foreach(YDFXGUISeqLine *line,_lines)
549     {
550       int locSz;
551       if(!line->checkOK(locSz))
552         return false;
553       if(refSz==std::numeric_limits<int>::max())
554         refSz=locSz;
555       if(locSz!=refSz)
556         {
557           Q_EMIT line->setter()->problemDetected(QString("Var %1 does not have the same number of elts than others !").arg(line->getName()));
558           return false;
559         }
560     }
561   sz=refSz;
562   return true;
563 }
564
565 ////////////////////////////
566
567 YDFXGUISeqInit::YDFXGUISeqInit(QWidget *parent, YACSEvalYFXWrap *efx):QWidget(parent),_zeWidget(0)
568 {
569   QVBoxLayout *verticalLayout(new QVBoxLayout(this));
570   QFrame *frame(new QFrame(this));
571   frame->setFrameStyle(QFrame::Panel | QFrame::Sunken);
572   verticalLayout->addWidget(frame);
573   QHBoxLayout *horizontalLayout2(new QHBoxLayout(frame));
574   QScrollArea *sa(new QScrollArea(frame));
575   horizontalLayout2->addWidget(sa);
576   _zeWidget=new YDFXGUISeqInitEff(sa,efx);
577   sa->setWidgetResizable(true);
578   sa->setWidget(_zeWidget);
579   //
580   QHBoxLayout *horizontalLayout(new QHBoxLayout);
581   QSpacerItem *si(new QSpacerItem(40,20,QSizePolicy::Expanding,QSizePolicy::Minimum));
582   YDFXGUIStatus *statusInfo(new YDFXGUIStatus(this));
583   _zeWidget->connectToStatus(statusInfo);
584   connect(_zeWidget,SIGNAL(configurationIsOK(bool)),statusInfo,SLOT(declareOK(bool)));
585   QPushButton *button(new QPushButton(this));
586   button->setText("Check");
587   QPushButton *button2(new QPushButton(this));
588   button2->setText("Assign !");
589   connect(_zeWidget,SIGNAL(configurationIsOK(bool)),button2,SLOT(setEnabled(bool)));
590   connect(button2,SIGNAL(clicked(bool)),_zeWidget,SLOT(applyOnEFX()));
591   connect(button2,SIGNAL(clicked(bool)),this,SIGNAL(assignButtonClicked()));
592   horizontalLayout->addWidget(statusInfo);
593   horizontalLayout->addItem(si);
594   horizontalLayout->addWidget(button);
595   horizontalLayout->addWidget(button2);
596   connect(button,SIGNAL(pressed()),_zeWidget,SLOT(assignButtonClicked()));
597   button2->setEnabled(false);
598   //
599   verticalLayout->addLayout(horizontalLayout);
600 }
601
602 void YDFXGUISeqInit::loadState(const QMap<QString,QString>& state)
603 {
604   _zeWidget->loadState(state);
605 }
606
607 QMap<QString,QString> YDFXGUISeqInit::saveState() const
608 {
609   return _zeWidget->saveState();
610 }