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