1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2011 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
22 # Author : Guillaume Boulant (EDF)
25 from PyQt4.QtGui import QDialog, QIcon
26 from PyQt4.QtCore import QObject, SIGNAL, SLOT, Qt
28 from plugindialog_ui import Ui_PluginDialog
29 from inputdialog import InputDialog
30 from inputdata import InputData
31 # __GBO__: uncomment this line and comment the previous one to use the
32 # demo input dialog instead of the real one.
33 #from demoinputdialog import InputDialog
37 from salome.kernel import studyedit
38 from salome.kernel.uiexception import AdminException
40 from omniORB import CORBA
45 gui_states = ["CAN_SELECT", "CAN_COMPUTE", "CAN_REFRESH", "CAN_PUBLISH"]
47 run_states = ["CREATED", "IN_PROCESS", "QUEUED", "RUNNING", "PAUSED"];
48 end_states = ["FINISHED", "ERROR"]
49 all_states = run_states+end_states;
51 # The SALOME launcher resource is specified by its name as defined in
52 # the file CatalogResources.xml (see root directory of the
53 # application). We could have a check box in the dialog to specify
54 # wether we want a local execution or a remote one.
55 resource_name = "localhost"
56 from salome.smesh.spadder.configreader import ConfigReader
59 class PluginDialog(QDialog):
61 def __init__(self,parent = None,name = None,modal = 0,fl = 0):
62 QDialog.__init__(self,parent)
63 # Set up the user interface from Designer.
64 self.__ui = Ui_PluginDialog()
65 self.__ui.setupUi(self)
67 # The default display strategy is to use a separate dialog box
68 # to select the input data.
69 self.viewInputFrame(False)
71 # The icon are supposed to be located in the plugin folder,
72 # i.e. in the same folder than this python module file
73 iconfolder=os.path.dirname(os.path.abspath(__file__))
75 icon.addFile(os.path.join(iconfolder,"input.png"))
76 self.__ui.btnInput.setIcon(icon)
78 icon.addFile(os.path.join(iconfolder,"compute.png"))
79 self.__ui.btnCompute.setIcon(icon)
81 icon.addFile(os.path.join(iconfolder,"refresh.png"))
82 self.__ui.btnRefresh.setIcon(icon)
84 icon.addFile(os.path.join(iconfolder,"publish.png"))
85 self.__ui.btnPublish.setIcon(icon)
87 icon.addFile(os.path.join(iconfolder,"clear.png"))
88 self.__ui.btnClear.setIcon(icon)
90 # Then, we can connect the slot to there associated button event
91 self.connect(self.__ui.btnInput, SIGNAL('clicked()'), self.onInput )
92 self.connect(self.__ui.btnCompute, SIGNAL('clicked()'), self.onCompute )
93 self.connect(self.__ui.btnRefresh, SIGNAL('clicked()'), self.onRefresh )
94 self.connect(self.__ui.btnPublish, SIGNAL('clicked()'), self.onPublish )
95 self.connect(self.__ui.btnClear, SIGNAL('clicked()'), self.onClear )
99 self.setupJobManager()
102 def setupJobManager(self):
104 This function configures the jobmanager by transmiting the
105 parameters required for a local execution and a remote
106 execution. The choice between "local" and "remote" is done at
107 the initialize step, by specifing the name of the resource to
112 configReader = ConfigReader()
113 config = configReader.getLocalConfig()
114 configId = config.resname
115 self.__getJobManager().configure(configId, config)
116 # Note that the resname parameter is used as the key identifier of
117 # the configuration in the job manager. As is, there can be then
118 # only one configuration for each machine defined in the resources
119 # catalog (no need to have further, I thing)
120 config = configReader.getRemoteConfig()
121 configId = config.resname
122 self.__getJobManager().configure(configId, config)
124 # We specify the default configuration identifier as the
125 # resource name of the default configuration
126 self.__configId = configReader.getDefaultConfig().resname
129 def viewInputFrame(self, view=True):
130 # By default, the top input frame is visible and the input
131 # button is not visible.
133 self.__ui.frameInput.setVisible(False)
134 self.__ui.btnInput.setVisible(True)
135 # We create the input dialog that will be displayed when
136 # button input is pressed:
137 self.__inputDialog = InputDialog(self)
138 # The window is kept on the top to ease the selection of
139 # items in the object browser:
140 self.__inputDialog.setWindowFlags(
141 self.__inputDialog.windowFlags() | Qt.WindowStaysOnTopHint)
142 # The signal inputValidated emited from inputDialog is
143 # connected to the slot function onProcessInput:
144 self.connect(self.__inputDialog, SIGNAL('inputValidated()'), self.onProcessInput)
147 self.__ui.frameInput.setVisible(True)
148 self.__ui.btnInput.setVisible(False)
149 # This case is NOT IMPLEMENTED YET (not really). It could
150 # be used to draw the input frame directly in the frame
151 # frameInput of this dialog box.
153 def getInputFrame(self):
154 return self.__ui.frameInput
156 def __setGuiState(self,states=["CAN_SELECT"]):
157 if "CAN_SELECT" in states:
158 self.__ui.btnInput.setEnabled(True)
160 self.__ui.btnInput.setEnabled(False)
162 if "CAN_COMPUTE" in states:
163 self.__ui.btnCompute.setEnabled(True)
165 self.__ui.btnCompute.setEnabled(False)
167 if "CAN_REFRESH" in states:
168 self.__ui.btnRefresh.setEnabled(True)
170 self.__ui.btnRefresh.setEnabled(False)
172 if "CAN_PUBLISH" in states:
173 self.__ui.btnPublish.setEnabled(True)
175 self.__ui.btnPublish.setEnabled(False)
177 def __getJobManager(self):
179 This function requests a pointer to the MeshJobManager
180 servant. Note that the component is loaded on first demand,
181 and then the reference is recycled.
183 if self.__dict__.has_key("__jobManager") and self.__jobManager is not None:
184 return self.__jobManager
186 # WARN: we first have to update the SALOME components catalog
187 # with the SPADDER components (because they are not defined in
188 # the SMESH catalog, and then they are not in the default
190 from salome.smesh import spadder
191 spadder.loadSpadderCatalog()
192 # Then we can load the MeshJobManager component
193 component=salome.lcc.FindOrLoadComponent("FactoryServer","MeshJobManager")
194 if component is None:
195 msg="ERR: the SALOME component MeshJobManager can't be reached"
197 raise AdminException(msg)
199 self.__jobManager = component
200 return self.__jobManager
202 def __log(self, message):
204 This function prints the specified message in the log area
206 self.__ui.txtLog.append(message)
208 def __exportMesh(self, meshName, meshObject):
210 This function exports the specified mesh object to a med
211 file whose name (basepath) is built from the specified mesh
212 name. This returns the filename.
214 filename=str("/tmp/padder_inputfile_"+meshName+".med")
215 meshObject.ExportToMEDX( filename, 0, SMESH.MED_V2_2, 1 )
220 This function clears the log area and the states of the buttons
222 self.__listInputData = []
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).
246 self.__inputDialog.setData(self.__listInputData)
247 self.__inputDialog.open()
249 def onProcessInput(self):
251 This function is the slot connected to the signal
252 inputValidated(), emit by the input dialog window when it's
253 validated, i.e. OK is pressed and data are valid.
255 # The processing simply consists in requesting the input data
256 # from the dialog window.
257 self.__listInputData = self.__inputDialog.getData()
258 self.__ui.lblStatusBar.setText("Input data OK")
259 self.__log("INF: Press \"Compute\" to start the job")
260 self.__setGuiState(["CAN_SELECT", "CAN_COMPUTE"])
264 This function is the slot connected to the Compute button. It
265 initializes a mesh computation job and start it using the
268 # We first have to create the list of parameters for the
269 # initialize function. For that, we have to create the files
270 # from the mesh objects:
271 meshJobParameterList=[]
273 for inputData in self.__listInputData:
274 # For each input data, we have to create a
275 # MeshJobParameter and add it to the list.
276 filename = self.__exportMesh(inputData.meshName, inputData.meshObject)
277 if inputData.meshType == InputData.MESHTYPES.CONCRETE:
278 filetype = MESHJOB.MED_CONCRETE
280 filetype = MESHJOB.MED_STEELBAR
282 parameter = MESHJOB.MeshJobParameter(
283 file_name = filename,
284 file_type = filetype,
285 group_name = inputData.groupName)
286 meshJobParameterList.append(parameter)
288 jobManager = self.__getJobManager()
289 self.__jobid = jobManager.initialize(meshJobParameterList, self.__configId)
291 self.__log("ERR: the job can't be initialized")
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")
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
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.
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:
317 self.__isRunning = False
318 if state == "FINISHED":
319 self.__setGuiState(["CAN_PUBLISH"])
321 self.__setGuiState(["CAN_SELECT"])
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
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)+")")
337 if state not in end_states:
338 self.__log("ERR: jobid = "+str(self.__jobid)+" ended abnormally with state="+str(state))
341 meshJobResults = jobManager.finalize(self.__jobid)
343 self.__log("ERR: jobid = "+str(self.__jobid)+" ended with error: "+meshJobResults.status)
346 logsdirname = os.path.join(meshJobResults.results_dirname, "logs")
347 self.__log("INF: jobid="+str(self.__jobid)+" ended normally : "+meshJobResults.status)
348 self.__log("INF: jobid="+str(self.__jobid)+" see log files in : "+logsdirname)
350 medfilename = os.path.join(meshJobResults.results_dirname,
351 meshJobResults.outputmesh_filename)
353 smesh.SetCurrentStudy(studyedit.getActiveStudy())
354 ([outputMesh], status) = smesh.CreateMeshesFromMED(medfilename)
356 # By convention, the name of the output mesh in the study is
357 # build from a constant string 'padder' and the session jobid
358 meshname = 'padder_'+str(self.__jobid)
359 smesh.SetName(outputMesh.GetMesh(), meshname)
360 if salome.sg.hasDesktop():
361 salome.sg.updateObjBrowser(0)
363 self.__ui.lblStatusBar.setText("Publication OK")
364 self.__setGuiState(["CAN_SELECT"])
368 This function is the slot connected on the Clear button. It
369 erases data in the dialog box and cancel the current job if
379 This function returns a singleton instance of the plugin dialog.
383 __dialog = PluginDialog()
387 # ==============================================================================
388 # Basic use cases and unit test functions
389 # ==============================================================================
391 def TEST_PluginDialog():
393 from PyQt4.QtGui import QApplication
394 from PyQt4.QtCore import QObject, SIGNAL, SLOT
395 app = QApplication(sys.argv)
396 QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()"))
401 if __name__ == "__main__":