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 # 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__))
48 self.iconfolder=os.environ["SMESH_ROOT_DIR"]+"/share/salome/resources/smesh"
49 #print "MGCleanerMonPlugDialog iconfolder",iconfolder
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")
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")
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("")
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)
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)
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)
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)
99 def PBHelpPressed(self):
101 mydir=os.environ["SMESH_ROOT_DIR"]
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)
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()
115 def enregistreResultat(self):
119 from salome.kernel import studyedit
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())
127 #print "name new MESH",self.LE_MeshFile.text()
128 a=str(self.fichierIn)
129 name=os.path.basename(os.path.splitext(a)[0])
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
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" )
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=" ; "))
149 SOMesh=maStudy.FindObjectByName(meshname ,"SMESH")[0]
150 newLink=monStudyBuilder.NewObject(SOMesh)
151 monStudyBuilder.Addreference(newLink, newStudyIter);
153 if salome.sg.hasDesktop(): salome.sg.updateObjBrowser(0)
157 def PBSavePressed(self, NomHypo=False):
158 from datetime import datetime
160 text = "# Params for Hypothese : Hypo_MGCleaner_"+str(self.num - 1)+"\n"
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")
169 f=open(self.paramsFile,"a")
171 QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
176 QMessageBox.warning(self, "File", "Unable to write "+self.paramsFile)
180 def PBSaveHypPressed(self):
181 """save hypothesis in Object Browser"""
182 #QMessageBox.warning(self, "save Object Browser MGCleaner Hypothesis", "TODO")
187 from salome.kernel import studyedit
189 maStudy=studyedit.getActiveStudy()
190 smesh.SetCurrentStudy(maStudy)
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" )
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=" ; "))
206 if salome.sg.hasDesktop(): salome.sg.updateObjBrowser(0)
210 def getResumeData(self, separator="\n"):
212 if self.RB_Fix1.isChecked():
213 CheckOrFix="fix1pass"
215 if self.RB_Fix2.isChecked():
216 CheckOrFix="fix2pass"
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
235 def loadResumeData(self, hypothesis, separator="\n"):
238 for slig in reversed(text.split(separator)):
240 #print "load ResumeData",lig
241 if lig=="": continue #skip blanck lines
242 if lig[0]=="#": break
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)))
266 QMessageBox.warning(self, "load MGCleaner Hypothesis", "Problem on '"+lig+"'")
268 def PBLoadPressed(self):
269 """load last hypothesis saved in tail of file"""
271 f=open(self.paramsFile,"r")
273 QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
278 QMessageBox.warning(self, "File", "Unable to read "+self.paramsFile)
281 self.loadResumeData(text, separator="\n")
283 def PBLoadHypPressed(self):
284 """load hypothesis saved in Object Browser"""
285 #QMessageBox.warning(self, "load Object Browser MGCleaner hypothesis", "TODO")
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
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")
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()
303 #if mySObject.GetFather().GetName()!="MGCleaner Hypotheses":
304 # QMessageBox.critical(self, "Hypothese", "not a child of MGCleaner Hypotheses")
307 text=mySObject.GetComment()
310 if "CheckOrFix=" not in text:
311 QMessageBox.critical(self, "Load Hypothese", "Object Browser selection not a MGCleaner Hypothesis")
313 self.loadResumeData(text, separator=" ; ")
316 def PBCancelPressed(self):
319 def PBMeshFilePressed(self):
320 fd = QFileDialog(self, "select an existing Mesh file", self.LE_MeshFile.text(), "Mesh-Files (*.mesh);;All Files (*)")
322 infile = fd.selectedFiles()[0]
323 self.LE_MeshFile.setText(infile)
324 self.fichierIn=infile.toLatin1()
326 self.LE_MeshSmesh.setText("")
328 def setParamsFileName(self):
329 fd = QFileDialog(self, "select a file", self.LE_ParamsFile.text(), "dat Files (*.dat);;All Files (*)")
331 infile = fd.selectedFiles()[0]
332 self.LE_ParamsFile.setText(infile)
333 self.paramsFile=infile.toLatin1()
335 def meshFileNameChanged(self):
336 self.fichierIn=str(self.LE_MeshFile.text())
337 #print "meshFileNameChanged", self.fichierIn
338 if os.path.exists(self.fichierIn):
340 self.LE_MeshSmesh.setText("")
342 QMessageBox.warning( self, "Unknown File", "File doesn't exist")
344 def paramsFileNameChanged(self):
345 self.paramsFile=self.LE_ParamsFile.text()
347 def PBMeshSmeshPressed(self):
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
355 mySObject, myEntry = guihelper.getSObjectSelected()
356 if CORBA.is_nil(mySObject) or mySObject==None:
357 QMessageBox.critical(self, "Mesh", "select an input mesh")
359 self.smeshStudyTool = SMeshStudyTools()
361 self.__selectedMesh = self.smeshStudyTool.getMeshObjectFromSObject(mySObject)
363 QMessageBox.critical(self, "Mesh", "select an input mesh")
365 if CORBA.is_nil(self.__selectedMesh):
366 QMessageBox.critical(self, "Mesh", "select an input mesh")
368 myName = mySObject.GetName()
369 #print "MeshSmeshNameChanged", myName
371 self.LE_MeshSmesh.setText(myName)
372 self.LE_MeshFile.setText("")
375 def prepareFichier(self):
376 self.fichierIn="/tmp/ForMGCleaner_"+str(self.num)+".mesh"
378 self.__selectedMesh.ExportGMF(self.__selectedMesh,self.fichierIn, True)
380 def PrepareLigneCommande(self):
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 .
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
396 #self.commande="mg-cleaner.exe --in " + self.fichierIn + " --out " + self.fichierOut + " --fix2pass"
398 if self.fichierIn=="" and self.MeshIn=="" :
399 QMessageBox.critical(self, "Mesh", "select an input mesh")
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))
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"
417 if self.RB_Fix2.isChecked():
418 self.commande+=" --fix2pass"
420 self.commande+=" --check"
421 if self.CB_PreserveTopology.isChecked():
422 self.commande+=" --topology respect"
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())
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)
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)
455 self.CB_ComputedToleranceDisplacement.setChecked(True)
456 self.CB_ComputedResolutionLength.setChecked(True)
457 self.CB_ComputedOverlapDistance.setChecked(True)
462 This function returns a singleton instance of the plugin dialog.
463 c est obligatoire pour faire un show sans parent...
467 __dialog = MGCleanerMonPlugDialog()
474 # ==============================================================================
476 # ==============================================================================
478 def TEST_standalone():
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
485 python /export/home/wambeke/2013/V6_main_MGC_CO6.4_64/INSTALL/SMESH/share/salome/plugins/smesh/MGCleanerMonPlugDialog.py
490 from salome.kernel import studyedit
492 maStudy=studyedit.getActiveStudy()
496 # ==============================================================================
497 # Basic use cases and unit test functions
498 # ==============================================================================
500 def TEST_MGCleanerMonPlugDialog():
501 print "TEST_MGCleanerMonPlugDialog"
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()"))
508 dlg=MGCleanerMonPlugDialog()
510 sys.exit(app.exec_())
512 if __name__ == "__main__":
513 TEST_MGCleanerMonPlugDialog()