Salome HOME
PyQt4/PyQt5 support.
[modules/smesh.git] / src / Tools / YamsPlug / monYamsPlugDialog.py
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2015  EDF R&D
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20
21 # Modules Python
22 # Modules Eficas
23
24 import os, subprocess
25 from YamsPlugDialog_ui import Ui_YamsPlugDialog
26 from monViewText import MonViewText
27 from qtsalome import *
28
29 class MonYamsPlugDialog(Ui_YamsPlugDialog,QWidget):
30   """
31   """
32   def __init__(self):
33     QWidget.__init__(self)
34     self.setupUi(self)
35     self.connecterSignaux()
36     self.fichierIn=""
37     self.fichierOut=""
38     self.MeshIn=""
39     self.commande=""
40     self.num=1
41     self.__selectedMesh=None
42
43     # complex whith QResources: not used
44     # The icon are supposed to be located in the $SMESH_ROOT_DIR/share/salome/resources/smesh folder,
45     # other solution could be in the same folder than this python module file:
46     # iconfolder=os.path.dirname(os.path.abspath(__file__))
47
48     self.iconfolder=os.environ["SMESH_ROOT_DIR"]+"/share/salome/resources/smesh"
49     #print "monYamsPlugDialog iconfolder",iconfolder
50     icon = QIcon()
51     icon.addFile(os.path.join(self.iconfolder,"select1.png"))
52     self.PB_LoadHyp.setIcon(icon)
53     self.PB_LoadHyp.setToolTip("hypothesis from Salome Object Browser")
54     self.PB_SaveHyp.setIcon(icon)
55     self.PB_SaveHyp.setToolTip("hypothesis to Salome Object Browser")
56     self.PB_MeshSmesh.setIcon(icon)
57     self.PB_MeshSmesh.setToolTip("source mesh from Salome Object Browser")
58     icon = QIcon()
59     icon.addFile(os.path.join(self.iconfolder,"open.png"))
60     self.PB_ParamsFileExplorer.setIcon(icon)
61     self.PB_Load.setIcon(icon)
62     self.PB_Load.setToolTip("hypothesis from file")
63     self.PB_Save.setIcon(icon)
64     self.PB_Save.setToolTip("hypothesis to file")
65     self.PB_MeshFile.setIcon(icon)
66     self.PB_MeshFile.setToolTip("source mesh from a file in disk")
67
68     #Ces parametres ne sont pas remis à rien par le clean
69     self.paramsFile= os.path.abspath(os.path.join(os.environ["HOME"],".yams.dat"))
70     self.LE_ParamsFile.setText(self.paramsFile)
71     self.LE_MeshFile.setText("")
72     self.LE_MeshSmesh.setText("")
73     
74     v1=QDoubleValidator(self)
75     v1.setBottom(0.)
76     #v1.setTop(1000.) #per thousand... only if relative
77     v1.setDecimals(3)
78     self.SP_Tolerance.setValidator(v1)
79     self.SP_Tolerance.titleForWarning="Chordal Tolerance"
80     
81     self.resize(800, 600)
82     self.clean()
83
84   def connecterSignaux(self) :
85     self.PB_Cancel.clicked.connect(self.PBCancelPressed)
86     self.PB_Default.clicked.connect(self.clean)
87     self.PB_Help.clicked.connect(self.PBHelpPressed)
88     self.PB_OK.clicked.connect(self.PBOKPressed)
89     
90     self.PB_Load.clicked.connect(self.PBLoadPressed)
91     self.PB_Save.clicked.connect(self.PBSavePressed)
92     self.PB_LoadHyp.clicked.connect(self.PBLoadHypPressed)
93     self.PB_SaveHyp.clicked.connect(self.PBSaveHypPressed)
94     
95     self.PB_MeshFile.clicked.connect(self.PBMeshFilePressed)
96     self.PB_MeshSmesh.clicked.connect(self.PBMeshSmeshPressed)
97     self.LE_MeshSmesh.returnPressed.connect(self.meshSmeshNameChanged)
98     self.PB_ParamsFileExplorer.clicked.connect(self.setParamsFileName)
99     self.LE_MeshFile.returnPressed.connect(self.meshFileNameChanged)
100     self.LE_ParamsFile.returnPressed.connect(self.paramsFileNameChanged)
101
102   def PBHelpPressed(self):
103     import SalomePyQt
104     sgPyQt = SalomePyQt.SalomePyQt()
105     try :
106       mydir=os.environ["SMESH_ROOT_DIR"]
107     except Exception:
108       QMessageBox.warning(self, "Help", "Help unavailable $SMESH_ROOT_DIR not found")
109       return
110
111     maDoc=mydir+"/share/doc/salome/gui/SMESH/yams/index.html"
112     sgPyQt.helpContext(maDoc,"")
113     
114     #maDoc=mydir+"/share/doc/salome/gui/SMESH/yams/_downloads/mg-surfopt_user_manual.pdf"
115     #command="xdg-open "+maDoc+";"
116     #subprocess.call(command, shell=True)
117
118   def PBOKPressed(self):
119     if not(self.PrepareLigneCommande()):
120       #warning done yet
121       #QMessageBox.warning(self, "Compute", "Command not found")
122       return
123     maFenetre=MonViewText(self,self.commande)
124
125   def enregistreResultat(self):
126     import salome
127     import SMESH
128     from salome.kernel import studyedit
129     from salome.smesh import smeshBuilder
130     smesh = smeshBuilder.New(salome.myStudy)
131     
132     if not os.path.isfile(self.fichierOut):
133       QMessageBox.warning(self, "Compute", "Result file "+self.fichierOut+" not found")
134
135     maStudy=studyedit.getActiveStudy()
136     smesh.SetCurrentStudy(maStudy)
137     (outputMesh, status) = smesh.CreateMeshesFromGMF(self.fichierOut)
138     name=str(self.LE_MeshSmesh.text())
139     initialMeshFile=None
140     initialMeshObject=None
141     if name=="":
142       a=str(self.fichierIn)
143       name=os.path.basename(os.path.splitext(a)[0])
144       initialMeshFile=a
145     else:
146       initialMeshObject=maStudy.FindObjectByName(name ,"SMESH")[0]
147
148     meshname = name+"_YAMS_"+str(self.num)
149     smesh.SetName(outputMesh.GetMesh(), meshname)
150     outputMesh.Compute() #no algorithms message for "Mesh_x" has been computed with warnings: -  global 1D algorithm is missing
151
152     self.editor = studyedit.getStudyEditor()
153     moduleEntry=self.editor.findOrCreateComponent("SMESH","SMESH")
154     HypReMeshEntry = self.editor.findOrCreateItem(
155         moduleEntry, name = "Plugins Hypotheses", icon="mesh_tree_hypo.png") #, comment = "HypoForRemeshing" )
156     
157     monStudyBuilder=maStudy.NewBuilder()
158     monStudyBuilder.NewCommand()
159     newStudyIter=monStudyBuilder.NewObject(HypReMeshEntry)
160     self.editor.setAttributeValue(newStudyIter, "AttributeName", "YAMS Parameters_"+str(self.num))
161     self.editor.setAttributeValue(newStudyIter, "AttributeComment", self.getResumeData(separator=" ; "))
162     
163     SOMesh=maStudy.FindObjectByName(meshname ,"SMESH")[0]
164     
165     if initialMeshFile!=None:
166       newStudyFileName=monStudyBuilder.NewObject(SOMesh)
167       self.editor.setAttributeValue(newStudyFileName, "AttributeName", "meshFile")
168       self.editor.setAttributeValue(newStudyFileName, "AttributeExternalFileDef", initialMeshFile)
169       self.editor.setAttributeValue(newStudyFileName, "AttributeComment", initialMeshFile)
170
171     if initialMeshObject!=None:
172       newLink=monStudyBuilder.NewObject(SOMesh)
173       monStudyBuilder.Addreference(newLink, initialMeshObject)
174
175     newLink=monStudyBuilder.NewObject(SOMesh)
176     monStudyBuilder.Addreference(newLink, newStudyIter)
177
178     if salome.sg.hasDesktop(): salome.sg.updateObjBrowser(0)
179     self.num+=1
180     return True
181
182   def PBSavePressed(self):
183     from datetime import datetime
184     if not(self.PrepareLigneCommande()): return
185     text = "# YAMS hypothesis parameters\n" 
186     text += "# Params for mesh : " +  self.LE_MeshSmesh.text() +"\n"
187     text += datetime.now().strftime("# Date : %d/%m/%y %H:%M:%S\n")
188     text += "# Command : "+self.commande+"\n"
189     text += self.getResumeData(separator="\n")
190     text += "\n\n"
191
192     try:
193       f=open(self.paramsFile,"a")
194     except:
195       QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
196       return
197     try:
198       f.write(text)
199     except:
200       QMessageBox.warning(self, "File", "Unable to write "+self.paramsFile)
201       return
202     f.close()
203
204   def PBSaveHypPressed(self):
205     """save hypothesis in Object Browser"""
206     import salome
207     import SMESH
208     from salome.kernel import studyedit
209     from salome.smesh import smeshBuilder
210     smesh = smeshBuilder.New(salome.myStudy)
211
212     maStudy=studyedit.getActiveStudy()
213     smesh.SetCurrentStudy(maStudy)
214     
215     self.editor = studyedit.getStudyEditor()
216     moduleEntry=self.editor.findOrCreateComponent("SMESH","SMESH")
217     HypReMeshEntry = self.editor.findOrCreateItem(
218         moduleEntry, name = "Plugins Hypotheses", icon="mesh_tree_hypo.png") #, comment = "HypoForRemeshing" )
219     
220     monStudyBuilder=maStudy.NewBuilder()
221     monStudyBuilder.NewCommand()
222     newStudyIter=monStudyBuilder.NewObject(HypReMeshEntry)
223     self.editor.setAttributeValue(newStudyIter, "AttributeName", "YAMS Parameters_"+str(self.num))
224     self.editor.setAttributeValue(newStudyIter, "AttributeComment", self.getResumeData(separator=" ; "))
225     
226     if salome.sg.hasDesktop(): salome.sg.updateObjBrowser(0)
227     self.num+=1
228     return True
229
230   def SP_toStr(self, widget):
231     """only for a QLineEdit widget"""
232     #cr, pos=widget.validator().validate(res, 0) #n.b. "1,3" is acceptable !locale!
233     try:
234       val=float(widget.text())
235     except:
236       QMessageBox.warning(self, widget.titleForWarning, "float value is incorrect: '"+widget.text()+"'")
237       res=str(widget.validator().bottom())
238       widget.setProperty("text", res)
239       return res
240     valtest=widget.validator().bottom()
241     if valtest!=None:
242       if val<valtest:
243         QMessageBox.warning(self, widget.titleForWarning, "float value is under minimum: "+str(val)+" < "+str(valtest))
244         res=str(valtest)
245         widget.setProperty("text", res)
246         return res
247     valtest=widget.validator().top()
248     if valtest!=None:
249       if val>valtest:
250         QMessageBox.warning(self, widget.titleForWarning, "float value is over maximum: "+str(val)+" > "+str(valtest))
251         res=str(valtest)
252         widget.setProperty("text", res)
253         return res    
254     return str(val)
255
256   def getResumeData(self, separator="\n"):
257     text=""
258     for RB in self.GBOptim.findChildren(QRadioButton,):
259       if RB.isChecked()==True:
260         text+="Optimisation="+RB.text()+separator
261         break
262     if self.RB_Absolute.isChecked():
263       text+="Units=absolute"+separator
264     else:
265       text+="Units=relative"+separator
266     v=self.SP_toStr(self.SP_Tolerance)
267     text+="ChordalToleranceDeviation="+v+separator
268     text+="RidgeDetection="+str(self.CB_Ridge.isChecked())+separator
269     text+="SplitEdge="+str(self.CB_SplitEdge.isChecked())+separator
270     text+="PointSmoothing="+str(self.CB_Point.isChecked())+separator
271     text+="GeometricalApproximation="+str(self.SP_Geomapp.value())+separator
272     text+="RidgeAngle="+str(self.SP_Ridge.value())+separator
273     text+="MaximumSize="+str(self.SP_MaxSize.value())+separator
274     text+="MinimumSize="+str(self.SP_MinSize.value())+separator
275     text+="MeshGradation="+str(self.SP_Gradation.value())+separator
276     text+="Verbosity="+str(self.SP_Verbosity.value())+separator
277     text+="Memory="+str(self.SP_Memory.value())+separator
278     return str(text)
279
280   def loadResumeData(self, hypothesis, separator="\n"):
281     text=str(hypothesis)
282     self.clean()
283     for slig in reversed(text.split(separator)):
284       lig=slig.strip()
285       #print "load ResumeData",lig
286       if lig=="": continue #skip blanck lines
287       if lig[0]=="#": break
288       try:
289         tit,value=lig.split("=")
290         if tit=="Optimisation":
291           #no need: exlusives QRadioButton
292           #for RB in self.GBOptim.findChildren(QRadioButton,):
293           #  RB.setChecked(False)
294           for RB in self.GBOptim.findChildren(QRadioButton,):
295             if RB.text()==value :
296               RB.setChecked(True)
297               break
298         if tit=="Units":
299           if value=="absolute":
300             self.RB_Absolute.setChecked(True)
301             self.RB_Relative.setChecked(False)
302           else:
303             self.RB_Absolute.setChecked(False)
304             self.RB_Relative.setChecked(True)
305         if tit=="ChordalToleranceDeviation": self.SP_Tolerance.setProperty("text", float(value))
306         if tit=="RidgeDetection": self.CB_Ridge.setChecked(value=="True")
307         if tit=="SplitEdge": self.CB_SplitEdge.setChecked(value=="True")
308         if tit=="PointSmoothing": self.CB_Point.setChecked(value=="True")
309         if tit=="GeometricalApproximation": self.SP_Geomapp.setProperty("value", float(value))
310         if tit=="RidgeAngle": self.SP_Ridge.setProperty("value", float(value))
311         if tit=="MaximumSize": self.SP_MaxSize.setProperty("value", float(value))
312         if tit=="MinimumSize": self.SP_MinSize.setProperty("value", float(value))
313         if tit=="MeshGradation": self.SP_Gradation.setProperty("value", float(value))
314         if tit=="Verbosity": self.SP_Verbosity.setProperty("value", int(float(value)))
315         if tit=="Memory": self.SP_Memory.setProperty("value", float(value))
316       except:
317         QMessageBox.warning(self, "load YAMS Hypothesis", "Problem on '"+lig+"'")
318
319   def PBLoadPressed(self):
320     """load last hypothesis saved in tail of file"""
321     try:
322       f=open(self.paramsFile,"r")
323     except:
324       QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
325       return
326     try:
327       text=f.read()
328     except:
329       QMessageBox.warning(self, "File", "Unable to read "+self.paramsFile)
330       return
331     f.close()
332     self.loadResumeData(text, separator="\n")
333
334   def PBLoadHypPressed(self):
335     """load hypothesis saved in Object Browser"""
336     #QMessageBox.warning(self, "load Object Browser YAMS hypothesis", "TODO")
337     import salome
338     from salome.kernel import studyedit
339     from salome.smesh.smeshstudytools import SMeshStudyTools
340     from salome.gui import helper as guihelper
341     from omniORB import CORBA
342
343     mySObject, myEntry = guihelper.getSObjectSelected()
344     if CORBA.is_nil(mySObject) or mySObject==None:
345       QMessageBox.critical(self, "Hypothese", "select an Object Browser YAMS hypothesis")
346       return
347     
348     text=mySObject.GetComment()
349     
350     #a verification
351     if "Optimisation=" not in text:
352       QMessageBox.critical(self, "Load Hypothese", "Object Browser selection not a YAMS Hypothesis")
353       return
354     self.loadResumeData(text, separator=" ; ")
355     return
356     
357   def PBCancelPressed(self):
358     self.close()
359
360   def PBMeshFilePressed(self):
361     fd = QFileDialog(self, "select an existing Mesh file", self.LE_MeshFile.text(), "Mesh-Files (*.mesh);;All Files (*)")
362     if fd.exec_():
363       infile = fd.selectedFiles()[0]
364       self.LE_MeshFile.setText(infile)
365       self.fichierIn=unicode(infile).encode("latin-1")
366       self.MeshIn=""
367       self.LE_MeshSmesh.setText("")
368
369   def setParamsFileName(self):
370     fd = QFileDialog(self, "select a file", self.LE_ParamsFile.text(), "dat Files (*.dat);;All Files (*)")
371     if fd.exec_():
372       infile = fd.selectedFiles()[0]
373       self.LE_ParamsFile.setText(infile)
374       self.paramsFile=unicode(infile).encode("latin-1")
375
376   def meshFileNameChanged(self):
377     self.fichierIn=str(self.LE_MeshFile.text())
378     #print "meshFileNameChanged", self.fichierIn
379     if os.path.exists(self.fichierIn): 
380       self.__selectedMesh=None
381       self.MeshIn=""
382       self.LE_MeshSmesh.setText("")
383       return
384     QMessageBox.warning(self, "Mesh file", "File doesn't exist")
385
386   def meshSmeshNameChanged(self):
387     """only change by GUI mouse selection, otherwise clear"""
388     self.__selectedMesh = None
389     self.MeshIn=""
390     self.LE_MeshSmesh.setText("")
391     self.fichierIn=""
392     return
393
394   def paramsFileNameChanged(self):
395     self.paramsFile=self.LE_ParamsFile.text()
396
397   def PBMeshSmeshPressed(self):
398     from omniORB import CORBA
399     import salome
400     from salome.kernel import studyedit
401     from salome.smesh.smeshstudytools import SMeshStudyTools
402     from salome.gui import helper as guihelper
403     from salome.smesh import smeshBuilder
404     smesh = smeshBuilder.New(salome.myStudy)
405
406     mySObject, myEntry = guihelper.getSObjectSelected()
407     if CORBA.is_nil(mySObject) or mySObject==None:
408       #QMessageBox.critical(self, "Mesh", "select an input mesh")
409       self.LE_MeshSmesh.setText("")
410       self.MeshIn=""
411       self.LE_MeshFile.setText("")
412       self.fichierIn=""
413       return
414     self.smeshStudyTool = SMeshStudyTools()
415     try:
416       self.__selectedMesh = self.smeshStudyTool.getMeshObjectFromSObject(mySObject)
417     except:
418       QMessageBox.critical(self, "Mesh", "select an input mesh")
419       return
420     if CORBA.is_nil(self.__selectedMesh):
421       QMessageBox.critical(self, "Mesh", "select an input mesh")
422       return
423     myName = mySObject.GetName()
424     #print "MeshSmeshNameChanged", myName
425     self.MeshIn=myName
426     self.LE_MeshSmesh.setText(myName)
427     self.LE_MeshFile.setText("")
428     self.fichierIn=""
429
430   def prepareFichier(self):
431     self.fichierIn="/tmp/ForSurfOpt_"+str(self.num)+".meshb"
432     self.__selectedMesh.ExportGMF(self.__selectedMesh, self.fichierIn, True)
433
434   def PrepareLigneCommande(self):
435     if self.fichierIn=="" and self.MeshIn=="":
436       QMessageBox.critical(self, "Mesh", "select an input mesh")
437       return False
438     if self.__selectedMesh!=None: self.prepareFichier()
439     if not (os.path.isfile(self.fichierIn)):
440       QMessageBox.critical(self, "File", "unable to read GMF Mesh in "+str(self.fichierIn))
441       return False
442     
443     self.commande="mg-surfopt.exe"
444     
445     for obj in self.GBOptim.findChildren(QRadioButton,):
446       try:
447         if obj.isChecked():
448           self.style=obj.objectName().remove(0,3)
449           self.style.replace("_","-")
450           break
451       except:
452         pass
453       
454     style = unicode(self.style).encode("latin-1")
455     # Translation of old Yams options to new MG-SurfOpt options
456     if   style == "0" :
457       self.commande+= " --optimisation only"
458     elif style == "2" :
459       self.commande+= " --Hausdorff_like yes"
460     elif style == "-1":
461       self.commande+= " --enrich no"
462     elif style == "-2":
463       self.commande+= " --Hausdorff_like yes --enrich no"
464     elif style == "U" :
465       self.commande+= " --uniform_flat_subdivision yes"
466     elif style == "S" :
467       self.commande+= " --sand_paper yes"
468     elif style == "G" :
469       self.commande+= " -O G"  # This option has not been updated to the new option style yet
470
471     deb=os.path.splitext(self.fichierIn)
472     self.fichierOut=deb[0] + "_output.meshb"
473     
474     tolerance=self.SP_toStr(self.SP_Tolerance)
475     if not self.RB_Absolute.isChecked():
476       tolerance+="r"  
477     self.commande+=" --chordal_error %s"%tolerance
478     
479     if self.CB_Ridge.isChecked()    == False : self.commande+=" --compute_ridges no"
480     if self.CB_Point.isChecked()    == False : self.commande+=" --optimisation no"
481     if self.CB_SplitEdge.isChecked()== True  : self.commande+=" --element_order quadratic"
482     if self.SP_Geomapp.value()      != 15.0  : self.commande+=" --geometric_approximation_angle %f"%self.SP_Geomapp.value()
483     if self.SP_Ridge.value()        != 45.0  : self.commande+=" --ridge_angle %f"%self.SP_Ridge.value()
484     if self.SP_MaxSize.value()      != 100   : self.commande+=" --max_size %f"   %self.SP_MaxSize.value()
485     if self.SP_MinSize.value()      != 5     : self.commande+=" --min_size %f"   %self.SP_MinSize.value()
486     if self.SP_Gradation.value()    != 1.3   : self.commande+=" --gradation %f"  %self.SP_Gradation.value()
487     if self.SP_Memory.value()       != 0     : self.commande+=" --max_memory %d" %self.SP_Memory.value()
488     if self.SP_Verbosity.value()    != 3     : self.commande+=" --verbose %d" %self.SP_Verbosity.value()
489
490     self.commande+=" --in "  + self.fichierIn
491     self.commande+=" --out " + self.fichierOut
492     
493     print self.commande
494     return True
495
496   def clean(self):
497     self.RB_0.setChecked(True)
498     #no need: exlusives QRadioButton
499     #self.RB_G.setChecked(False)
500     #self.RB_U.setChecked(False)
501     #self.RB_S.setChecked(False)
502     #self.RB_2.setChecked(False)
503     #self.RB_1.setChecked(False)
504     self.RB_Relative.setChecked(True)
505     #no need: exlusives QRadioButton
506     #self.RB_Absolute.setChecked(False)
507     self.SP_Tolerance.setProperty("text", "0.001")
508     self.SP_Geomapp.setProperty("value", 15.0)
509     self.SP_Ridge.setProperty("value", 45.0)
510     self.SP_Gradation.setProperty("value", 1.3)
511     self.CB_Ridge.setChecked(True)
512     self.CB_Point.setChecked(True)
513     self.CB_SplitEdge.setChecked(False)
514     self.SP_MaxSize.setProperty("value", 100)
515     self.SP_MinSize.setProperty("value", 5)
516     self.SP_Verbosity.setProperty("value", 3)
517     self.SP_Memory.setProperty("value", 0)
518     self.PBMeshSmeshPressed()
519     self.TWOptions.setCurrentIndex(0) # Reset current active tab to the first tab
520
521 __dialog=None
522 def getDialog():
523   """
524   This function returns a singleton instance of the plugin dialog.
525   It is mandatory in order to call show without a parent ...
526   """
527   global __dialog
528   if __dialog is None:
529     __dialog = MonYamsPlugDialog()
530   else :
531     __dialog.clean()
532   return __dialog
533
534 #
535 # ==============================================================================
536 # Basic use cases and unit test functions
537 # ==============================================================================
538 #
539 def TEST_MonYamsPlugDialog():
540   import sys
541   from qtsalome import QApplication
542   app = QApplication(sys.argv)
543   app.lastWindowClosed.connect(app.quit)
544
545   dlg=MonYamsPlugDialog()
546   dlg.show()
547   sys.exit(app.exec_())
548
549 if __name__ == "__main__":
550   TEST_MonYamsPlugDialog()
551   pass