1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2020 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, or (at your option) any later version.
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
26 from YamsPlugDialog_ui import Ui_YamsPlugDialog
27 from monViewText import MonViewText
28 from qtsalome import *
32 class MonYamsPlugDialog(Ui_YamsPlugDialog,QWidget):
36 QWidget.__init__(self)
38 self.connecterSignaux()
44 self.__selectedMesh=None
46 # complex with 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__))
51 self.iconfolder=os.environ["SMESH_ROOT_DIR"]+"/share/salome/resources/smesh"
52 #print "MGSurfOptPlugDialog iconfolder",iconfolder
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")
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")
71 #Ces parametres ne sont pas remis a rien par le clean
72 self.paramsFile= os.path.abspath(os.path.join(os.path.expanduser("~"),".MGSurfOpt.dat"))
73 self.LE_ParamsFile.setText(self.paramsFile)
74 self.LE_MeshFile.setText("")
75 self.LE_MeshSmesh.setText("")
77 v1=QDoubleValidator(self)
79 #v1.setTop(1000.) #per thousand... only if relative
81 self.SP_Tolerance.setValidator(v1)
82 self.SP_Tolerance.titleForWarning="Chordal Tolerance"
84 self.SP_MinSize.setDecimals(5)
89 def connecterSignaux(self) :
90 self.PB_Cancel.clicked.connect(self.PBCancelPressed)
91 self.PB_Default.clicked.connect(self.clean)
92 self.PB_Help.clicked.connect(self.PBHelpPressed)
93 self.PB_OK.clicked.connect(self.PBOKPressed)
95 self.PB_Load.clicked.connect(self.PBLoadPressed)
96 self.PB_Save.clicked.connect(self.PBSavePressed)
97 self.PB_LoadHyp.clicked.connect(self.PBLoadHypPressed)
98 self.PB_SaveHyp.clicked.connect(self.PBSaveHypPressed)
100 self.PB_MeshFile.clicked.connect(self.PBMeshFilePressed)
101 self.PB_MeshSmesh.clicked.connect(self.PBMeshSmeshPressed)
102 self.LE_MeshSmesh.returnPressed.connect(self.meshSmeshNameChanged)
103 self.PB_ParamsFileExplorer.clicked.connect(self.setParamsFileName)
104 self.LE_MeshFile.returnPressed.connect(self.meshFileNameChanged)
105 self.LE_ParamsFile.returnPressed.connect(self.paramsFileNameChanged)
107 def PBHelpPressed(self):
109 sgPyQt = SalomePyQt.SalomePyQt()
111 mydir=os.environ["SMESH_ROOT_DIR"]
113 QMessageBox.warning(self, "Help", "Help unavailable $SMESH_ROOT_DIR not found")
116 maDoc=mydir+"/share/doc/salome/gui/SMESH/yams/index.html"
117 sgPyQt.helpContext(maDoc,"")
119 #maDoc=mydir+"/share/doc/salome/gui/SMESH/yams/_downloads/mg-surfopt_user_manual.pdf"
120 #command="xdg-open "+maDoc+";"
121 #subprocess.call(command, shell=True)
123 def PBOKPressed(self):
124 if not(self.PrepareLigneCommande()):
126 #QMessageBox.warning(self, "Compute", "Command not found")
128 maFenetre=MonViewText(self,self.commande)
130 def enregistreResultat(self):
133 from salome.kernel import studyedit
134 from salome.smesh import smeshBuilder
135 smesh = smeshBuilder.New()
137 if not os.path.isfile(self.fichierOut):
138 QMessageBox.warning(self, "Compute", "Result file "+self.fichierOut+" not found")
140 maStudy=salome.myStudy
142 (outputMesh, status) = smesh.CreateMeshesFromGMF(self.fichierOut)
143 name=str(self.LE_MeshSmesh.text())
145 initialMeshObject=None
147 a=str(self.fichierIn)
148 name=os.path.basename(os.path.splitext(a)[0])
151 initialMeshObject=maStudy.FindObjectByName(name ,"SMESH")[0]
153 meshname = name+"_MGSO_"+str(self.num)
154 smesh.SetName(outputMesh.GetMesh(), meshname)
155 outputMesh.Compute() #no algorithms message for "Mesh_x" has been computed with warnings: - global 1D algorithm is missing
157 self.editor = studyedit.getStudyEditor()
158 moduleEntry=self.editor.findOrCreateComponent("SMESH","SMESH")
159 HypReMeshEntry = self.editor.findOrCreateItem(
160 moduleEntry, name = "Plugins Hypotheses", icon="mesh_tree_hypo.png") #, comment = "HypoForRemeshing" )
162 monStudyBuilder=maStudy.NewBuilder()
163 monStudyBuilder.NewCommand()
164 newStudyIter=monStudyBuilder.NewObject(HypReMeshEntry)
165 self.editor.setAttributeValue(newStudyIter, "AttributeName", "MGSurfOpt Parameters_"+str(self.num))
166 self.editor.setAttributeValue(newStudyIter, "AttributeComment", self.getResumeData(separator=" ; "))
168 SOMesh=maStudy.FindObjectByName(meshname ,"SMESH")[0]
170 if initialMeshFile!=None:
171 newStudyFileName=monStudyBuilder.NewObject(SOMesh)
172 self.editor.setAttributeValue(newStudyFileName, "AttributeName", "meshFile")
173 self.editor.setAttributeValue(newStudyFileName, "AttributeExternalFileDef", initialMeshFile)
174 self.editor.setAttributeValue(newStudyFileName, "AttributeComment", initialMeshFile)
176 if initialMeshObject!=None:
177 newLink=monStudyBuilder.NewObject(SOMesh)
178 monStudyBuilder.Addreference(newLink, initialMeshObject)
180 newLink=monStudyBuilder.NewObject(SOMesh)
181 monStudyBuilder.Addreference(newLink, newStudyIter)
183 if salome.sg.hasDesktop(): salome.sg.updateObjBrowser()
187 def PBSavePressed(self):
188 from datetime import datetime
189 if not(self.PrepareLigneCommande()): return
190 text = "# MGSurfOpt hypothesis parameters\n"
191 text += "# Params for mesh : " + self.LE_MeshSmesh.text() +"\n"
192 text += datetime.now().strftime("# Date : %d/%m/%y %H:%M:%S\n")
193 text += "# Command : "+self.commande+"\n"
194 text += self.getResumeData(separator="\n")
198 f=open(self.paramsFile,"a")
200 QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
205 QMessageBox.warning(self, "File", "Unable to write "+self.paramsFile)
209 def PBSaveHypPressed_risky(self):
211 save hypothesis in Object Browser outside GEOM ou MESH
212 WARNING: at root of Object Browser is not politically correct
216 if verbose: print("save hypothesis in Object Browser")
219 #how ??? icon = "mesh_tree_hypo.png"
220 namei = "MGSurfOpt Parameters_%i" % self.num
221 datai = self.getResumeData(separator=" ; ")
223 myStudy = salome.myStudy
224 myBuilder = myStudy.NewBuilder()
225 #myStudy.IsStudyLocked()
226 myComponent = myStudy.FindComponent(name)
227 if myComponent == None:
228 print("myComponent not found, create")
229 myComponent = myBuilder.NewComponent(name)
230 AName = myBuilder.FindOrCreateAttribute(myComponent, "AttributeName")
232 ACmt = myBuilder.FindOrCreateAttribute(myComponent, "AttributeComment")
235 myObject = myBuilder.NewObject(myComponent)
236 AName = myBuilder.FindOrCreateAttribute(myObject, "AttributeName")
237 AName.SetValue(namei)
238 ACmt = myBuilder.FindOrCreateAttribute(myObject, "AttributeComment")
241 if salome.sg.hasDesktop(): salome.sg.updateObjBrowser()
243 if verbose: print(("save %s in Object Browser done: %s\n%s" % (name, myObject.GetID(), datai)))
246 def PBSaveHypPressed(self):
248 save hypothesis in Object Browser
249 bug: affichage ne marche pas si inclusion dans dans GEOM ou MESH depuis salome 730
253 from salome.kernel import studyedit
254 from salome.smesh import smeshBuilder
255 #[PAL issue tracker:issue1871] Les nouveaux objets ne s'affichent pas dans SMESH
256 QMessageBox.warning(self, "Save", "waiting for fix: Object Browser will not display hypothesis")
258 if verbose: print("save hypothesis in Object Browser")
259 smesh = smeshBuilder.New()
261 maStudy=salome.myStudy
264 self.editor = studyedit.getStudyEditor()
265 moduleEntry=self.editor.findOrCreateComponent("SMESH","SMESH")
266 HypReMeshEntry = self.editor.findOrCreateItem(
267 moduleEntry, name = "Plugins Hypotheses", icon="mesh_tree_hypo.png")
268 #, comment = "HypothesisForRemeshing" )
270 monStudyBuilder=maStudy.NewBuilder()
271 monStudyBuilder.NewCommand()
272 newStudyIter=monStudyBuilder.NewObject(HypReMeshEntry)
273 name = "MGSurfOpt Parameters_%i" % self.num
274 self.editor.setAttributeValue(newStudyIter, "AttributeName", name)
275 data = self.getResumeData(separator=" ; ")
276 self.editor.setAttributeValue(newStudyIter, "AttributeComment", data)
278 if salome.sg.hasDesktop(): salome.sg.updateObjBrowser()
280 if verbose: print(("save %s in Object Browser done:\n%s" % (name, data)))
283 def SP_toStr(self, widget):
284 """only for a QLineEdit widget"""
285 #cr, pos=widget.validator().validate(res, 0) #n.b. "1,3" is acceptable !locale!
287 val=float(widget.text())
289 QMessageBox.warning(self, widget.titleForWarning, "float value is incorrect: '"+widget.text()+"'")
290 res=str(widget.validator().bottom())
291 widget.setProperty("text", res)
293 valtest=widget.validator().bottom()
296 QMessageBox.warning(self, widget.titleForWarning, "float value is under minimum: "+str(val)+" < "+str(valtest))
298 widget.setProperty("text", res)
300 valtest=widget.validator().top()
303 QMessageBox.warning(self, widget.titleForWarning, "float value is over maximum: "+str(val)+" > "+str(valtest))
305 widget.setProperty("text", res)
309 def getResumeData(self, separator="\n"):
311 for RB in self.GBOptim.findChildren(QRadioButton,):
312 if RB.isChecked()==True:
313 text+="Optimisation="+RB.text()+separator
315 if self.RB_Absolute.isChecked():
316 text+="Units=absolute"+separator
318 text+="Units=relative"+separator
319 v=self.SP_toStr(self.SP_Tolerance)
320 text+="ChordalToleranceDeviation="+v+separator
321 text+="RidgeDetection="+str(self.CB_Ridge.isChecked())+separator
322 text+="SplitEdge="+str(self.CB_SplitEdge.isChecked())+separator
323 text+="PointSmoothing="+str(self.CB_Point.isChecked())+separator
324 text+="GeometricalApproximation="+str(self.SP_Geomapp.value())+separator
325 text+="RidgeAngle="+str(self.SP_Ridge.value())+separator
326 text+="MaximumSize="+str(self.SP_MaxSize.value())+separator
327 text+="MinimumSize="+str(self.SP_MinSize.value())+separator
328 text+="MeshGradation="+str(self.SP_Gradation.value())+separator
329 text+="Verbosity="+str(self.SP_Verbosity.value())+separator
330 text+="Memory="+str(self.SP_Memory.value())+separator
333 def loadResumeData(self, hypothesis, separator="\n"):
336 for slig in reversed(text.split(separator)):
338 #print "load ResumeData",lig
339 if lig=="": continue #skip blank lines
340 if lig[0]=="#": break
342 tit,value=lig.split("=")
343 if tit=="Optimisation":
344 #no need: exlusives QRadioButton
345 #for RB in self.GBOptim.findChildren(QRadioButton,):
346 # RB.setChecked(False)
347 for RB in self.GBOptim.findChildren(QRadioButton,):
348 if RB.text()==value :
352 if value=="absolute":
353 self.RB_Absolute.setChecked(True)
354 self.RB_Relative.setChecked(False)
356 self.RB_Absolute.setChecked(False)
357 self.RB_Relative.setChecked(True)
358 if tit=="ChordalToleranceDeviation": self.SP_Tolerance.setProperty("text", float(value))
359 if tit=="RidgeDetection": self.CB_Ridge.setChecked(value=="True")
360 if tit=="SplitEdge": self.CB_SplitEdge.setChecked(value=="True")
361 if tit=="PointSmoothing": self.CB_Point.setChecked(value=="True")
362 if tit=="GeometricalApproximation": self.SP_Geomapp.setProperty("value", float(value))
363 if tit=="RidgeAngle": self.SP_Ridge.setProperty("value", float(value))
364 if tit=="MaximumSize": self.SP_MaxSize.setProperty("value", float(value))
365 if tit=="MinimumSize": self.SP_MinSize.setProperty("value", float(value))
366 if tit=="MeshGradation": self.SP_Gradation.setProperty("value", float(value))
367 if tit=="Verbosity": self.SP_Verbosity.setProperty("value", int(float(value)))
368 if tit=="Memory": self.SP_Memory.setProperty("value", float(value))
370 QMessageBox.warning(self, "load MGSurfOpt Hypothesis", "Problem on '"+lig+"'")
372 def PBLoadPressed(self):
373 """load last hypothesis saved in tail of file"""
375 f=open(self.paramsFile,"r")
377 QMessageBox.warning(self, "File", "Unable to open "+self.paramsFile)
382 QMessageBox.warning(self, "File", "Unable to read "+self.paramsFile)
385 self.loadResumeData(text, separator="\n")
387 def PBLoadHypPressed(self):
388 """load hypothesis saved in Object Browser"""
389 #QMessageBox.warning(self, "load Object Browser MGSurfOpt hypothesis", "TODO")
391 from salome.kernel import studyedit
392 from salome.smesh.smeshstudytools import SMeshStudyTools
393 from salome.gui import helper as guihelper
394 from omniORB import CORBA
396 mySObject, myEntry = guihelper.getSObjectSelected()
397 if CORBA.is_nil(mySObject) or mySObject==None:
398 QMessageBox.critical(self, "Hypothese", "select an Object Browser MGSurfOpt hypothesis")
401 text=mySObject.GetComment()
404 if "Optimisation=" not in text:
405 QMessageBox.critical(self, "Load Hypothese", "Object Browser selection not a MGSurfOptHypothesis")
407 self.loadResumeData(text, separator=" ; ")
410 def PBCancelPressed(self):
413 def PBMeshFilePressed(self):
414 fd = QFileDialog(self, "select an existing Mesh file", self.LE_MeshFile.text(), "Mesh-Files (*.mesh);;All Files (*)")
416 infile = fd.selectedFiles()[0]
417 self.LE_MeshFile.setText(infile)
418 self.fichierIn=str(infile)
420 self.LE_MeshSmesh.setText("")
421 self.__selectedMesh=None
423 def setParamsFileName(self):
424 fd = QFileDialog(self, "select a file", self.LE_ParamsFile.text(), "dat Files (*.dat);;All Files (*)")
426 infile = fd.selectedFiles()[0]
427 self.LE_ParamsFile.setText(infile)
428 self.paramsFile=str(infile)
430 def meshFileNameChanged(self):
431 self.fichierIn=str(self.LE_MeshFile.text())
432 #print "meshFileNameChanged", self.fichierIn
433 if os.path.exists(self.fichierIn):
434 self.__selectedMesh=None
436 self.LE_MeshSmesh.setText("")
438 QMessageBox.warning(self, "Mesh file", "File doesn't exist")
440 def meshSmeshNameChanged(self):
441 """only change by GUI mouse selection, otherwise clear"""
442 self.__selectedMesh = None
444 self.LE_MeshSmesh.setText("")
448 def paramsFileNameChanged(self):
449 self.paramsFile=self.LE_ParamsFile.text()
451 def PBMeshSmeshPressed(self):
452 from omniORB import CORBA
454 from salome.kernel import studyedit
455 from salome.smesh.smeshstudytools import SMeshStudyTools
456 from salome.gui import helper as guihelper
457 from salome.smesh import smeshBuilder
458 smesh = smeshBuilder.New()
460 mySObject, myEntry = guihelper.getSObjectSelected()
461 if CORBA.is_nil(mySObject) or mySObject==None:
462 QMessageBox.critical(self, "Mesh", "select an input mesh")
463 #self.LE_MeshSmesh.setText("")
465 #self.LE_MeshFile.setText("")
468 self.smeshStudyTool = SMeshStudyTools()
470 self.__selectedMesh = self.smeshStudyTool.getMeshObjectFromSObject(mySObject)
472 QMessageBox.critical(self, "Mesh", "select an input mesh")
474 if CORBA.is_nil(self.__selectedMesh):
475 QMessageBox.critical(self, "Mesh", "select an input mesh")
477 myName = mySObject.GetName()
478 #print "MeshSmeshNameChanged", myName
480 self.LE_MeshSmesh.setText(myName)
481 self.LE_MeshFile.setText("")
484 def prepareFichier(self):
485 self.fichierIn=tempfile.mktemp(suffix=".mesh",prefix="ForSurfOpt_")
486 if os.path.exists(self.fichierIn):
487 os.remove(self.fichierIn)
488 self.__selectedMesh.ExportGMF(self.__selectedMesh, self.fichierIn, True)
490 def PrepareLigneCommande(self):
491 if self.fichierIn=="" and self.MeshIn=="":
492 QMessageBox.critical(self, "Mesh", "select an input mesh")
494 if self.__selectedMesh!=None: self.prepareFichier()
495 if not (os.path.isfile(self.fichierIn)):
496 QMessageBox.critical(self, "File", "unable to read GMF Mesh in "+str(self.fichierIn))
499 self.commande="mg-surfopt.exe"
501 for obj in self.GBOptim.findChildren(QRadioButton,):
504 self.style=obj.objectName().remove(0,3)
505 self.style.replace("_","-")
510 style = str(self.style)
511 # Translation of old Yams options to new MG-SurfOpt options
513 self.commande+= " --optimisation only"
515 self.commande+= " --Hausdorff_like yes"
517 self.commande+= " --enrich no"
519 self.commande+= " --Hausdorff_like yes --enrich no"
521 self.commande+= " --uniform_flat_subdivision yes"
523 self.commande+= " --sand_paper yes"
525 self.commande+= " -O G" # This option has not been updated to the new option style yet
527 deb=os.path.splitext(self.fichierIn)
528 self.fichierOut=deb[0] + "_output.mesh"
530 tolerance=self.SP_toStr(self.SP_Tolerance)
531 if not self.RB_Absolute.isChecked():
533 self.commande+=" --chordal_error %s"%tolerance
535 if self.CB_Ridge.isChecked() == False : self.commande+=" --compute_ridges no"
536 if self.CB_Point.isChecked() == False : self.commande+=" --optimisation no"
537 if self.CB_SplitEdge.isChecked()== True : self.commande+=" --element_order quadratic"
538 if self.SP_Geomapp.value() != 15.0 : self.commande+=" --geometric_approximation_angle %f"%self.SP_Geomapp.value()
539 if self.SP_Ridge.value() != 45.0 : self.commande+=" --ridge_angle %f"%self.SP_Ridge.value()
540 if self.SP_MaxSize.value() != 100 : self.commande+=" --max_size %f" %self.SP_MaxSize.value()
541 if self.SP_MinSize.value() != 5 : self.commande+=" --min_size %f" %self.SP_MinSize.value()
542 if self.SP_Gradation.value() != 1.3 : self.commande+=" --gradation %f" %self.SP_Gradation.value()
543 if self.SP_Memory.value() != 0 : self.commande+=" --max_memory %d" %self.SP_Memory.value()
544 if self.SP_Verbosity.value() != 3 : self.commande+=" --verbose %d" %self.SP_Verbosity.value()
546 self.commande+=' --in "' + self.fichierIn +'"'
547 self.commande+=' --out "' + self.fichierOut +'"'
553 self.RB_0.setChecked(True)
554 #no need: exlusives QRadioButton
555 #self.RB_G.setChecked(False)
556 #self.RB_U.setChecked(False)
557 #self.RB_S.setChecked(False)
558 #self.RB_2.setChecked(False)
559 #self.RB_1.setChecked(False)
560 self.RB_Relative.setChecked(True)
561 #no need: exlusives QRadioButton
562 #self.RB_Absolute.setChecked(False)
563 self.SP_Tolerance.setProperty("text", "0.001")
564 self.SP_Geomapp.setProperty("value", 15.0)
565 self.SP_Ridge.setProperty("value", 45.0)
566 self.SP_Gradation.setProperty("value", 1.3)
567 self.CB_Ridge.setChecked(True)
568 self.CB_Point.setChecked(True)
569 self.CB_SplitEdge.setChecked(False)
570 self.SP_MaxSize.setProperty("value", 100)
571 self.SP_MinSize.setProperty("value", 5)
572 self.SP_Verbosity.setProperty("value", 3)
573 self.SP_Memory.setProperty("value", 0)
574 #self.PBMeshSmeshPressed() #do not that! problem if done in load surfopt hypo from object browser
575 self.TWOptions.setCurrentIndex(0) # Reset current active tab to the first tab
580 This function returns a singleton instance of the plugin dialog.
581 It is mandatory in order to call show without a parent ...
585 __dialog = MonYamsPlugDialog()
591 # ==============================================================================
592 # Basic use cases and unit test functions
593 # ==============================================================================
595 def TEST_MonYamsPlugDialog():
597 from qtsalome import QApplication
598 app = QApplication(sys.argv)
599 app.lastWindowClosed.connect(app.quit)
601 dlg=MonYamsPlugDialog()
603 sys.exit(app.exec_())
605 if __name__ == "__main__":
606 TEST_MonYamsPlugDialog()