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