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