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
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
34 from salome.kernel import studyedit
35 from salome.kernel.uiexception import AdminException
37 from omniORB import CORBA
39 from salome.smesh import smeshBuilder
40 smesh = smeshBuilder.New(salome.myStudy)
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 # whether 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.__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 )
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.__inputDialog.inputValidated.connect( 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, 1, 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")
290 self.__log("ERR: %s"%jobManager.getLastErrorMessage())
292 self.__log("INF: the job has been initialized with jobid = "+str(self.__jobid))
294 startOk = jobManager.start(self.__jobid)
296 self.__log("ERR: the job with jobid = "+str(self.__jobid)+" can't be started")
297 self.__log("ERR: %s"%jobManager.getLastErrorMessage())
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))
339 self.__log("ERR: %s"%jobManager.getLastErrorMessage())
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)
350 self.__log("INF: jobid=%s ended normally (see log files in %s)"%(str(self.__jobid),logsdirname))
352 medfilename = os.path.join(meshJobResults.results_dirname,
353 meshJobResults.outputmesh_filename)
355 smesh.SetCurrentStudy(studyedit.getActiveStudy())
356 ([outputMesh], status) = smesh.CreateMeshesFromMED(medfilename)
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(False)
365 self.__ui.lblStatusBar.setText("Publication OK")
366 self.__setGuiState(["CAN_SELECT"])
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
381 This function returns a singleton instance of the plugin dialog.
385 __dialog = PluginDialog()
389 # ==============================================================================
390 # Basic use cases and unit test functions
391 # ==============================================================================
393 def TEST_PluginDialog():
395 from qtsalome import QApplication
396 app = QApplication(sys.argv)
397 app.lastWindowClosed.connect( app.quit )
402 if __name__ == "__main__":