Salome HOME
Padder : fix RmaxRmin's variable name and update default values for NbIter and RmaxRmin
[modules/smesh.git] / src / Tools / padder / spadderpy / gui / plugindialog.py
1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2011-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 # Author : Guillaume Boulant (EDF)
21 #
22
23 from qtsalome import QDialog, QIcon, Qt
24
25 from plugindialog_ui import Ui_PluginDialog
26 from inputdialog import InputDialog, INPUTDATA_KEY_FILES, INPUTDATA_KEY_PARAM
27 from inputdialog import PARAM_KEY_NBITER, PARAM_KEY_RMAXRMIN
28 from inputdata import InputData
29 # __GBO__: uncomment this line and comment the previous one to use the
30 # demo input dialog instead of the real one.
31 #from demoinputdialog import InputDialog
32
33 import os
34 import salome
35 from salome.kernel import studyedit
36 from salome.kernel.uiexception import AdminException
37
38 from omniORB import CORBA
39 import SMESH
40 from salome.smesh import smeshBuilder
41 smesh = smeshBuilder.New(salome.myStudy)
42 import MESHJOB
43
44 gui_states = ["CAN_SELECT", "CAN_COMPUTE", "CAN_REFRESH", "CAN_PUBLISH"]
45
46 run_states = ["CREATED", "IN_PROCESS", "QUEUED", "RUNNING", "PAUSED"];
47 end_states = ["FINISHED", "ERROR"]
48 all_states = run_states+end_states;
49
50 # The SALOME launcher resource is specified by its name as defined in
51 # the file CatalogResources.xml (see root directory of the
52 # application). We could have a check box in the dialog to specify
53 # whether we want a local execution or a remote one.
54 resource_name = "localhost"
55 from salome.smesh.spadder.configreader import ConfigReader
56
57
58 class PluginDialog(QDialog):
59
60     def __init__(self,parent = None,name = None,modal = 0,fl = 0):
61         QDialog.__init__(self,parent)
62         # Set up the user interface from Designer.
63         self.__ui = Ui_PluginDialog()
64         self.__ui.setupUi(self)
65
66         # The default display strategy is to use a separate dialog box
67         # to select the input data.
68         self.viewInputFrame(False)
69
70         # The icon are supposed to be located in the plugin folder,
71         # i.e. in the same folder than this python module file
72         iconfolder=os.path.dirname(os.path.abspath(__file__))
73         icon = QIcon()
74         icon.addFile(os.path.join(iconfolder,"input.png"))
75         self.__ui.btnInput.setIcon(icon)
76         icon = QIcon()
77         icon.addFile(os.path.join(iconfolder,"compute.png"))
78         self.__ui.btnCompute.setIcon(icon)
79         icon = QIcon()
80         icon.addFile(os.path.join(iconfolder,"refresh.png"))
81         self.__ui.btnRefresh.setIcon(icon)
82         icon = QIcon()
83         icon.addFile(os.path.join(iconfolder,"publish.png"))
84         self.__ui.btnPublish.setIcon(icon)
85         icon = QIcon()
86         icon.addFile(os.path.join(iconfolder,"clear.png"))
87         self.__ui.btnClear.setIcon(icon)
88
89         # Then, we can connect the slot to there associated button event
90         self.__ui.btnInput.clicked.connect( self.onInput )
91         self.__ui.btnCompute.clicked.connect( self.onCompute )
92         self.__ui.btnRefresh.clicked.connect( self.onRefresh )
93         self.__ui.btnPublish.clicked.connect( self.onPublish )
94         self.__ui.btnClear.clicked.connect( self.onClear )
95
96         self.clear()
97
98         self.setupJobManager()
99
100
101     def setupJobManager(self):
102         '''
103         This function configures the jobmanager by transmiting the
104         parameters required for a local execution and a remote
105         execution. The choice between "local" and "remote" is done at
106         the initialize step, by specifing the name of the resource to
107         be used.
108         '''
109         # We first
110
111         configReader = ConfigReader()
112         config = configReader.getLocalConfig()
113         configId = config.resname
114         self.__getJobManager().configure(configId, config)
115         # Note that the resname parameter is used as the key identifier of
116         # the configuration in the job manager. As is, there can be then
117         # only one configuration for each machine defined in the resources
118         # catalog (no need to have further, I thing)
119         config = configReader.getRemoteConfig()
120         configId = config.resname
121         self.__getJobManager().configure(configId, config)
122
123         # We specify the default configuration identifier as the
124         # resource name of the default configuration
125         self.__configId = configReader.getDefaultConfig().resname
126
127
128     def viewInputFrame(self, view=True):
129         # By default, the top input frame is visible and the input
130         # button is not visible.
131         if view is False:
132             self.__ui.frameInput.setVisible(False)
133             self.__ui.btnInput.setVisible(True)
134             # We create the input dialog that will be displayed when
135             # button input is pressed:
136             self.__inputDialog = InputDialog(self)
137             # The window is kept on the top to ease the selection of
138             # items in the object browser:
139             self.__inputDialog.setWindowFlags(
140                 self.__inputDialog.windowFlags() | Qt.WindowStaysOnTopHint)
141             # The signal inputValidated emitted from inputDialog is
142             # connected to the slot function onProcessInput:
143             self.__inputDialog.inputValidated.connect( self.onProcessInput )
144
145         else:
146             self.__ui.frameInput.setVisible(True)
147             self.__ui.btnInput.setVisible(False)
148             # This case is NOT IMPLEMENTED YET (not really). It could
149             # be used to draw the input frame directly in the frame
150             # frameInput of this dialog box.
151
152     def getInputFrame(self):
153         return self.__ui.frameInput
154
155     def __setGuiState(self,states=["CAN_SELECT"]):
156         if "CAN_SELECT" in states:
157             self.__ui.btnInput.setEnabled(True)
158         else:
159             self.__ui.btnInput.setEnabled(False)
160
161         if "CAN_COMPUTE" in states:
162             self.__ui.btnCompute.setEnabled(True)
163         else:
164             self.__ui.btnCompute.setEnabled(False)
165
166         if "CAN_REFRESH" in states:
167             self.__ui.btnRefresh.setEnabled(True)
168         else:
169             self.__ui.btnRefresh.setEnabled(False)
170
171         if "CAN_PUBLISH" in states:
172             self.__ui.btnPublish.setEnabled(True)
173         else:
174             self.__ui.btnPublish.setEnabled(False)
175
176     def __getJobManager(self):
177         """
178         This function requests a pointer to the MeshJobManager
179         servant. Note that the component is loaded on first demand,
180         and then the reference is recycled.
181         """
182         if self.__dict__.has_key("__jobManager") and self.__jobManager is not None:
183             return self.__jobManager
184
185         # WARN: we first have to update the SALOME components catalog
186         # with the SPADDER components (because they are not defined in
187         # the SMESH catalog, and then they are not in the default
188         # catalog)
189         from salome.smesh import spadder
190         spadder.loadSpadderCatalog()
191         # Then we can load the MeshJobManager component
192         component=salome.lcc.FindOrLoadComponent("FactoryServer","MeshJobManager")
193         if component is None:
194             msg="ERR: the SALOME component MeshJobManager can't be reached"
195             self.__log(msg)
196             raise AdminException(msg)
197
198         self.__jobManager = component
199         return self.__jobManager
200
201     def __log(self, message):
202         """
203         This function prints the specified message in the log area
204         """
205         self.__ui.txtLog.append(message)
206
207     def __exportMesh(self, meshName, meshObject):
208         '''
209         This function exports the specified mesh object to a med
210         file whose name (basepath) is built from the specified mesh
211         name. This returns the filename.
212         '''
213         filename=str("/tmp/padder_inputfile_"+meshName+".med")
214         meshObject.ExportToMEDX( filename, 0, SMESH.MED_V2_2, 1, 1 )
215         return filename
216
217     def clear(self):
218         """
219         This function clears the log area and the states of the buttons
220         """
221         self.__listInputData = []
222         self.__dictInputParameters = {}
223         self.__ui.txtLog.clear()
224         self.__setGuiState(["CAN_SELECT"])
225         self.__isRunning = False
226         self.__ui.lblStatusBar.setText("Ready")
227
228     def update(self):
229         '''
230         This function can be used to programmatically force the
231         refresh of the dialog box, the job state in particular.
232         '''
233         if self.__isRunning:
234             self.onRefresh()
235
236     def onInput(self):
237         '''
238         This function is the slot connected to the Input button
239         (signal clicked()). It opens the dialog window to input
240         data. The dialog is opened in a window modal mode so that the
241         SALOME study objects can be selected. In conterpart, this
242         class must listen to signals emitted by the child dialog
243         windows to process the validation event (see the slot
244         onProcessInput which is connected to this event).
245         '''
246         dictInputData = {}
247         dictInputData[INPUTDATA_KEY_FILES] = self.__listInputData
248         dictInputData[INPUTDATA_KEY_PARAM] = self.__dictInputParameters
249         self.__inputDialog.setData(dictInputData)
250         self.__inputDialog.open()
251
252     def onProcessInput(self):
253         """
254         This function is the slot connected to the signal
255         inputValidated(), emit by the input dialog window when it's
256         validated, i.e. OK is pressed and data are valid.
257         """
258         # The processing simply consists in requesting the input data
259         # from the dialog window.
260         dictInputData = self.__inputDialog.getData()
261         self.__listInputData = dictInputData[INPUTDATA_KEY_FILES]
262         self.__dictInputParameters = dictInputData[INPUTDATA_KEY_PARAM]
263
264         self.__ui.lblStatusBar.setText("Input data OK")
265         self.__log("INF: Press \"Compute\" to start the job")
266         self.__setGuiState(["CAN_SELECT", "CAN_COMPUTE"])
267
268     def onCompute(self):
269         '''
270         This function is the slot connected to the Compute button. It
271         initializes a mesh computation job and start it using the
272         SALOME launcher.
273         '''
274         # We first have to create the list of parameters for the
275         # initialize function. For that, we have to create the files
276         # from the mesh objects:
277         meshJobFileList=[]
278         concreteIndex=0
279         for inputData in self.__listInputData:
280             # For each input data, we have to create a
281             # MeshJobFile and add it to the list.
282             filename  = self.__exportMesh(inputData.meshName, inputData.meshObject)
283             if inputData.meshType == InputData.MESHTYPES.CONCRETE:
284                 filetype = MESHJOB.MED_CONCRETE
285             else:
286                 filetype = MESHJOB.MED_STEELBAR
287
288             parameter = MESHJOB.MeshJobFile(
289                 file_name  = filename,
290                 file_type  = filetype,
291                 group_name = inputData.groupName)
292             meshJobFileList.append(parameter)
293
294         # And to create a list of the additional parameters.
295         # WARN: the CORBA interface requires string values.
296         meshJobParameterList=[]
297         for inputParameterKey in self.__dictInputParameters.keys():
298             value = self.__dictInputParameters[inputParameterKey]
299             parameter = MESHJOB.MeshJobParameter(name=inputParameterKey,value=str(value))
300             meshJobParameterList.append(parameter)
301
302         jobManager = self.__getJobManager()
303         self.__jobid = jobManager.initialize(meshJobFileList, meshJobParameterList, self.__configId)
304         if self.__jobid < 0:
305             self.__log("ERR: the job can't be initialized")
306             self.__log("ERR: %s"%jobManager.getLastErrorMessage())
307             return
308         self.__log("INF: the job has been initialized with jobid = "+str(self.__jobid))
309
310         startOk = jobManager.start(self.__jobid)
311         if not startOk:
312             self.__log("ERR: the job with jobid = "+str(self.__jobid)+" can't be started")
313             self.__log("ERR: %s"%jobManager.getLastErrorMessage())
314             return
315         self.__log("INF: the job "+str(self.__jobid)+" has been started")
316         self.__ui.lblStatusBar.setText("Submission OK")
317         self.__setGuiState(["CAN_REFRESH"])
318         self.__isRunning = True
319
320     def onRefresh(self):
321         """
322         This function is the slot connected on the Refresh button. It
323         requests the mesh job manager to get the state of the job and
324         display it in the log area.
325         """
326         jobManager = self.__getJobManager()
327         state = jobManager.getState(self.__jobid)
328         self.__log("INF: job state = "+str(state))
329         self.__ui.lblStatusBar.setText("")
330         if state in run_states:
331             return
332
333         self.__isRunning = False
334         if state == "FINISHED":
335             self.__setGuiState(["CAN_PUBLISH"])
336         else:
337             self.__setGuiState(["CAN_SELECT"])
338
339
340     def onPublish(self):
341         """
342         This function is the slot connected on the Publish button. It
343         requests the mesh job manager to download the results data
344         from the computation resource host and load the med file in
345         the SALOME study.
346         """
347         jobManager = self.__getJobManager()
348         state = jobManager.getState(self.__jobid)
349         if state in run_states:
350             self.__log("WRN: jobid = "+str(self.__jobid)+" is not finished (state="+str(state)+")")
351             return
352
353         if state not in end_states:
354             self.__log("ERR: jobid = "+str(self.__jobid)+" ended abnormally with state="+str(state))
355             self.__log("ERR: %s"%jobManager.getLastErrorMessage())
356             return
357
358         meshJobResults = jobManager.finalize(self.__jobid)
359         logsdirname = os.path.join(meshJobResults.results_dirname, "logs")
360         if state == "ERROR" or meshJobResults.status is not True:
361             msgtemp = "ERR: jobid = %s ended with error: %s"
362             self.__log(msgtemp%(str(self.__jobid),jobManager.getLastErrorMessage()))
363             self.__log("ERR: see log files in %s"%logsdirname)
364             return
365
366         self.__log("INF:  jobid=%s ended normally (see log files in %s)"%(str(self.__jobid),logsdirname))
367
368         medfilename = os.path.join(meshJobResults.results_dirname,
369                                    meshJobResults.outputmesh_filename)
370
371         smesh.SetCurrentStudy(studyedit.getActiveStudy())
372         ([outputMesh], status) = smesh.CreateMeshesFromMED(medfilename)
373
374         # By convention, the name of the output mesh in the study is
375         # build from a constant string 'padder' and the session jobid
376         meshname = 'padder_'+str(self.__jobid)
377         smesh.SetName(outputMesh.GetMesh(), meshname)
378         if salome.sg.hasDesktop():
379             salome.sg.updateObjBrowser(False)
380
381         self.__ui.lblStatusBar.setText("Publication OK")
382         self.__setGuiState(["CAN_SELECT"])
383
384     def onClear(self):
385         """
386         This function is the slot connected on the Clear button. It
387         erases data in the dialog box and cancel the current job if
388         one is running.
389         """
390         self.clear()
391
392
393
394 __dialog=None
395 def getDialog():
396     """
397     This function returns a singleton instance of the plugin dialog.
398     """
399     global __dialog
400     if __dialog is None:
401         __dialog = PluginDialog()
402     return __dialog
403
404 #
405 # ==============================================================================
406 # Basic use cases and unit test functions
407 # ==============================================================================
408 #
409 def TEST_PluginDialog():
410     import sys
411     from qtsalome import QApplication
412     app = QApplication(sys.argv)
413     app.lastWindowClosed.connect( app.quit )
414
415     dlg=PluginDialog()
416     dlg.exec_()
417
418 if __name__ == "__main__":
419     TEST_PluginDialog()
420
421
422