1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2013 EDF R&D
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.
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.
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
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
25 from MGCleanerPlugDialog import Ui_MGCleanerPlugDialog
26 from MGCleanerMonViewText import MGCleanerMonViewText
27 from PyQt4.QtGui import *
28 from PyQt4.QtCore import *
31 class MGCleanerMonPlugDialog(Ui_MGCleanerPlugDialog,QWidget):
35 QWidget.__init__(self)
37 self.connecterSignaux()
43 self.__selectedMesh=None
45 # complex whith QResources: not used
46 # The icon are supposed to be located in the $SMESH_ROOT_DIR/share/salome/resources/smesh folder,
47 # other solution could be in the same folder than this python module file:
48 # iconfolder=os.path.dirname(os.path.abspath(__file__))
50 self.iconfolder=os.environ["SMESH_ROOT_DIR"]+"/share/salome/resources/smesh"
51 #print "MGCleanerMonPlugDialog iconfolder",iconfolder
53 icon.addFile(os.path.join(self.iconfolder,"select1.png"))
54 self.PB_LoadHyp.setIcon(icon)
55 self.PB_LoadHyp.setToolTip("hypothesis from Salome Object Browser")
56 self.PB_SaveHyp.setIcon(icon)
57 self.PB_SaveHyp.setToolTip("hypothesis to Salome Object Browser")
58 self.PB_MeshSmesh.setIcon(icon)
59 self.PB_MeshSmesh.setToolTip("source mesh from Salome Object Browser")
61 icon.addFile(os.path.join(self.iconfolder,"open.png"))
62 self.PB_ParamsFileExplorer.setIcon(icon)
63 self.PB_Load.setIcon(icon)
64 self.PB_Load.setToolTip("hypothesis from file")
65 self.PB_Save.setIcon(icon)
66 self.PB_Save.setToolTip("hypothesis to file")
67 self.PB_MeshFile.setIcon(icon)
68 self.PB_MeshFile.setToolTip("source mesh from a file in disk")
70 #Ces parametres ne sont pas remis à rien par le clean
71 self.paramsFile= os.path.abspath(os.path.join(os.environ["HOME"],".MGCleaner.dat"))
72 self.LE_ParamsFile.setText(self.paramsFile)
73 self.LE_MeshFile.setText("")
74 self.LE_MeshSmesh.setText("")
76 v1=QDoubleValidator(self)
80 self.SP_MinHoleSize.setValidator(v1)
82 v2=QDoubleValidator(self)
86 self.SP_ToleranceDisplacement.setValidator(v2)
88 v3=QDoubleValidator(self)
92 self.SP_ResolutionLength.setValidator(v3)
94 v4=QDoubleValidator(self)
98 self.SP_OverlapDistance.setValidator(v4)
100 self.resize(800, 500)
103 def connecterSignaux(self) :
104 self.connect(self.PB_Cancel,SIGNAL("clicked()"),self.PBCancelPressed)
105 self.connect(self.PB_Default,SIGNAL("clicked()"),self.clean)
106 self.connect(self.PB_Help,SIGNAL("clicked()"),self.PBHelpPressed)
107 self.connect(self.PB_OK,SIGNAL("clicked()"),self.PBOKPressed)
109 self.connect(self.PB_Load,SIGNAL("clicked()"),self.PBLoadPressed)
110 self.connect(self.PB_Save,SIGNAL("clicked()"),self.PBSavePressed)
111 self.connect(self.PB_LoadHyp,SIGNAL("clicked()"),self.PBLoadHypPressed)
112 self.connect(self.PB_SaveHyp,SIGNAL("clicked()"),self.PBSaveHypPressed)
114 self.connect(self.PB_MeshFile,SIGNAL("clicked()"),self.PBMeshFilePressed)
115 self.connect(self.PB_MeshSmesh,SIGNAL("clicked()"),self.PBMeshSmeshPressed)
116 self.connect(self.LE_MeshSmesh,SIGNAL("returnPressed()"),self.meshSmeshNameChanged)
117 self.connect(self.PB_ParamsFileExplorer,SIGNAL("clicked()"),self.setParamsFileName)
118 self.connect(self.LE_MeshFile,SIGNAL("returnPressed()"),self.meshFileNameChanged)
119 self.connect(self.LE_ParamsFile,SIGNAL("returnPressed()"),self.paramsFileNameChanged)
121 #QtCore.QObject.connect(self.checkBox, QtCore.SIGNAL("stateChanged(int)"), self.change)
122 self.connect(self.CB_FillHoles,SIGNAL("stateChanged(int)"),self.SP_MinHoleSize.setEnabled)
123 self.connect(self.CB_ComputedToleranceDisplacement,SIGNAL("stateChanged(int)"),self.SP_ToleranceDisplacement.setDisabled)
124 self.connect(self.CB_ComputedResolutionLength,SIGNAL("stateChanged(int)"),self.SP_ResolutionLength.setDisabled)
125 self.connect(self.CB_ComputedOverlapDistance,SIGNAL("stateChanged(int)"),self.SP_OverlapDistance.setDisabled)
127 def PBHelpPressed(self):
129 mydir=os.environ["SMESH_ROOT_DIR"]
131 QMessageBox.warning( self, "Help", "Help unavailable $SMESH_ROOT_DIR not found")
133 maDoc=mydir+"/share/doc/salome/gui/SMESH/MGCleaner/_downloads/mg-cleaner_user_manual.pdf"
134 command="xdg-open "+maDoc+";"
135 subprocess.call(command, shell=True)
137 def PBOKPressed(self):
138 if not(self.PrepareLigneCommande()):
140 #QMessageBox.warning(self, "Compute", "Command not found")
142 maFenetre=MGCleanerMonViewText(self, self.commande)
144 def enregistreResultat(self):
147 from salome.kernel import studyedit
148 from salome.smesh import smeshBuilder
149 smesh = smeshBuilder.New(salome.myStudy)
151 if not os.path.isfile(self.fichierOut):
152 QMessageBox.warning(self, "Compute", "Result file "+self.fichierOut+" not found")
154 maStudy=studyedit.getActiveStudy()
155 smesh.SetCurrentStudy(maStudy)
156 (outputMesh, status) = smesh.CreateMeshesFromGMF(self.fichierOut)
157 name=str(self.LE_MeshSmesh.text())
159 initialMeshObject=None
161 a=str(self.fichierIn)
162 name=os.path.basename(os.path.splitext(a)[0])
165 initialMeshObject=maStudy.FindObjectByName(name ,"SMESH")[0]
167 meshname = name+"_MGC_"+str(self.num)
168 smesh.SetName(outputMesh.GetMesh(), meshname)
169 outputMesh.Compute() #no algorithms message for "Mesh_x" has been computed with warnings: - global 1D algorithm is missing
171 self.editor = studyedit.getStudyEditor()
172 moduleEntry=self.editor.findOrCreateComponent("SMESH","SMESH")
173 HypReMeshEntry = self.editor.findOrCreateItem(
174 moduleEntry, name = "Plugins Hypotheses", icon="mesh_tree_hypo.png") #, comment = "HypoForRemeshing" )
176 monStudyBuilder=maStudy.NewBuilder()
177 monStudyBuilder.NewCommand()
178 newStudyIter=monStudyBuilder.NewObject(HypReMeshEntry)
179 self.editor.setAttributeValue(newStudyIter, "AttributeName", "MGCleaner Parameters_"+str(self.num))
180 self.editor.setAttributeValue(newStudyIter, "AttributeComment", self.getResumeData(separator=" ; "))
182 SOMesh=maStudy.FindObjectByName(meshname ,"SMESH")[0]
184 if initialMeshFile!=None:
185 newStudyFileName=monStudyBuilder.NewObject(SOMesh)
186 self.editor.setAttributeValue(newStudyFileName, "AttributeName", "meshFile")
187 self.editor.setAttributeValue(newStudyFileName, "AttributeExternalFileDef", initialMeshFile)
188 self.editor.setAttributeValue(newStudyFileName, "AttributeComment", initialMeshFile)
190 if initialMeshObject!=None:
191 newLink=monStudyBuilder.NewObject(SOMesh)
192 monStudyBuilder.Addreference(newLink, initialMeshObject)
194 newLink=monStudyBuilder.NewObject(SOMesh)
195 monStudyBuilder.Addreference(newLink, newStudyIter)
197 if salome.sg.hasDesktop(): salome.sg.updateObjBrowser(0)
201 def PBSavePressed(self):
202 from datetime import datetime
203 if not(self.PrepareLigneCommande()): return
204 text = "# MGCleaner hypothesis parameters\n"
205 text += "# Params for mesh : " + self.LE_MeshSmesh.text() +"\n"
206 text += datetime.now().strftime("# Date : %d/%m/%y %H:%M:%S\n")
207 text += "# Command : "+self.commande+"\n"
208 text += self.getResumeData(separator="\n")
212 f=open(self.paramsFile,"a")
214 QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
219 QMessageBox.warning(self, "File", "Unable to write "+self.paramsFile)
223 def PBSaveHypPressed(self):
224 """save hypothesis in Object Browser"""
227 from salome.kernel import studyedit
228 from salome.smesh import smeshBuilder
229 smesh = smeshBuilder.New(salome.myStudy)
231 maStudy=studyedit.getActiveStudy()
232 smesh.SetCurrentStudy(maStudy)
234 self.editor = studyedit.getStudyEditor()
235 moduleEntry=self.editor.findOrCreateComponent("SMESH","SMESH")
236 HypReMeshEntry = self.editor.findOrCreateItem(
237 moduleEntry, name = "Plugins Hypotheses", icon="mesh_tree_hypo.png") #, comment = "HypoForRemeshing" )
239 monStudyBuilder=maStudy.NewBuilder()
240 monStudyBuilder.NewCommand()
241 newStudyIter=monStudyBuilder.NewObject(HypReMeshEntry)
242 self.editor.setAttributeValue(newStudyIter, "AttributeName", "MGCleaner Parameters_"+str(self.num))
243 self.editor.setAttributeValue(newStudyIter, "AttributeComment", self.getResumeData(separator=" ; "))
245 if salome.sg.hasDesktop(): salome.sg.updateObjBrowser(0)
250 import salome_pluginsmanager
251 print "salome_pluginsmanager.plugins",salome_pluginsmanager.plugins
252 print "salome_pluginsmanager.current_plugins_manager",salome_pluginsmanager.current_plugins_manager
255 def SP_toStr(self, widget):
256 #cr, pos=widget.validator().validate(res, 0) #n.b. "1,3" is acceptable !locale!
258 return str(float(widget.text()))
260 widget.setProperty("text", "0.0")
263 def getResumeData(self, separator="\n"):
265 if self.RB_Fix1.isChecked():
266 CheckOrFix="fix1pass"
268 if self.RB_Fix2.isChecked():
269 CheckOrFix="fix2pass"
272 text+="CheckOrFix="+CheckOrFix+separator
273 text+="PreserveTopology="+str(self.CB_PreserveTopology.isChecked())+separator
274 text+="FillHoles="+str(self.CB_FillHoles.isChecked())+separator
275 v=self.SP_toStr(self.SP_MinHoleSize)
276 text+="MinHoleSize="+v+separator
277 text+="ComputedToleranceDisplacement="+str(self.CB_ComputedToleranceDisplacement.isChecked())+separator
278 v=self.SP_toStr(self.SP_ToleranceDisplacement)
279 text+="ToleranceDisplacement="+v+separator
280 text+="ComputedResolutionLength="+str(self.CB_ComputedResolutionLength.isChecked())+separator
281 v=self.SP_toStr(self.SP_ResolutionLength)
282 text+="ResolutionLength="+v+separator
283 text+="FoldingAngle="+str(self.SP_FoldingAngle.value())+separator
284 text+="RemeshPlanes="+str(self.CB_RemeshPlanes.isChecked())+separator
285 text+="ComputedOverlapDistance="+str(self.CB_ComputedOverlapDistance.isChecked())+separator
286 v=self.SP_toStr(self.SP_OverlapDistance)
287 text+="OverlapDistance="+v+separator
288 text+="OverlapAngle="+str(self.SP_OverlapAngle.value())+separator
289 text+="Verbosity="+str(self.SP_Verbosity.value())+separator
292 def loadResumeData(self, hypothesis, separator="\n"):
295 for slig in reversed(text.split(separator)):
297 #print "load ResumeData",lig
298 if lig=="": continue #skip blanck lines
299 if lig[0]=="#": break
301 tit,value=lig.split("=")
302 if tit=="CheckOrFix":
303 self.RB_Fix1.setChecked(False)
304 self.RB_Fix2.setChecked(False)
305 self.RB_Check.setChecked(False)
306 if value=="fix1pass": self.RB_Fix1.setChecked(True)
307 if value=="fix2pass": self.RB_Fix2.setChecked(True)
308 if value=="check": self.RB_Check.setChecked(True)
309 if tit=="PreserveTopology": self.CB_PreserveTopology.setChecked(value=="True")
310 if tit=="FillHoles": self.CB_FillHoles.setChecked(value=="True")
311 if tit=="MinHoleSize": self.SP_MinHoleSize.setProperty("text", value)
312 if tit=="ComputedToleranceDisplacement": self.CB_ComputedToleranceDisplacement.setChecked(value=="True")
313 if tit=="ToleranceDisplacement": self.SP_ToleranceDisplacement.setProperty("text", value)
314 if tit=="ComputedResolutionLength": self.CB_ComputedResolutionLength.setChecked(value=="True")
315 if tit=="ResolutionLength": self.SP_ResolutionLength.setProperty("text", value)
316 if tit=="FoldingAngle": self.SP_FoldingAngle.setProperty("value", float(value))
317 if tit=="RemeshPlanes": self.CB_RemeshPlanes.setChecked(value=="True")
318 if tit=="ComputedOverlapDistance": self.CB_ComputedOverlapDistance.setChecked(value=="True")
319 if tit=="OverlapDistance": self.SP_OverlapDistance.setProperty("text", value)
320 if tit=="OverlapAngle": self.SP_OverlapAngle.setProperty("value", float(value))
321 if tit=="Verbosity": self.SP_Verbosity.setProperty("value", int(float(value)))
323 QMessageBox.warning(self, "load MGCleaner Hypothesis", "Problem on '"+lig+"'")
325 def PBLoadPressed(self):
326 """load last hypothesis saved in tail of file"""
328 f=open(self.paramsFile,"r")
330 QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
335 QMessageBox.warning(self, "File", "Unable to read "+self.paramsFile)
338 self.loadResumeData(text, separator="\n")
340 def PBLoadHypPressed(self):
341 """load hypothesis saved in Object Browser"""
342 #QMessageBox.warning(self, "load Object Browser MGCleaner hypothesis", "TODO")
344 from salome.kernel import studyedit
345 from salome.smesh.smeshstudytools import SMeshStudyTools
346 from salome.gui import helper as guihelper
347 from omniORB import CORBA
349 mySObject, myEntry = guihelper.getSObjectSelected()
350 if CORBA.is_nil(mySObject) or mySObject==None:
351 QMessageBox.critical(self, "Hypothese", "select an Object Browser MGCleaner hypothesis")
354 #for i in dir(mySObject): print "dir mySObject",i
355 #print "GetAllAttributes",mySObject.GetAllAttributes()
356 #print "GetComment",mySObject.GetComment()
357 #print "GetName",mySObject.GetName()
360 #if mySObject.GetFather().GetName()!="MGCleaner Hypotheses":
361 # QMessageBox.critical(self, "Hypothese", "not a child of MGCleaner Hypotheses")
364 text=mySObject.GetComment()
367 if "CheckOrFix=" not in text:
368 QMessageBox.critical(self, "Load Hypothese", "Object Browser selection not a MGCleaner Hypothesis")
370 self.loadResumeData(text, separator=" ; ")
373 def PBCancelPressed(self):
376 def PBMeshFilePressed(self):
377 fd = QFileDialog(self, "select an existing Mesh file", self.LE_MeshFile.text(), "Mesh-Files (*.mesh);;All Files (*)")
379 infile = fd.selectedFiles()[0]
380 self.LE_MeshFile.setText(infile)
381 self.fichierIn=infile.toLatin1()
383 self.LE_MeshSmesh.setText("")
385 def setParamsFileName(self):
386 fd = QFileDialog(self, "select a file", self.LE_ParamsFile.text(), "dat Files (*.dat);;All Files (*)")
388 infile = fd.selectedFiles()[0]
389 self.LE_ParamsFile.setText(infile)
390 self.paramsFile=infile.toLatin1()
392 def meshFileNameChanged(self):
393 self.fichierIn=str(self.LE_MeshFile.text())
394 #print "meshFileNameChanged", self.fichierIn
395 if os.path.exists(self.fichierIn):
396 self.__selectedMesh=None
398 self.LE_MeshSmesh.setText("")
400 QMessageBox.warning(self, "Mesh file", "File doesn't exist")
402 def meshSmeshNameChanged(self):
403 """only change by GUI mouse selection, otherwise clear"""
404 #self.MeshIn=str(self.LE_MeshSmesh.text())
405 #print "meshSmeshNameChanged", self.MeshIn
406 self.__selectedMesh = None
408 self.LE_MeshSmesh.setText("")
412 def paramsFileNameChanged(self):
413 self.paramsFile=self.LE_ParamsFile.text()
415 def PBMeshSmeshPressed(self):
416 from omniORB import CORBA
418 from salome.kernel import studyedit
419 from salome.smesh.smeshstudytools import SMeshStudyTools
420 from salome.gui import helper as guihelper
421 from salome.smesh import smeshBuilder
422 smesh = smeshBuilder.New(salome.myStudy)
424 mySObject, myEntry = guihelper.getSObjectSelected()
425 if CORBA.is_nil(mySObject) or mySObject==None:
426 QMessageBox.critical(self, "Mesh", "select an input mesh")
428 self.smeshStudyTool = SMeshStudyTools()
430 self.__selectedMesh = self.smeshStudyTool.getMeshObjectFromSObject(mySObject)
432 QMessageBox.critical(self, "Mesh", "select an input mesh")
434 if CORBA.is_nil(self.__selectedMesh):
435 QMessageBox.critical(self, "Mesh", "select an input mesh")
437 myName = mySObject.GetName()
438 #print "MeshSmeshNameChanged", myName
440 self.LE_MeshSmesh.setText(myName)
441 self.LE_MeshFile.setText("")
444 def prepareFichier(self):
445 self.fichierIn="/tmp/ForMGCleaner_"+str(self.num)+".mesh"
446 self.__selectedMesh.ExportGMF(self.__selectedMesh, self.fichierIn, True)
448 def PrepareLigneCommande(self):
450 #use doc examples of mg-cleaner:
451 ls -al /data/tmplgls/salome/prerequis/install/COMMON_64/MeshGems-1.0/bin
452 source /data/tmplgls/salome/prerequis/install/LICENSE/dlim8.var.sh
453 export PATH=/data/tmplgls/salome/prerequis/install/COMMON_64/MeshGems-1.0/bin/Linux_64:$PATH
454 cp -r /data/tmplgls/salome/prerequis/install/COMMON_64/MeshGems-1.0/examples .
456 mg-cleaner.exe --help
457 mg-cleaner.exe --in case7.mesh --out case7-test.mesh --check
458 mg-cleaner.exe case7.mesh case7-fix.mesh --fix
459 mg-cleaner.exe --in Porsche.mesh --out Porsche-test.mesh --check
460 mg-cleaner.exe --in Porsche.mesh --out Porschefix.mesh --fix
461 mg-cleaner.exe --in Porsche.mesh --out PorscheNewfix.mesh --fix --resolution_length 0.03
464 #self.commande="mg-cleaner.exe --in " + self.fichierIn + " --out " + self.fichierOut + " --fix2pass"
466 #print "PrepareLigneCommande '"+self.fichierIn+"' '"+self.MeshIn+"'",self.__selectedMesh
467 if self.fichierIn=="" and self.MeshIn=="":
468 QMessageBox.critical(self, "Mesh", "select an input mesh")
470 if self.__selectedMesh!=None: self.prepareFichier()
471 if not (os.path.isfile(self.fichierIn)):
472 QMessageBox.critical(self, "File", "unable to read GMF Mesh in "+str(self.fichierIn))
475 self.commande="mg-cleaner.exe"
476 verbosity=str(self.SP_Verbosity.value())
477 self.commande+=" --verbose " + verbosity
478 self.commande+=" --in " + self.fichierIn
479 #print "self.fichierIn",self.fichierIn,type(self.fichierIn)
480 deb=os.path.splitext(str(self.fichierIn))
481 self.fichierOut=deb[0] + "_fix.mesh"
482 self.commande+=" --out "+self.fichierOut
483 if self.RB_Fix1.isChecked():
484 self.commande+=" --fix1pass"
486 if self.RB_Fix2.isChecked():
487 self.commande+=" --fix2pass"
489 self.commande+=" --check"
490 if self.CB_PreserveTopology.isChecked():
491 self.commande+=" --topology respect"
493 self.commande+=" --topology ignore"
494 if self.CB_FillHoles.isChecked(): #no fill holes default
495 self.commande+=" --min_hole_size " + self.SP_toStr(self.SP_MinHoleSize)
496 if not self.CB_ComputedToleranceDisplacement.isChecked(): #computed default
497 self.commande+=" --tolerance_displacement " + self.SP_toStr(self.SP_ToleranceDisplacement)
498 if not self.CB_ComputedResolutionLength.isChecked(): #computed default
499 self.commande+=" --resolution_length " + self.SP_toStr(self.SP_ResolutionLength)
500 self.commande+=" --folding_angle " + str(self.SP_FoldingAngle.value())
501 if self.CB_RemeshPlanes.isChecked(): #no remesh default
502 self.commande+=" --remesh_planes"
503 if not self.CB_ComputedOverlapDistance.isChecked(): #computed default
504 self.commande+=" --overlap_distance " + self.SP_toStr(self.SP_OverlapDistance)
505 self.commande+=" --overlap_angle " + str(self.SP_OverlapAngle.value())
509 self.RB_Check.setChecked(False)
510 self.RB_Fix1.setChecked(False)
511 self.RB_Fix2.setChecked(True)
512 self.CB_PreserveTopology.setChecked(False)
513 self.CB_FillHoles.setChecked(False)
514 self.CB_RemeshPlanes.setChecked(False)
516 self.SP_MinHoleSize.setProperty("text", 0)
517 self.SP_ToleranceDisplacement.setProperty("text", 0)
518 self.SP_ResolutionLength.setProperty("text", 0)
519 self.SP_FoldingAngle.setProperty("value", 15)
520 self.SP_OverlapDistance.setProperty("text", 0)
521 self.SP_OverlapAngle.setProperty("value", 15)
522 self.SP_Verbosity.setProperty("value", 3)
524 self.CB_ComputedToleranceDisplacement.setChecked(True)
525 self.CB_ComputedResolutionLength.setChecked(True)
526 self.CB_ComputedOverlapDistance.setChecked(True)
531 This function returns a singleton instance of the plugin dialog.
532 c est obligatoire pour faire un show sans parent...
536 __dialog = MGCleanerMonPlugDialog()
543 # ==============================================================================
545 # ==============================================================================
547 def TEST_standalone():
549 works only if a salome is launched yet with a study loaded
550 to launch standalone python do:
551 /export/home/wambeke/2013/V6_main_MGC_CO6.4_64/APPLI/runSession
554 python /export/home/wambeke/2013/V6_main_MGC_CO6.4_64/INSTALL/SMESH/share/salome/plugins/smesh/MGCleanerMonPlugDialog.py
558 from salome.kernel import studyedit
560 maStudy=studyedit.getActiveStudy()
564 # ==============================================================================
565 # Basic use cases and unit test functions
566 # ==============================================================================
568 def TEST_MGCleanerMonPlugDialog():
569 #print "TEST_MGCleanerMonPlugDialog"
571 from PyQt4.QtGui import QApplication
572 from PyQt4.QtCore import QObject, SIGNAL, SLOT
573 app = QApplication(sys.argv)
574 QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()"))
576 dlg=MGCleanerMonPlugDialog()
578 sys.exit(app.exec_())
580 if __name__ == "__main__":
581 TEST_MGCleanerMonPlugDialog()