Salome HOME
more load/save hypothesis
[modules/smesh.git] / src / Tools / MGCleanerPlug / MGCleanerMonPlugDialog.py
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2013  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.
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 MGCleanerPlugDialog import Ui_MGCleanerPlugDialog
26 from MGCleanerMonViewText import MGCleanerMonViewText
27 from PyQt4.QtGui import *
28 from PyQt4.QtCore import *
29
30
31 class MGCleanerMonPlugDialog(Ui_MGCleanerPlugDialog,QWidget):
32   """
33   """
34   def __init__(self):
35     QWidget.__init__(self)
36     self.setupUi(self)
37     self.connecterSignaux()
38     self.fichierIn=""
39     self.fichierOut=""
40     self.MeshIn=""
41     self.num=1
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 "MGCleanerMonPlugDialog 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"],".MGCleaner.dat"))
70     self.LE_ParamsFile.setText(self.paramsFile)
71     self.LE_MeshFile.setText("")
72     self.LE_MeshSmesh.setText("")
73     self.resize(800, 500)
74     self.clean()
75
76   def connecterSignaux(self) :
77     self.connect(self.PB_Cancel,SIGNAL("clicked()"),self.PBCancelPressed)
78     self.connect(self.PB_Default,SIGNAL("clicked()"),self.clean)
79     self.connect(self.PB_Help,SIGNAL("clicked()"),self.PBHelpPressed)
80     self.connect(self.PB_OK,SIGNAL("clicked()"),self.PBOKPressed)
81     
82     self.connect(self.PB_Load,SIGNAL("clicked()"),self.PBLoadPressed)
83     self.connect(self.PB_Save,SIGNAL("clicked()"),self.PBSavePressed)
84     self.connect(self.PB_LoadHyp,SIGNAL("clicked()"),self.PBLoadHypPressed)
85     self.connect(self.PB_SaveHyp,SIGNAL("clicked()"),self.PBSaveHypPressed)
86     
87     self.connect(self.PB_MeshFile,SIGNAL("clicked()"),self.PBMeshFilePressed)
88     self.connect(self.PB_MeshSmesh,SIGNAL("clicked()"),self.PBMeshSmeshPressed)
89     self.connect(self.PB_ParamsFileExplorer,SIGNAL("clicked()"),self.setParamsFileName)
90     self.connect(self.LE_MeshFile,SIGNAL("returnPressed()"),self.meshFileNameChanged)
91     self.connect(self.LE_ParamsFile,SIGNAL("returnPressed()"),self.paramsFileNameChanged)
92
93     #QtCore.QObject.connect(self.checkBox, QtCore.SIGNAL("stateChanged(int)"), self.change) 
94     self.connect(self.CB_FillHoles,SIGNAL("stateChanged(int)"),self.SP_MinHoleSize.setEnabled)
95     self.connect(self.CB_ComputedToleranceDisplacement,SIGNAL("stateChanged(int)"),self.SP_ToleranceDisplacement.setDisabled)
96     self.connect(self.CB_ComputedResolutionLength,SIGNAL("stateChanged(int)"),self.SP_ResolutionLength.setDisabled)
97     self.connect(self.CB_ComputedOverlapDistance,SIGNAL("stateChanged(int)"),self.SP_OverlapDistance.setDisabled)
98         
99   def PBHelpPressed(self):
100     try :
101       mydir=os.environ["SMESH_ROOT_DIR"]
102     except Exception:
103       QMessageBox.warning( self, "Help unavailable $SMESH_ROOT_DIR not found")
104     maDoc=mydir+"/share/doc/salome/gui/SMESH/MGCleaner/_downloads/mg-cleaner_user_manual.pdf"
105     command="xdg-open "+maDoc+";"
106     subprocess.call(command, shell=True)
107
108
109   def PBOKPressed(self):
110     if not(self.PrepareLigneCommande()): return
111     maFenetre=MGCleanerMonViewText(self, self.commande)
112     #print "compute Pressed"
113     if os.path.isfile(self.fichierOut): self.enregistreResultat()
114
115   def enregistreResultat(self):
116     import smesh
117     import SMESH
118     import salome
119     from salome.kernel import studyedit
120
121     #print "enregistreResultat"
122     maStudy=studyedit.getActiveStudy()
123     smesh.SetCurrentStudy(maStudy)
124     (outputMesh, status) = smesh.CreateMeshesFromGMF(self.fichierOut)
125     name=str(self.LE_MeshSmesh.text())
126     if name=="":
127       #print "name new MESH",self.LE_MeshFile.text()
128       a=str(self.fichierIn)
129       name=os.path.basename(os.path.splitext(a)[0])
130
131     meshname = name+"_MGC_"+str(self.num)
132     smesh.SetName(outputMesh.GetMesh(), meshname)
133     outputMesh.Compute() #no algorithms message for "Mesh_x" has been computed with warnings: -  global 1D algorithm is missing
134
135     self.editor = studyedit.getStudyEditor()
136     moduleEntry=self.editor.findOrCreateComponent("SMESH","SMESH")
137     HypReMeshEntry = self.editor.findOrCreateItem(
138         moduleEntry, name = "MGCleaner Hypotheses", icon="mesh_tree_algo.png") #, comment = "HypoForRemeshing" )
139     
140     monStudyBuilder=maStudy.NewBuilder()
141     monStudyBuilder.NewCommand()
142     newStudyIter=monStudyBuilder.NewObject(HypReMeshEntry)
143     aNameAttrib=monStudyBuilder.FindOrCreateAttribute(newStudyIter,"AttributeName")
144     hypoName = "HypoMGC_"+str(self.num)
145     aNameAttrib.SetValue(hypoName)
146     aCommentAttrib=monStudyBuilder.FindOrCreateAttribute(newStudyIter,"AttributeComment")
147     aCommentAttrib.SetValue(self.getResumeData(separator=" ; "))
148     
149     SOMesh=maStudy.FindObjectByName(meshname ,"SMESH")[0]
150     newLink=monStudyBuilder.NewObject(SOMesh)
151     monStudyBuilder.Addreference(newLink, newStudyIter);
152
153     if salome.sg.hasDesktop(): salome.sg.updateObjBrowser(0)
154     self.num+=1
155     return True
156
157   def PBSavePressed(self, NomHypo=False):
158     from datetime import datetime
159     if NomHypo: 
160       text = "# Params for Hypothese : Hypo_MGCleaner_"+str(self.num - 1)+"\n"
161     else:
162       text = "# Save intermediate params \n" 
163     text += "# Params for mesh : " +  self.LE_MeshSmesh.text() +"\n"
164     text += datetime.now().strftime("# Date : %d/%m/%y %H:%M:%S\n")
165     text += self.getResumeData(separator="\n")
166     text += "\n\n"
167
168     try:
169         f=open(self.paramsFile,"a")
170     except:
171         QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
172         return
173     try:
174         f.write(text)
175     except:
176         QMessageBox.warning(self, "File", "Unable to write "+self.paramsFile)
177         return
178     f.close()
179
180   def PBSaveHypPressed(self):
181     """save hypothesis in Object Browser"""
182     #QMessageBox.warning(self, "save Object Browser MGCleaner Hypothesis", "TODO")
183     
184     import smesh
185     import SMESH
186     import salome
187     from salome.kernel import studyedit
188
189     maStudy=studyedit.getActiveStudy()
190     smesh.SetCurrentStudy(maStudy)
191     
192     self.editor = studyedit.getStudyEditor()
193     moduleEntry=self.editor.findOrCreateComponent("SMESH","SMESH")
194     HypReMeshEntry = self.editor.findOrCreateItem(
195         moduleEntry, name = "MGCleaner Hypotheses", icon="mesh_tree_algo.png") #, comment = "HypoForRemeshing" )
196     
197     monStudyBuilder=maStudy.NewBuilder()
198     monStudyBuilder.NewCommand()
199     newStudyIter=monStudyBuilder.NewObject(HypReMeshEntry)
200     aNameAttrib=monStudyBuilder.FindOrCreateAttribute(newStudyIter,"AttributeName")
201     hypoName = "HypoMGC_"+str(self.num)
202     aNameAttrib.SetValue(hypoName)
203     aCommentAttrib=monStudyBuilder.FindOrCreateAttribute(newStudyIter,"AttributeComment")
204     aCommentAttrib.SetValue(self.getResumeData(separator=" ; "))
205     
206     if salome.sg.hasDesktop(): salome.sg.updateObjBrowser(0)
207     self.num+=1
208     return True
209
210   def getResumeData(self, separator="\n"):
211     text=""
212     if self.RB_Fix1.isChecked():
213       CheckOrFix="fix1pass"
214     else:
215       if self.RB_Fix2.isChecked():
216         CheckOrFix="fix2pass"
217       else:
218         CheckOrFix="check"
219     text+="CheckOrFix=" + CheckOrFix+separator
220     text+="PreserveTopology=" + str(self.CB_PreserveTopology.isChecked())+separator
221     text+="FillHoles=" + str(self.CB_FillHoles.isChecked())+separator
222     text+="MinHoleSize=" + str(self.SP_MinHoleSize.value())+separator
223     text+="ComputedToleranceDisplacement=" + str(self.CB_ComputedToleranceDisplacement.isChecked())+separator
224     text+="ToleranceDisplacement=" + str(self.SP_ToleranceDisplacement.value())+separator
225     text+="ComputedResolutionLength=" + str(self.CB_ComputedResolutionLength.isChecked())+separator
226     text+="ResolutionLength=" + str(self.SP_ResolutionLength.value())+separator
227     text+="FoldingAngle=" + str(self.SP_FoldingAngle.value())+separator
228     text+="RemeshPlanes=" + str(self.CB_RemeshPlanes.isChecked())+separator
229     text+="ComputedOverlapDistance=" + str(self.CB_ComputedOverlapDistance.isChecked())+separator
230     text+="OverlapDistance=" + str(self.SP_OverlapDistance.value())+separator
231     text+="OverlapAngle=" + str(self.SP_OverlapAngle.value())+separator
232     text+="Verbosity=" + str(self.SP_Verbosity.value())+separator
233     return text
234
235   def loadResumeData(self, hypothesis, separator="\n"):
236     text=str(hypothesis)
237     self.clean()
238     for slig in reversed(text.split(separator)):
239       lig=slig.strip()
240       #print "load ResumeData",lig
241       if lig=="": continue #skip blanck lines
242       if lig[0]=="#": break
243       try:
244         tit,value=lig.split("=")
245         if tit=="CheckOrFix":
246           self.RB_Fix1.setChecked(False)
247           self.RB_Fix2.setChecked(False)
248           self.RB_Check.setChecked(False)
249           if value=="fix1pass": self.RB_Fix1.setChecked(True)
250           if value=="fix2pass": self.RB_Fix2.setChecked(True)
251           if value=="check": self.RB_Check.setChecked(True)
252         if tit=="PreserveTopology": self.CB_PreserveTopology.setChecked(value=="True")
253         if tit=="FillHoles": self.CB_FillHoles.setChecked(value=="True")
254         if tit=="MinHoleSize": self.SP_MinHoleSize.setProperty("value", float(value))
255         if tit=="ComputedToleranceDisplacement": self.CB_ComputedToleranceDisplacement.setChecked(value=="True")
256         if tit=="ToleranceDisplacement": self.SP_ToleranceDisplacement.setProperty("value", float(value))
257         if tit=="ComputedResolutionLength": self.CB_ComputedResolutionLength.setChecked(value=="True")
258         if tit=="ResolutionLength": self.SP_ResolutionLength.setProperty("value", float(value))
259         if tit=="FoldingAngle": self.SP_FoldingAngle.setProperty("value", float(value))
260         if tit=="RemeshPlanes": self.CB_RemeshPlanes.setChecked(value=="True")
261         if tit=="ComputedOverlapDistance": self.CB_ComputedOverlapDistance.setChecked(value=="True")
262         if tit=="OverlapDistance": self.SP_OverlapDistance.setProperty("value", float(value))
263         if tit=="OverlapAngle": self.SP_OverlapAngle.setProperty("value", float(value))
264         if tit=="Verbosity": self.SP_Verbosity.setProperty("value", int(float(value)))
265       except:
266         QMessageBox.warning(self, "load MGCleaner Hypothesis", "Problem on '"+lig+"'")
267
268   def PBLoadPressed(self):
269     """load last hypothesis saved in tail of file"""
270     try:
271         f=open(self.paramsFile,"r")
272     except :
273         QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
274         return
275     try:
276         text=f.read()
277     except :
278         QMessageBox.warning(self, "File", "Unable to read "+self.paramsFile)
279         return
280     f.close()
281     self.loadResumeData(text, separator="\n")
282
283   def PBLoadHypPressed(self):
284     """load hypothesis saved in Object Browser"""
285     #QMessageBox.warning(self, "load Object Browser MGCleaner hypothesis", "TODO")
286     import salome
287     from salome.kernel import studyedit
288     from salome.smesh.smeshstudytools import SMeshStudyTools
289     from salome.gui import helper as guihelper
290     from omniORB import CORBA
291
292     mySObject, myEntry = guihelper.getSObjectSelected()
293     if CORBA.is_nil(mySObject) or mySObject==None:
294       QMessageBox.critical(self, "Hypothese", "select an Object Browser MGCleaner hypothesis")
295       return
296     
297     #for i in dir(mySObject): print "dir mySObject",i
298     #print "GetAllAttributes",mySObject.GetAllAttributes()
299     #print "GetComment",mySObject.GetComment()
300     #print "GetName",mySObject.GetName()
301     
302     #could be renamed...
303     #if mySObject.GetFather().GetName()!="MGCleaner Hypotheses":
304     #  QMessageBox.critical(self, "Hypothese", "not a child of MGCleaner Hypotheses")
305     #  return
306     
307     text=mySObject.GetComment()
308     
309     #a verification
310     if "CheckOrFix=" not in text:
311       QMessageBox.critical(self, "Load Hypothese", "Object Browser selection not a MGCleaner Hypothesis")
312       return
313     self.loadResumeData(text, separator=" ; ")
314     return
315     
316   def PBCancelPressed(self):
317     self.close()
318
319   def PBMeshFilePressed(self):
320     fd = QFileDialog(self, "select an existing Mesh file", self.LE_MeshFile.text(), "Mesh-Files (*.mesh);;All Files (*)")
321     if fd.exec_():
322       infile = fd.selectedFiles()[0]
323       self.LE_MeshFile.setText(infile)
324       self.fichierIn=infile.toLatin1()
325       self.MeshIn=""
326       self.LE_MeshSmesh.setText("")
327
328   def setParamsFileName(self):
329     fd = QFileDialog(self, "select a file", self.LE_ParamsFile.text(), "dat Files (*.dat);;All Files (*)")
330     if fd.exec_():
331       infile = fd.selectedFiles()[0]
332       self.LE_ParamsFile.setText(infile)
333       self.paramsFile=infile.toLatin1()
334
335   def meshFileNameChanged(self):
336     self.fichierIn=str(self.LE_MeshFile.text())
337     #print "meshFileNameChanged", self.fichierIn
338     if os.path.exists(self.fichierIn): 
339       self.MeshIn=""
340       self.LE_MeshSmesh.setText("")
341       return
342     QMessageBox.warning( self, "Unknown File", "File doesn't exist")
343
344   def paramsFileNameChanged(self):
345     self.paramsFile=self.LE_ParamsFile.text()
346
347   def PBMeshSmeshPressed(self):
348     import salome
349     import smesh
350     from salome.kernel import studyedit
351     from salome.smesh.smeshstudytools import SMeshStudyTools
352     from salome.gui import helper as guihelper
353     from omniORB import CORBA
354
355     mySObject, myEntry = guihelper.getSObjectSelected()
356     if CORBA.is_nil(mySObject) or mySObject==None:
357       QMessageBox.critical(self, "Mesh", "select an input mesh")
358       return
359     self.smeshStudyTool = SMeshStudyTools()
360     try:
361       self.__selectedMesh = self.smeshStudyTool.getMeshObjectFromSObject(mySObject)
362     except:
363       QMessageBox.critical(self, "Mesh", "select an input mesh")
364       return
365     if CORBA.is_nil(self.__selectedMesh):
366       QMessageBox.critical(self, "Mesh", "select an input mesh")
367       return
368     myName = mySObject.GetName()
369     #print "MeshSmeshNameChanged", myName
370     self.MeshIn=myName
371     self.LE_MeshSmesh.setText(myName)
372     self.LE_MeshFile.setText("")
373     self.fichierIn=""
374
375   def prepareFichier(self):
376     self.fichierIn="/tmp/ForMGCleaner_"+str(self.num)+".mesh"
377     import SMESH
378     self.__selectedMesh.ExportGMF(self.__selectedMesh,self.fichierIn, True)
379
380   def PrepareLigneCommande(self):
381     """
382     #use doc examples of mg-cleaner:
383     ls -al /data/tmplgls/salome/prerequis/install/COMMON_64/MeshGems-1.0/bin
384     source /data/tmplgls/salome/prerequis/install/LICENSE/dlim8.var.sh
385     export PATH=/data/tmplgls/salome/prerequis/install/COMMON_64/MeshGems-1.0/bin/Linux_64:$PATH
386     cp -r /data/tmplgls/salome/prerequis/install/COMMON_64/MeshGems-1.0/examples .
387     cd examples
388     mg-cleaner.exe --help
389     mg-cleaner.exe --in case7.mesh --out case7-test.mesh --check
390     mg-cleaner.exe case7.mesh case7-fix.mesh --fix
391     mg-cleaner.exe --in Porsche.mesh --out Porsche-test.mesh --check
392     mg-cleaner.exe --in Porsche.mesh --out Porschefix.mesh --fix
393     mg-cleaner.exe --in Porsche.mesh --out PorscheNewfix.mesh --fix --resolution_length 0.03
394     """
395     
396     #self.commande="mg-cleaner.exe --in " + self.fichierIn + " --out " + self.fichierOut + " --fix2pass" 
397     #return True
398     if self.fichierIn=="" and self.MeshIn=="" :
399       QMessageBox.critical(self, "Mesh", "select an input mesh")
400       return False
401     if self.MeshIn!="" : self.prepareFichier()
402     if not (os.path.isfile(self.fichierIn)):
403       QMessageBox.critical(self, "File", "unable to read GMF Mesh in "+str(self.fichierIn))
404       return False
405     
406     self.commande="mg-cleaner.exe"
407     verbosity=str(self.SP_Verbosity.value())
408     self.commande+=" --verbose " + verbosity
409     self.commande+=" --in " + self.fichierIn
410     #print "self.fichierIn",self.fichierIn,type(self.fichierIn)
411     deb=os.path.splitext(str(self.fichierIn))
412     self.fichierOut=deb[0] + "_fix.mesh"
413     self.commande+=" --out "+self.fichierOut
414     if self.RB_Fix1.isChecked():
415       self.commande+=" --fix1pass"
416     else:
417       if self.RB_Fix2.isChecked():
418         self.commande+=" --fix2pass"
419       else:
420         self.commande+=" --check"
421     if self.CB_PreserveTopology.isChecked():
422       self.commande+=" --topology respect"
423     else:
424       self.commande+=" --topology ignore"
425     if self.CB_FillHoles.isChecked(): #no fill holes default
426       self.commande+=" --min_hole_size " + str(self.SP_MinHoleSize.value())
427     if not self.CB_ComputedToleranceDisplacement.isChecked(): #computed default
428       self.commande+=" --tolerance_displacement " + str(self.SP_ToleranceDisplacement.value())
429     if not self.CB_ComputedResolutionLength.isChecked(): #computed default
430       self.commande+=" --tolerance_displacement " + str(self.SP_ResolutionLength.value())
431     self.commande+=" --folding_angle " + str(self.SP_FoldingAngle.value())
432     if self.CB_RemeshPlanes.isChecked(): #no remesh default
433       self.commande+=" --remesh_planes"
434     if not self.CB_ComputedOverlapDistance.isChecked(): #computed default
435       self.commande+=" --overlap_distance " + str(self.SP_OverlapDistance.value())
436     self.commande+=" --overlap_angle " + str(self.SP_OverlapAngle.value())
437     return True
438     
439   def clean(self):
440     self.RB_Check.setChecked(False)
441     self.RB_Fix1.setChecked(False)
442     self.RB_Fix2.setChecked(True)
443     self.CB_PreserveTopology.setChecked(False)
444     self.CB_FillHoles.setChecked(False)
445     self.CB_RemeshPlanes.setChecked(False)
446     
447     self.SP_MinHoleSize.setProperty("value", 0)
448     self.SP_ToleranceDisplacement.setProperty("value", 0)
449     self.SP_ResolutionLength.setProperty("value", 0)
450     self.SP_FoldingAngle.setProperty("value", 15)
451     self.SP_OverlapDistance.setProperty("value", 0)
452     self.SP_OverlapAngle.setProperty("value", 15)
453     self.SP_Verbosity.setProperty("value", 3)
454     
455     self.CB_ComputedToleranceDisplacement.setChecked(True)
456     self.CB_ComputedResolutionLength.setChecked(True)
457     self.CB_ComputedOverlapDistance.setChecked(True)
458
459 __dialog=None
460 def getDialog():
461   """
462   This function returns a singleton instance of the plugin dialog.
463   c est obligatoire pour faire un show sans parent...
464   """
465   global __dialog
466   if __dialog is None:
467       __dialog = MGCleanerMonPlugDialog()
468   #else :
469   #   __dialog.clean()
470   return __dialog
471
472
473 #
474 # ==============================================================================
475 # For memory
476 # ==============================================================================
477 #
478 def TEST_standalone():
479   """
480   works only if a salome is launched yet with a study loaded
481   to launch standalone python do:
482   /export/home/wambeke/2013/V6_main_MGC_CO6.4_64/APPLI/runSession
483   python
484   or (do not works)
485   python /export/home/wambeke/2013/V6_main_MGC_CO6.4_64/INSTALL/SMESH/share/salome/plugins/smesh/MGCleanerMonPlugDialog.py
486   """
487   import salome
488   import smesh
489   import SMESH
490   from salome.kernel import studyedit
491   salome.salome_init()
492   maStudy=studyedit.getActiveStudy()
493   #etc...a mano...
494
495 #
496 # ==============================================================================
497 # Basic use cases and unit test functions
498 # ==============================================================================
499 #
500 def TEST_MGCleanerMonPlugDialog():
501   print "TEST_MGCleanerMonPlugDialog"
502   import sys
503   from PyQt4.QtGui import QApplication
504   from PyQt4.QtCore import QObject, SIGNAL, SLOT
505   app = QApplication(sys.argv)
506   QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()"))
507
508   dlg=MGCleanerMonPlugDialog()
509   dlg.show()
510   sys.exit(app.exec_())
511
512 if __name__ == "__main__":
513   TEST_MGCleanerMonPlugDialog()
514   #TEST_standalone()
515   pass