# -*- coding: iso-8859-1 -*- # Copyright (C) 2011 EDF R&D # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # # -* Makefile *- # # Author : Guillaume Boulant (EDF) # from PyQt4.QtGui import QDialog, QMessageBox, QIcon from PyQt4.QtCore import QObject, SIGNAL, SLOT, Qt from plugindialog_ui import Ui_PluginDialog from inputdialog import InputDialog from inputdata import InputData # __GBO__: uncomment this line and comment the previous one to use the # demo input dialog instead of the real one. #from demoinputdialog import InputDialog import os import salome from salome.kernel import studyedit from omniORB import CORBA import SMESH import smesh import MESHJOB gui_states = ["CAN_SELECT", "CAN_COMPUTE", "CAN_REFRESH", "CAN_PUBLISH"] run_states = ["CREATED", "IN_PROCESS", "QUEUED", "RUNNING", "PAUSED"]; end_states = ["FINISHED", "ERROR"] all_states = run_states+end_states; # The SALOME launcher resource is specified by its name as defined in # the file CatalogResources.xml (see root directory of the # application). We could have a check box in the dialog to specify # wether we want a local execution or a remote one. resource_name = "localhost" from salome.smesh.spadder.configreader import ConfigReader class PluginDialog(QDialog): def __init__(self,parent = None,name = None,modal = 0,fl = 0): QDialog.__init__(self,parent) # Set up the user interface from Designer. self.__ui = Ui_PluginDialog() self.__ui.setupUi(self) # The default display strategy is to use a separate dialog box # to select the input data. self.viewInputFrame(False) # The icon are supposed to be located in the plugin folder, # i.e. in the same folder than this python module file iconfolder=os.path.dirname(os.path.abspath(__file__)) icon = QIcon() icon.addFile(os.path.join(iconfolder,"input.png")) self.__ui.btnInput.setIcon(icon) icon = QIcon() icon.addFile(os.path.join(iconfolder,"compute.png")) self.__ui.btnCompute.setIcon(icon) icon = QIcon() icon.addFile(os.path.join(iconfolder,"refresh.png")) self.__ui.btnRefresh.setIcon(icon) icon = QIcon() icon.addFile(os.path.join(iconfolder,"publish.png")) self.__ui.btnPublish.setIcon(icon) icon = QIcon() icon.addFile(os.path.join(iconfolder,"clear.png")) self.__ui.btnClear.setIcon(icon) # Then, we can connect the slot to there associated button event self.connect(self.__ui.btnInput, SIGNAL('clicked()'), self.onInput ) self.connect(self.__ui.btnCompute, SIGNAL('clicked()'), self.onCompute ) self.connect(self.__ui.btnRefresh, SIGNAL('clicked()'), self.onRefresh ) self.connect(self.__ui.btnPublish, SIGNAL('clicked()'), self.onPublish ) self.connect(self.__ui.btnClear, SIGNAL('clicked()'), self.onClear ) self.clear() self.setupJobManager() def setupJobManager(self): ''' This function configures the jobmanager by transmiting the parameters required for a local execution and a remote execution. The choice between "local" and "remote" is done at the initialize step, by specifing the name of the resource to be used. ''' configReader = ConfigReader() config = configReader.getLocalConfig() configId = config.resname self.__getJobManager().configure(configId, config) # Note that the resname parameter is used as the key identifier of # the configuration in the job manager. As is, there can be then # only one configuration for each machine defined in the resources # catalog (no need to have further, I thing) config = configReader.getRemoteConfig() configId = config.resname self.__getJobManager().configure(configId, config) # We specify the default configuration identifier as the # resource name of the default configuration self.__configId = configReader.getDefaultConfig().resname def viewInputFrame(self, view=True): # By default, the top input frame is visible and the input # button is not visible. if view is False: self.__ui.frameInput.setVisible(False) self.__ui.btnInput.setVisible(True) # We create the input dialog that will be displayed when # button input is pressed: self.__inputDialog = InputDialog(self) # The window is kept on the top to ease the selection of # items in the object browser: self.__inputDialog.setWindowFlags( self.__inputDialog.windowFlags() | Qt.WindowStaysOnTopHint) # The signal inputValidated emited from inputDialog is # connected to the slot function onProcessInput: self.connect(self.__inputDialog, SIGNAL('inputValidated()'), self.onProcessInput) else: self.__ui.frameInput.setVisible(True) self.__ui.btnInput.setVisible(False) # This case is NOT IMPLEMENTED YET (not really). It could # be used to draw the input frame directly in the frame # frameInput of this dialog box. def getInputFrame(self): return self.__ui.frameInput def __setGuiState(self,states=["CAN_SELECT"]): if "CAN_SELECT" in states: self.__ui.btnInput.setEnabled(True) else: self.__ui.btnInput.setEnabled(False) if "CAN_COMPUTE" in states: self.__ui.btnCompute.setEnabled(True) else: self.__ui.btnCompute.setEnabled(False) if "CAN_REFRESH" in states: self.__ui.btnRefresh.setEnabled(True) else: self.__ui.btnRefresh.setEnabled(False) if "CAN_PUBLISH" in states: self.__ui.btnPublish.setEnabled(True) else: self.__ui.btnPublish.setEnabled(False) def __getJobManager(self): """ This function requests a pointer to the MeshJobManager servant. """ component=salome.lcc.FindOrLoadComponent("FactoryServer","MeshJobManager") if component is None: self.__log("ERR: the SALOME component MeshJobManager can't be reached") return component def __log(self, message): """ This function prints the specified message in the log area """ self.__ui.txtLog.append(message) def __exportMesh(self, meshName, meshObject): ''' This function exports the specified mesh object to a med file whose name (basepath) is built from the specified mesh name. This returns the filename. ''' filename=str("/tmp/padder_inputfile_"+meshName+".med") meshObject.ExportToMEDX( filename, 0, SMESH.MED_V2_2, 1 ) return filename def clear(self): """ This function clears the log area and the states of the buttons """ self.__listInputData = [] self.__ui.txtLog.clear() self.__setGuiState(["CAN_SELECT"]) self.__isRunning = False self.__ui.lblStatusBar.setText("Ready") def update(self): ''' This function can be used to programmatically force the refresh of the dialog box, the job state in particular. ''' if self.__isRunning: self.onRefresh() def onInput(self): ''' This function is the slot connected to the Input button (signal clicked()). It opens the dialog window to input data. The dialog is opened in a window modal mode so that the SALOME study objects can be selected. In conterpart, this class must listen to signals emitted by the child dialog windows to process the validation event (see the slot onProcessInput which is connected to this event). ''' self.__inputDialog.setData(self.__listInputData) self.__inputDialog.open() def onProcessInput(self): """ This function is the slot connected to the signal inputValidated(), emit by the input dialog window when it's validated, i.e. OK is pressed and data are valid. """ # The processing simply consists in requesting the input data # from the dialog window. self.__listInputData = self.__inputDialog.getData() self.__ui.lblStatusBar.setText("Input data OK") self.__setGuiState(["CAN_SELECT", "CAN_COMPUTE"]) def onCompute(self): ''' This function is the slot connected to the Compute button. It initializes a mesh computation job and start it using the SALOME launcher. ''' # We first have to create the list of parameters for the # initialize function. For that, we have to create the files # from the mesh objects: meshJobParameterList=[] concreteIndex=0 for inputData in self.__listInputData: # For each input data, we have to create a # MeshJobParameter and add it to the list. filename = self.__exportMesh(inputData.meshName, inputData.meshObject) if inputData.meshType == InputData.MESHTYPES.CONCRETE: filetype = MESHJOB.MED_CONCRETE else: filetype = MESHJOB.MED_STEELBAR parameter = MESHJOB.MeshJobParameter( file_name = filename, file_type = filetype, group_name = inputData.groupName) meshJobParameterList.append(parameter) jobManager = self.__getJobManager() self.__jobid = jobManager.initialize(meshJobParameterList, self.__configId) if self.__jobid < 0: self.__log("ERR: the job can't be initialized") return self.__log("INF: the job has been initialized with jobid = "+str(self.__jobid)) startOk = jobManager.start(self.__jobid) if not startOk: self.__log("ERR: the job with jobid = "+str(self.__jobid)+" can't be started") return self.__log("INF: the job "+str(self.__jobid)+" has been started") self.__ui.lblStatusBar.setText("Submission OK") self.__setGuiState(["CAN_REFRESH"]) self.__isRunning = True def onRefresh(self): """ This function is the slot connected on the Refresh button. It requests the mesh job manager to get the state of the job and display it in the log area. """ jobManager = self.__getJobManager() state = jobManager.getState(self.__jobid) self.__log("INF: job state = "+str(state)) self.__ui.lblStatusBar.setText("") if state in run_states: return self.__isRunning = False if state == "FINISHED": self.__setGuiState(["CAN_PUBLISH"]) else: self.__setGuiState(["CAN_SELECT"]) def onPublish(self): """ This function is the slot connected on the Publish button. It requests the mesh job manager to download the results data from the computation resource host and load the med file in the SALOME study. """ jobManager = self.__getJobManager() state = jobManager.getState(self.__jobid) if state in run_states: self.__log("WRN: jobid = "+str(self.__jobid)+" is not finished (state="+str(state)+")") return if state not in end_states: self.__log("ERR: jobid = "+str(self.__jobid)+" ended abnormally with state="+str(state)) return meshJobResults = jobManager.finalize(self.__jobid) if state == "ERROR": self.__log("ERR: jobid = "+str(self.__jobid)+" ended with error: "+meshJobResults.status) return logsdirname = os.path.join(meshJobResults.results_dirname, "logs") self.__log("INF: jobid="+str(self.__jobid)+" ended normally : "+meshJobResults.status) self.__log("INF: jobid="+str(self.__jobid)+" see log files in : "+logsdirname) medfilename = os.path.join(meshJobResults.results_dirname, meshJobResults.outputmesh_filename) smesh.SetCurrentStudy(studyedit.getActiveStudy()) ([outputMesh], status) = smesh.CreateMeshesFromMED(medfilename) # By convention, the name of the output mesh in the study is # build from a constant string 'padder' and the session jobid meshname = 'padder_'+str(self.__jobid) smesh.SetName(outputMesh.GetMesh(), meshname) if salome.sg.hasDesktop(): salome.sg.updateObjBrowser(0) self.__ui.lblStatusBar.setText("Publication OK") self.__setGuiState(["CAN_SELECT"]) def onClear(self): """ This function is the slot connected on the Clear button. It erases data in the dialog box and cancel the current job if one is running. """ self.clear() __dialog=None def getDialog(): """ This function returns a singleton instance of the plugin dialog. """ global __dialog if __dialog is None: __dialog = PluginDialog() return __dialog # # ============================================================================== # Basic use cases and unit test functions # ============================================================================== # def TEST_PluginDialog(): import sys from PyQt4.QtGui import QApplication from PyQt4.QtCore import QObject, SIGNAL, SLOT app = QApplication(sys.argv) QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()")) dlg=PluginDialog() dlg.exec_() if __name__ == "__main__": TEST_PluginDialog()