1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2011-2016 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
20 # Author : Guillaume Boulant (EDF)
23 from qtsalome import QDialog, QIcon, Qt
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
35 from salome.kernel import studyedit
36 from salome.kernel.uiexception import AdminException
38 from omniORB import CORBA
40 from salome.smesh import smeshBuilder
41 smesh = smeshBuilder.New(salome.myStudy)
44 gui_states = ["CAN_SELECT", "CAN_COMPUTE", "CAN_REFRESH", "CAN_PUBLISH"]
46 run_states = ["CREATED", "IN_PROCESS", "QUEUED", "RUNNING", "PAUSED"];
47 end_states = ["FINISHED", "ERROR"]
48 all_states = run_states+end_states;
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
58 class PluginDialog(QDialog):
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)
66 # The default display strategy is to use a separate dialog box
67 # to select the input data.
68 self.viewInputFrame(False)
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__))
74 icon.addFile(os.path.join(iconfolder,"input.png"))
75 self.__ui.btnInput.setIcon(icon)
77 icon.addFile(os.path.join(iconfolder,"compute.png"))
78 self.__ui.btnCompute.setIcon(icon)
80 icon.addFile(os.path.join(iconfolder,"refresh.png"))
81 self.__ui.btnRefresh.setIcon(icon)
83 icon.addFile(os.path.join(iconfolder,"publish.png"))
84 self.__ui.btnPublish.setIcon(icon)
86 icon.addFile(os.path.join(iconfolder,"clear.png"))
87 self.__ui.btnClear.setIcon(icon)
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 )
98 self.setupJobManager()
101 def setupJobManager(self):
103 This function configures the jobmanager by transmitting 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 specifying the name of the resource to
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)
123 # We specify the default configuration identifier as the
124 # resource name of the default configuration
125 self.__configId = configReader.getDefaultConfig().resname
128 def viewInputFrame(self, view=True):
129 # By default, the top input frame is visible and the input
130 # button is not visible.
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 )
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.
152 def getInputFrame(self):
153 return self.__ui.frameInput
155 def __setGuiState(self,states=["CAN_SELECT"]):
156 if "CAN_SELECT" in states:
157 self.__ui.btnInput.setEnabled(True)
159 self.__ui.btnInput.setEnabled(False)
161 if "CAN_COMPUTE" in states:
162 self.__ui.btnCompute.setEnabled(True)
164 self.__ui.btnCompute.setEnabled(False)
166 if "CAN_REFRESH" in states:
167 self.__ui.btnRefresh.setEnabled(True)
169 self.__ui.btnRefresh.setEnabled(False)
171 if "CAN_PUBLISH" in states:
172 self.__ui.btnPublish.setEnabled(True)
174 self.__ui.btnPublish.setEnabled(False)
176 def __getJobManager(self):
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.
182 if self.__dict__.has_key("__jobManager") and self.__jobManager is not None:
183 return self.__jobManager
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
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"
196 raise AdminException(msg)
198 self.__jobManager = component
199 return self.__jobManager
201 def __log(self, message):
203 This function prints the specified message in the log area
205 self.__ui.txtLog.append(message)
207 def __exportMesh(self, meshName, meshObject):
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.
213 filename=str("/tmp/padder_inputfile_"+meshName+".med")
214 meshObject.ExportToMEDX( filename, 0, SMESH.MED_V2_2, 1, 1 )
219 This function clears the log area and the states of the buttons
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")
230 This function can be used to programmatically force the
231 refresh of the dialog box, the job state in particular.
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).
247 dictInputData[INPUTDATA_KEY_FILES] = self.__listInputData
248 dictInputData[INPUTDATA_KEY_PARAM] = self.__dictInputParameters
249 self.__inputDialog.setData(dictInputData)
250 self.__inputDialog.open()
252 def onProcessInput(self):
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.
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]
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"])
270 This function is the slot connected to the Compute button. It
271 initializes a mesh computation job and start it using the
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:
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
286 filetype = MESHJOB.MED_STEELBAR
288 parameter = MESHJOB.MeshJobFile(
289 file_name = filename,
290 file_type = filetype,
291 group_name = inputData.groupName)
292 meshJobFileList.append(parameter)
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)
302 jobManager = self.__getJobManager()
303 self.__jobid = jobManager.initialize(meshJobFileList, meshJobParameterList, self.__configId)
305 self.__log("ERR: the job can't be initialized")
306 self.__log("ERR: %s"%jobManager.getLastErrorMessage())
308 self.__log("INF: the job has been initialized with jobid = "+str(self.__jobid))
310 startOk = jobManager.start(self.__jobid)
312 self.__log("ERR: the job with jobid = "+str(self.__jobid)+" can't be started")
313 self.__log("ERR: %s"%jobManager.getLastErrorMessage())
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
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.
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:
333 self.__isRunning = False
334 if state == "FINISHED":
335 self.__setGuiState(["CAN_PUBLISH"])
337 self.__setGuiState(["CAN_SELECT"])
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
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)+")")
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())
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)
366 self.__log("INF: jobid=%s ended normally (see log files in %s)"%(str(self.__jobid),logsdirname))
368 medfilename = os.path.join(meshJobResults.results_dirname,
369 meshJobResults.outputmesh_filename)
371 smesh.SetCurrentStudy(studyedit.getActiveStudy())
372 ([outputMesh], status) = smesh.CreateMeshesFromMED(medfilename)
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)
381 self.__ui.lblStatusBar.setText("Publication OK")
382 self.__setGuiState(["CAN_SELECT"])
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
397 This function returns a singleton instance of the plugin dialog.
401 __dialog = PluginDialog()
405 # ==============================================================================
406 # Basic use cases and unit test functions
407 # ==============================================================================
409 def TEST_PluginDialog():
411 from qtsalome import QApplication
412 app = QApplication(sys.argv)
413 app.lastWindowClosed.connect( app.quit )
418 if __name__ == "__main__":