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