Back to index

salome-smesh  6.5.0
plugindialog.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 # Copyright (C) 2011-2012  EDF R&D
00003 #
00004 # This library is free software; you can redistribute it and/or
00005 # modify it under the terms of the GNU Lesser General Public
00006 # License as published by the Free Software Foundation; either
00007 # version 2.1 of the License.
00008 #
00009 # This library is distributed in the hope that it will be useful,
00010 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 # Lesser General Public License for more details.
00013 #
00014 # You should have received a copy of the GNU Lesser General Public
00015 # License along with this library; if not, write to the Free Software
00016 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00017 #
00018 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
00019 #
00020 # Author : Guillaume Boulant (EDF)
00021 #
00022 
00023 from PyQt4.QtGui import QDialog, QIcon
00024 from PyQt4.QtCore import QObject, SIGNAL, SLOT, Qt
00025 
00026 from plugindialog_ui import Ui_PluginDialog
00027 from inputdialog import InputDialog
00028 from inputdata import InputData
00029 # __GBO__: uncomment this line and comment the previous one to use the
00030 # demo input dialog instead of the real one.
00031 #from demoinputdialog import InputDialog
00032 
00033 import os
00034 import salome
00035 from salome.kernel import studyedit
00036 from salome.kernel.uiexception import AdminException
00037 
00038 from omniORB import CORBA
00039 import SMESH
00040 import smesh
00041 import MESHJOB
00042 
00043 gui_states = ["CAN_SELECT", "CAN_COMPUTE", "CAN_REFRESH", "CAN_PUBLISH"]
00044 
00045 run_states = ["CREATED", "IN_PROCESS", "QUEUED", "RUNNING", "PAUSED"];
00046 end_states = ["FINISHED", "ERROR"]
00047 all_states = run_states+end_states;
00048 
00049 # The SALOME launcher resource is specified by its name as defined in
00050 # the file CatalogResources.xml (see root directory of the
00051 # application). We could have a check box in the dialog to specify
00052 # wether we want a local execution or a remote one.
00053 resource_name = "localhost"
00054 from salome.smesh.spadder.configreader import ConfigReader
00055 
00056 
00057 class PluginDialog(QDialog):
00058 
00059     def __init__(self,parent = None,name = None,modal = 0,fl = 0):
00060         QDialog.__init__(self,parent)
00061         # Set up the user interface from Designer.
00062         self.__ui = Ui_PluginDialog()
00063         self.__ui.setupUi(self)
00064 
00065         # The default display strategy is to use a separate dialog box
00066         # to select the input data.
00067         self.viewInputFrame(False)
00068 
00069         # The icon are supposed to be located in the plugin folder,
00070         # i.e. in the same folder than this python module file
00071         iconfolder=os.path.dirname(os.path.abspath(__file__))
00072         icon = QIcon()
00073         icon.addFile(os.path.join(iconfolder,"input.png"))
00074         self.__ui.btnInput.setIcon(icon)
00075         icon = QIcon()
00076         icon.addFile(os.path.join(iconfolder,"compute.png"))
00077         self.__ui.btnCompute.setIcon(icon)
00078         icon = QIcon()
00079         icon.addFile(os.path.join(iconfolder,"refresh.png"))
00080         self.__ui.btnRefresh.setIcon(icon)
00081         icon = QIcon()
00082         icon.addFile(os.path.join(iconfolder,"publish.png"))
00083         self.__ui.btnPublish.setIcon(icon)
00084         icon = QIcon()
00085         icon.addFile(os.path.join(iconfolder,"clear.png"))
00086         self.__ui.btnClear.setIcon(icon)
00087 
00088         # Then, we can connect the slot to there associated button event
00089         self.connect(self.__ui.btnInput,       SIGNAL('clicked()'), self.onInput )
00090         self.connect(self.__ui.btnCompute,     SIGNAL('clicked()'), self.onCompute )
00091         self.connect(self.__ui.btnRefresh,     SIGNAL('clicked()'), self.onRefresh )
00092         self.connect(self.__ui.btnPublish,     SIGNAL('clicked()'), self.onPublish )
00093         self.connect(self.__ui.btnClear,       SIGNAL('clicked()'), self.onClear )
00094 
00095         self.clear()
00096 
00097         self.setupJobManager()
00098         
00099 
00100     def setupJobManager(self):
00101         '''
00102         This function configures the jobmanager by transmiting the
00103         parameters required for a local execution and a remote
00104         execution. The choice between "local" and "remote" is done at
00105         the initialize step, by specifing the name of the resource to
00106         be used.
00107         '''
00108         # We first 
00109         
00110         configReader = ConfigReader()
00111         config = configReader.getLocalConfig()
00112         configId = config.resname
00113         self.__getJobManager().configure(configId, config)
00114         # Note that the resname parameter is used as the key identifier of
00115         # the configuration in the job manager. As is, there can be then
00116         # only one configuration for each machine defined in the resources
00117         # catalog (no need to have further, I thing)
00118         config = configReader.getRemoteConfig()
00119         configId = config.resname
00120         self.__getJobManager().configure(configId, config)
00121 
00122         # We specify the default configuration identifier as the
00123         # resource name of the default configuration
00124         self.__configId = configReader.getDefaultConfig().resname
00125 
00126 
00127     def viewInputFrame(self, view=True):
00128         # By default, the top input frame is visible and the input
00129         # button is not visible.
00130         if view is False:
00131             self.__ui.frameInput.setVisible(False)
00132             self.__ui.btnInput.setVisible(True)
00133             # We create the input dialog that will be displayed when
00134             # button input is pressed:
00135             self.__inputDialog = InputDialog(self)
00136             # The window is kept on the top to ease the selection of
00137             # items in the object browser:
00138             self.__inputDialog.setWindowFlags(
00139                 self.__inputDialog.windowFlags() | Qt.WindowStaysOnTopHint)
00140             # The signal inputValidated emited from inputDialog is
00141             # connected to the slot function onProcessInput:
00142             self.connect(self.__inputDialog, SIGNAL('inputValidated()'), self.onProcessInput)
00143             
00144         else:
00145             self.__ui.frameInput.setVisible(True)
00146             self.__ui.btnInput.setVisible(False)
00147             # This case is NOT IMPLEMENTED YET (not really). It could
00148             # be used to draw the input frame directly in the frame
00149             # frameInput of this dialog box.
00150 
00151     def getInputFrame(self):
00152         return self.__ui.frameInput
00153         
00154     def __setGuiState(self,states=["CAN_SELECT"]):
00155         if "CAN_SELECT" in states:
00156             self.__ui.btnInput.setEnabled(True)
00157         else:
00158             self.__ui.btnInput.setEnabled(False)
00159             
00160         if "CAN_COMPUTE" in states:
00161             self.__ui.btnCompute.setEnabled(True)
00162         else:
00163             self.__ui.btnCompute.setEnabled(False)
00164 
00165         if "CAN_REFRESH" in states:
00166             self.__ui.btnRefresh.setEnabled(True)
00167         else:
00168             self.__ui.btnRefresh.setEnabled(False)
00169 
00170         if "CAN_PUBLISH" in states:
00171             self.__ui.btnPublish.setEnabled(True)
00172         else:
00173             self.__ui.btnPublish.setEnabled(False)
00174 
00175     def __getJobManager(self):
00176         """
00177         This function requests a pointer to the MeshJobManager
00178         servant. Note that the component is loaded on first demand,
00179         and then the reference is recycled.
00180         """
00181         if self.__dict__.has_key("__jobManager") and self.__jobManager is not None:
00182             return self.__jobManager
00183 
00184         # WARN: we first have to update the SALOME components catalog
00185         # with the SPADDER components (because they are not defined in
00186         # the SMESH catalog, and then they are not in the default
00187         # catalog)
00188         from salome.smesh import spadder
00189         spadder.loadSpadderCatalog()
00190         # Then we can load the MeshJobManager component
00191         component=salome.lcc.FindOrLoadComponent("FactoryServer","MeshJobManager")
00192         if component is None:
00193             msg="ERR: the SALOME component MeshJobManager can't be reached"
00194             self.__log(msg)
00195             raise AdminException(msg)
00196 
00197         self.__jobManager = component
00198         return self.__jobManager
00199 
00200     def __log(self, message):
00201         """
00202         This function prints the specified message in the log area
00203         """ 
00204         self.__ui.txtLog.append(message)
00205 
00206     def __exportMesh(self, meshName, meshObject):
00207         '''
00208         This function exports the specified mesh object to a med
00209         file whose name (basepath) is built from the specified mesh
00210         name. This returns the filename.
00211         '''
00212         filename=str("/tmp/padder_inputfile_"+meshName+".med")
00213         meshObject.ExportToMEDX( filename, 0, SMESH.MED_V2_2, 1 )
00214         return filename
00215 
00216     def clear(self):
00217         """
00218         This function clears the log area and the states of the buttons
00219         """
00220         self.__listInputData = []
00221         self.__ui.txtLog.clear()
00222         self.__setGuiState(["CAN_SELECT"])
00223         self.__isRunning = False
00224         self.__ui.lblStatusBar.setText("Ready")
00225 
00226     def update(self):
00227         '''
00228         This function can be used to programmatically force the
00229         refresh of the dialog box, the job state in particular.
00230         '''
00231         if self.__isRunning:
00232             self.onRefresh()
00233 
00234     def onInput(self):
00235         '''
00236         This function is the slot connected to the Input button
00237         (signal clicked()). It opens the dialog window to input
00238         data. The dialog is opened in a window modal mode so that the
00239         SALOME study objects can be selected. In conterpart, this
00240         class must listen to signals emitted by the child dialog
00241         windows to process the validation event (see the slot
00242         onProcessInput which is connected to this event).
00243         '''
00244         self.__inputDialog.setData(self.__listInputData)
00245         self.__inputDialog.open()
00246 
00247     def onProcessInput(self):
00248         """
00249         This function is the slot connected to the signal
00250         inputValidated(), emit by the input dialog window when it's
00251         validated, i.e. OK is pressed and data are valid.
00252         """
00253         # The processing simply consists in requesting the input data
00254         # from the dialog window.
00255         self.__listInputData = self.__inputDialog.getData()
00256         self.__ui.lblStatusBar.setText("Input data OK")
00257         self.__log("INF: Press \"Compute\" to start the job")
00258         self.__setGuiState(["CAN_SELECT", "CAN_COMPUTE"])
00259         
00260     def onCompute(self):
00261         '''
00262         This function is the slot connected to the Compute button. It
00263         initializes a mesh computation job and start it using the
00264         SALOME launcher.  
00265         '''
00266         # We first have to create the list of parameters for the
00267         # initialize function. For that, we have to create the files
00268         # from the mesh objects:
00269         meshJobParameterList=[]
00270         concreteIndex=0
00271         for inputData in self.__listInputData:
00272             # For each input data, we have to create a
00273             # MeshJobParameter and add it to the list.
00274             filename  = self.__exportMesh(inputData.meshName, inputData.meshObject)
00275             if inputData.meshType == InputData.MESHTYPES.CONCRETE:
00276                 filetype = MESHJOB.MED_CONCRETE
00277             else:
00278                 filetype = MESHJOB.MED_STEELBAR
00279 
00280             parameter = MESHJOB.MeshJobParameter(
00281                 file_name  = filename,
00282                 file_type  = filetype,
00283                 group_name = inputData.groupName)
00284             meshJobParameterList.append(parameter)
00285 
00286         jobManager = self.__getJobManager()
00287         self.__jobid = jobManager.initialize(meshJobParameterList, self.__configId)
00288         if self.__jobid < 0:
00289             self.__log("ERR: the job can't be initialized")
00290             return
00291         self.__log("INF: the job has been initialized with jobid = "+str(self.__jobid))
00292         
00293         startOk = jobManager.start(self.__jobid)
00294         if not startOk:
00295             self.__log("ERR: the job with jobid = "+str(self.__jobid)+" can't be started")
00296             return
00297         self.__log("INF: the job "+str(self.__jobid)+" has been started")
00298         self.__ui.lblStatusBar.setText("Submission OK")
00299         self.__setGuiState(["CAN_REFRESH"])
00300         self.__isRunning = True
00301 
00302     def onRefresh(self):
00303         """
00304         This function is the slot connected on the Refresh button. It
00305         requests the mesh job manager to get the state of the job and
00306         display it in the log area.
00307         """
00308         jobManager = self.__getJobManager()
00309         state = jobManager.getState(self.__jobid)
00310         self.__log("INF: job state = "+str(state))
00311         self.__ui.lblStatusBar.setText("")
00312         if state in run_states:
00313             return
00314 
00315         self.__isRunning = False
00316         if state == "FINISHED":
00317             self.__setGuiState(["CAN_PUBLISH"])
00318         else:
00319             self.__setGuiState(["CAN_SELECT"])
00320 
00321 
00322     def onPublish(self):
00323         """
00324         This function is the slot connected on the Publish button. It
00325         requests the mesh job manager to download the results data
00326         from the computation resource host and load the med file in
00327         the SALOME study. 
00328         """
00329         jobManager = self.__getJobManager()
00330         state = jobManager.getState(self.__jobid)
00331         if state in run_states:
00332             self.__log("WRN: jobid = "+str(self.__jobid)+" is not finished (state="+str(state)+")")
00333             return
00334 
00335         if state not in end_states:
00336             self.__log("ERR: jobid = "+str(self.__jobid)+" ended abnormally with state="+str(state))
00337             return
00338 
00339         meshJobResults = jobManager.finalize(self.__jobid)
00340         if state == "ERROR":
00341             self.__log("ERR: jobid = "+str(self.__jobid)+" ended with error: "+meshJobResults.status)
00342             return
00343 
00344         logsdirname = os.path.join(meshJobResults.results_dirname, "logs")
00345         self.__log("INF:  jobid="+str(self.__jobid)+" ended normally   : "+meshJobResults.status)
00346         self.__log("INF:  jobid="+str(self.__jobid)+" see log files in : "+logsdirname)
00347 
00348         medfilename = os.path.join(meshJobResults.results_dirname,
00349                                    meshJobResults.outputmesh_filename)
00350 
00351         smesh.SetCurrentStudy(studyedit.getActiveStudy())
00352         ([outputMesh], status) = smesh.CreateMeshesFromMED(medfilename)
00353 
00354         # By convention, the name of the output mesh in the study is
00355         # build from a constant string 'padder' and the session jobid
00356         meshname = 'padder_'+str(self.__jobid)
00357         smesh.SetName(outputMesh.GetMesh(), meshname)
00358         if salome.sg.hasDesktop():
00359             salome.sg.updateObjBrowser(0)
00360 
00361         self.__ui.lblStatusBar.setText("Publication OK")
00362         self.__setGuiState(["CAN_SELECT"])
00363 
00364     def onClear(self):
00365         """
00366         This function is the slot connected on the Clear button. It
00367         erases data in the dialog box and cancel the current job if
00368         one is running.
00369         """
00370         self.clear()
00371         
00372 
00373 
00374 __dialog=None
00375 def getDialog():
00376     """
00377     This function returns a singleton instance of the plugin dialog. 
00378     """
00379     global __dialog
00380     if __dialog is None:
00381         __dialog = PluginDialog()
00382     return __dialog
00383 
00384 #
00385 # ==============================================================================
00386 # Basic use cases and unit test functions
00387 # ==============================================================================
00388 #
00389 def TEST_PluginDialog():
00390     import sys
00391     from PyQt4.QtGui import QApplication
00392     from PyQt4.QtCore import QObject, SIGNAL, SLOT
00393     app = QApplication(sys.argv)
00394     QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()"))
00395 
00396     dlg=PluginDialog()
00397     dlg.exec_()
00398 
00399 if __name__ == "__main__":
00400     TEST_PluginDialog()
00401 
00402         
00403