1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2011-2014 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 PyQt4.QtGui import QDialog, QIcon
24 from PyQt4.QtCore import QObject, SIGNAL, SLOT, Qt
26 from plugindialog_ui import Ui_PluginDialog
27 from inputdialog import InputDialog
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 # wether 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.connect(self.__ui.btnInput, SIGNAL('clicked()'), self.onInput )
91 self.connect(self.__ui.btnCompute, SIGNAL('clicked()'), self.onCompute )
92 self.connect(self.__ui.btnRefresh, SIGNAL('clicked()'), self.onRefresh )
93 self.connect(self.__ui.btnPublish, SIGNAL('clicked()'), self.onPublish )
94 self.connect(self.__ui.btnClear, SIGNAL('clicked()'), self.onClear )
98 self.setupJobManager()
101 def setupJobManager(self):
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
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 emited from inputDialog is
142 # connected to the slot function onProcessInput:
143 self.connect(self.__inputDialog, SIGNAL('inputValidated()'), 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.__ui.txtLog.clear()
223 self.__setGuiState(["CAN_SELECT"])
224 self.__isRunning = False
225 self.__ui.lblStatusBar.setText("Ready")
229 This function can be used to programmatically force the
230 refresh of the dialog box, the job state in particular.
237 This function is the slot connected to the Input button
238 (signal clicked()). It opens the dialog window to input
239 data. The dialog is opened in a window modal mode so that the
240 SALOME study objects can be selected. In conterpart, this
241 class must listen to signals emitted by the child dialog
242 windows to process the validation event (see the slot
243 onProcessInput which is connected to this event).
245 self.__inputDialog.setData(self.__listInputData)
246 self.__inputDialog.open()
248 def onProcessInput(self):
250 This function is the slot connected to the signal
251 inputValidated(), emit by the input dialog window when it's
252 validated, i.e. OK is pressed and data are valid.
254 # The processing simply consists in requesting the input data
255 # from the dialog window.
256 self.__listInputData = self.__inputDialog.getData()
257 self.__ui.lblStatusBar.setText("Input data OK")
258 self.__log("INF: Press \"Compute\" to start the job")
259 self.__setGuiState(["CAN_SELECT", "CAN_COMPUTE"])
263 This function is the slot connected to the Compute button. It
264 initializes a mesh computation job and start it using the
267 # We first have to create the list of parameters for the
268 # initialize function. For that, we have to create the files
269 # from the mesh objects:
270 meshJobParameterList=[]
272 for inputData in self.__listInputData:
273 # For each input data, we have to create a
274 # MeshJobParameter and add it to the list.
275 filename = self.__exportMesh(inputData.meshName, inputData.meshObject)
276 if inputData.meshType == InputData.MESHTYPES.CONCRETE:
277 filetype = MESHJOB.MED_CONCRETE
279 filetype = MESHJOB.MED_STEELBAR
281 parameter = MESHJOB.MeshJobParameter(
282 file_name = filename,
283 file_type = filetype,
284 group_name = inputData.groupName)
285 meshJobParameterList.append(parameter)
287 jobManager = self.__getJobManager()
288 self.__jobid = jobManager.initialize(meshJobParameterList, self.__configId)
290 self.__log("ERR: the job can't be initialized")
291 self.__log("ERR: %s"%jobManager.getLastErrorMessage())
293 self.__log("INF: the job has been initialized with jobid = "+str(self.__jobid))
295 startOk = jobManager.start(self.__jobid)
297 self.__log("ERR: the job with jobid = "+str(self.__jobid)+" can't be started")
298 self.__log("ERR: %s"%jobManager.getLastErrorMessage())
300 self.__log("INF: the job "+str(self.__jobid)+" has been started")
301 self.__ui.lblStatusBar.setText("Submission OK")
302 self.__setGuiState(["CAN_REFRESH"])
303 self.__isRunning = True
307 This function is the slot connected on the Refresh button. It
308 requests the mesh job manager to get the state of the job and
309 display it in the log area.
311 jobManager = self.__getJobManager()
312 state = jobManager.getState(self.__jobid)
313 self.__log("INF: job state = "+str(state))
314 self.__ui.lblStatusBar.setText("")
315 if state in run_states:
318 self.__isRunning = False
319 if state == "FINISHED":
320 self.__setGuiState(["CAN_PUBLISH"])
322 self.__setGuiState(["CAN_SELECT"])
327 This function is the slot connected on the Publish button. It
328 requests the mesh job manager to download the results data
329 from the computation resource host and load the med file in
332 jobManager = self.__getJobManager()
333 state = jobManager.getState(self.__jobid)
334 if state in run_states:
335 self.__log("WRN: jobid = "+str(self.__jobid)+" is not finished (state="+str(state)+")")
338 if state not in end_states:
339 self.__log("ERR: jobid = "+str(self.__jobid)+" ended abnormally with state="+str(state))
340 self.__log("ERR: %s"%jobManager.getLastErrorMessage())
343 meshJobResults = jobManager.finalize(self.__jobid)
344 logsdirname = os.path.join(meshJobResults.results_dirname, "logs")
345 if state == "ERROR" or meshJobResults.status is not True:
346 msgtemp = "ERR: jobid = %s ended with error: %s"
347 self.__log(msgtemp%(str(self.__jobid),jobManager.getLastErrorMessage()))
348 self.__log("ERR: see log files in %s"%logsdirname)
351 self.__log("INF: jobid=%s ended normally (see log files in %s)"%(str(self.__jobid),logsdirname))
353 medfilename = os.path.join(meshJobResults.results_dirname,
354 meshJobResults.outputmesh_filename)
356 smesh.SetCurrentStudy(studyedit.getActiveStudy())
357 ([outputMesh], status) = smesh.CreateMeshesFromMED(medfilename)
359 # By convention, the name of the output mesh in the study is
360 # build from a constant string 'padder' and the session jobid
361 meshname = 'padder_'+str(self.__jobid)
362 smesh.SetName(outputMesh.GetMesh(), meshname)
363 if salome.sg.hasDesktop():
364 salome.sg.updateObjBrowser(0)
366 self.__ui.lblStatusBar.setText("Publication OK")
367 self.__setGuiState(["CAN_SELECT"])
371 This function is the slot connected on the Clear button. It
372 erases data in the dialog box and cancel the current job if
382 This function returns a singleton instance of the plugin dialog.
386 __dialog = PluginDialog()
390 # ==============================================================================
391 # Basic use cases and unit test functions
392 # ==============================================================================
394 def TEST_PluginDialog():
396 from PyQt4.QtGui import QApplication
397 from PyQt4.QtCore import QObject, SIGNAL, SLOT
398 app = QApplication(sys.argv)
399 QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()"))
404 if __name__ == "__main__":