1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2011-2012 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.
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
43 gui_states = ["CAN_SELECT", "CAN_COMPUTE", "CAN_REFRESH", "CAN_PUBLISH"]
45 run_states = ["CREATED", "IN_PROCESS", "QUEUED", "RUNNING", "PAUSED"];
46 end_states = ["FINISHED", "ERROR"]
47 all_states = run_states+end_states;
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 # wether we want a local execution or a remote one.
53 resource_name = "localhost"
54 from salome.smesh.spadder.configreader import ConfigReader
57 class PluginDialog(QDialog):
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)
65 # The default display strategy is to use a separate dialog box
66 # to select the input data.
67 self.viewInputFrame(False)
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__))
73 icon.addFile(os.path.join(iconfolder,"input.png"))
74 self.__ui.btnInput.setIcon(icon)
76 icon.addFile(os.path.join(iconfolder,"compute.png"))
77 self.__ui.btnCompute.setIcon(icon)
79 icon.addFile(os.path.join(iconfolder,"refresh.png"))
80 self.__ui.btnRefresh.setIcon(icon)
82 icon.addFile(os.path.join(iconfolder,"publish.png"))
83 self.__ui.btnPublish.setIcon(icon)
85 icon.addFile(os.path.join(iconfolder,"clear.png"))
86 self.__ui.btnClear.setIcon(icon)
88 # Then, we can connect the slot to there associated button event
89 self.connect(self.__ui.btnInput, SIGNAL('clicked()'), self.onInput )
90 self.connect(self.__ui.btnCompute, SIGNAL('clicked()'), self.onCompute )
91 self.connect(self.__ui.btnRefresh, SIGNAL('clicked()'), self.onRefresh )
92 self.connect(self.__ui.btnPublish, SIGNAL('clicked()'), self.onPublish )
93 self.connect(self.__ui.btnClear, SIGNAL('clicked()'), self.onClear )
97 self.setupJobManager()
100 def setupJobManager(self):
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
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)
122 # We specify the default configuration identifier as the
123 # resource name of the default configuration
124 self.__configId = configReader.getDefaultConfig().resname
127 def viewInputFrame(self, view=True):
128 # By default, the top input frame is visible and the input
129 # button is not visible.
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.connect(self.__inputDialog, SIGNAL('inputValidated()'), self.onProcessInput)
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.
151 def getInputFrame(self):
152 return self.__ui.frameInput
154 def __setGuiState(self,states=["CAN_SELECT"]):
155 if "CAN_SELECT" in states:
156 self.__ui.btnInput.setEnabled(True)
158 self.__ui.btnInput.setEnabled(False)
160 if "CAN_COMPUTE" in states:
161 self.__ui.btnCompute.setEnabled(True)
163 self.__ui.btnCompute.setEnabled(False)
165 if "CAN_REFRESH" in states:
166 self.__ui.btnRefresh.setEnabled(True)
168 self.__ui.btnRefresh.setEnabled(False)
170 if "CAN_PUBLISH" in states:
171 self.__ui.btnPublish.setEnabled(True)
173 self.__ui.btnPublish.setEnabled(False)
175 def __getJobManager(self):
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.
181 if self.__dict__.has_key("__jobManager") and self.__jobManager is not None:
182 return self.__jobManager
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
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"
195 raise AdminException(msg)
197 self.__jobManager = component
198 return self.__jobManager
200 def __log(self, message):
202 This function prints the specified message in the log area
204 self.__ui.txtLog.append(message)
206 def __exportMesh(self, meshName, meshObject):
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.
212 filename=str("/tmp/padder_inputfile_"+meshName+".med")
213 meshObject.ExportToMEDX( filename, 0, SMESH.MED_V2_2, 1 )
218 This function clears the log area and the states of the buttons
220 self.__listInputData = []
221 self.__ui.txtLog.clear()
222 self.__setGuiState(["CAN_SELECT"])
223 self.__isRunning = False
224 self.__ui.lblStatusBar.setText("Ready")
228 This function can be used to programmatically force the
229 refresh of the dialog box, the job state in particular.
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).
244 self.__inputDialog.setData(self.__listInputData)
245 self.__inputDialog.open()
247 def onProcessInput(self):
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.
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"])
262 This function is the slot connected to the Compute button. It
263 initializes a mesh computation job and start it using the
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=[]
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
278 filetype = MESHJOB.MED_STEELBAR
280 parameter = MESHJOB.MeshJobParameter(
281 file_name = filename,
282 file_type = filetype,
283 group_name = inputData.groupName)
284 meshJobParameterList.append(parameter)
286 jobManager = self.__getJobManager()
287 self.__jobid = jobManager.initialize(meshJobParameterList, self.__configId)
289 self.__log("ERR: the job can't be initialized")
291 self.__log("INF: the job has been initialized with jobid = "+str(self.__jobid))
293 startOk = jobManager.start(self.__jobid)
295 self.__log("ERR: the job with jobid = "+str(self.__jobid)+" can't be started")
297 self.__log("INF: the job "+str(self.__jobid)+" has been started")
298 self.__ui.lblStatusBar.setText("Submission OK")
299 self.__setGuiState(["CAN_REFRESH"])
300 self.__isRunning = True
304 This function is the slot connected on the Refresh button. It
305 requests the mesh job manager to get the state of the job and
306 display it in the log area.
308 jobManager = self.__getJobManager()
309 state = jobManager.getState(self.__jobid)
310 self.__log("INF: job state = "+str(state))
311 self.__ui.lblStatusBar.setText("")
312 if state in run_states:
315 self.__isRunning = False
316 if state == "FINISHED":
317 self.__setGuiState(["CAN_PUBLISH"])
319 self.__setGuiState(["CAN_SELECT"])
324 This function is the slot connected on the Publish button. It
325 requests the mesh job manager to download the results data
326 from the computation resource host and load the med file in
329 jobManager = self.__getJobManager()
330 state = jobManager.getState(self.__jobid)
331 if state in run_states:
332 self.__log("WRN: jobid = "+str(self.__jobid)+" is not finished (state="+str(state)+")")
335 if state not in end_states:
336 self.__log("ERR: jobid = "+str(self.__jobid)+" ended abnormally with state="+str(state))
339 meshJobResults = jobManager.finalize(self.__jobid)
341 self.__log("ERR: jobid = "+str(self.__jobid)+" ended with error: "+meshJobResults.status)
344 logsdirname = os.path.join(meshJobResults.results_dirname, "logs")
345 self.__log("INF: jobid="+str(self.__jobid)+" ended normally : "+meshJobResults.status)
346 self.__log("INF: jobid="+str(self.__jobid)+" see log files in : "+logsdirname)
348 medfilename = os.path.join(meshJobResults.results_dirname,
349 meshJobResults.outputmesh_filename)
351 smesh.SetCurrentStudy(studyedit.getActiveStudy())
352 ([outputMesh], status) = smesh.CreateMeshesFromMED(medfilename)
354 # By convention, the name of the output mesh in the study is
355 # build from a constant string 'padder' and the session jobid
356 meshname = 'padder_'+str(self.__jobid)
357 smesh.SetName(outputMesh.GetMesh(), meshname)
358 if salome.sg.hasDesktop():
359 salome.sg.updateObjBrowser(0)
361 self.__ui.lblStatusBar.setText("Publication OK")
362 self.__setGuiState(["CAN_SELECT"])
366 This function is the slot connected on the Clear button. It
367 erases data in the dialog box and cancel the current job if
377 This function returns a singleton instance of the plugin dialog.
381 __dialog = PluginDialog()
385 # ==============================================================================
386 # Basic use cases and unit test functions
387 # ==============================================================================
389 def TEST_PluginDialog():
391 from PyQt4.QtGui import QApplication
392 from PyQt4.QtCore import QObject, SIGNAL, SLOT
393 app = QApplication(sys.argv)
394 QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()"))
399 if __name__ == "__main__":