From 3c82f85c3bef9feea43b3f3f248ee73411a77c82 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Andr=C3=A9?= Date: Tue, 27 Apr 2010 17:17:47 +0200 Subject: [PATCH] Premier ebauche etude --- configure.ac | 2 + src/daSalome/daGUI/Makefile.am | 2 +- .../daGUI/daEficasWrapper/Makefile.am | 32 ++ .../daGUI/daEficasWrapper/__init__.py | 0 .../daEficasWrapper/datassimEficasWrapper.py | 58 +++ .../daGUI/daEficasWrapper/eficasWrapper.py.in | 278 +++++++++++ src/daSalome/daGUI/daGuiImpl/Makefile.am | 4 + .../daGUI/daGuiImpl/datassimGuiHelper.py | 121 +++++ .../daGUI/daGuiImpl/datassimGuiManager.py | 56 ++- .../daGUI/daGuiImpl/datassimModuleHelper.py | 211 +++++++++ .../daGUI/daGuiImpl/datassimStudyEditor.py | 122 +++++ src/daSalome/daGUI/daGuiImpl/studyedit.py | 443 ++++++++++++++++++ 12 files changed, 1325 insertions(+), 4 deletions(-) create mode 100644 src/daSalome/daGUI/daEficasWrapper/Makefile.am create mode 100644 src/daSalome/daGUI/daEficasWrapper/__init__.py create mode 100644 src/daSalome/daGUI/daEficasWrapper/datassimEficasWrapper.py create mode 100644 src/daSalome/daGUI/daEficasWrapper/eficasWrapper.py.in create mode 100644 src/daSalome/daGUI/daGuiImpl/datassimGuiHelper.py create mode 100644 src/daSalome/daGUI/daGuiImpl/datassimModuleHelper.py create mode 100644 src/daSalome/daGUI/daGuiImpl/datassimStudyEditor.py create mode 100644 src/daSalome/daGUI/daGuiImpl/studyedit.py diff --git a/configure.ac b/configure.ac index 17c0723..ac4803c 100644 --- a/configure.ac +++ b/configure.ac @@ -71,6 +71,8 @@ AC_CONFIG_FILES([ src/daSalome/Makefile src/daSalome/daGUI/Makefile src/daSalome/daGUI/daGuiImpl/Makefile + src/daSalome/daGUI/daEficasWrapper/Makefile + src/daSalome/daGUI/daEficasWrapper/eficasWrapper.py bin/Makefile bin/qtEficas_datassim_study.py ]) diff --git a/src/daSalome/daGUI/Makefile.am b/src/daSalome/daGUI/Makefile.am index f71a46b..738c844 100644 --- a/src/daSalome/daGUI/Makefile.am +++ b/src/daSalome/daGUI/Makefile.am @@ -17,7 +17,7 @@ # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # -SUBDIRS = daGuiImpl +SUBDIRS = daGuiImpl daEficasWrapper include $(top_srcdir)/adm_local/make_common_starter.am diff --git a/src/daSalome/daGUI/daEficasWrapper/Makefile.am b/src/daSalome/daGUI/daEficasWrapper/Makefile.am new file mode 100644 index 0000000..7b0f6a3 --- /dev/null +++ b/src/daSalome/daGUI/daEficasWrapper/Makefile.am @@ -0,0 +1,32 @@ +# Copyright (C) 2010 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 +# + +include $(top_srcdir)/adm_local/make_common_starter.am + +mypkgpythondir =$(salomepythondir)/daEficasWrapper + +mypkgpython_PYTHON = \ + eficasWrapper.py \ + __init__.py \ + datassimEficasWrapper.py + +EXTRA_DIST = eficasWrapper.py.in + + + diff --git a/src/daSalome/daGUI/daEficasWrapper/__init__.py b/src/daSalome/daGUI/daEficasWrapper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/daSalome/daGUI/daEficasWrapper/datassimEficasWrapper.py b/src/daSalome/daGUI/daEficasWrapper/datassimEficasWrapper.py new file mode 100644 index 0000000..5963037 --- /dev/null +++ b/src/daSalome/daGUI/daEficasWrapper/datassimEficasWrapper.py @@ -0,0 +1,58 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2010 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 +# + +from eficasWrapper import * +import sys + +# Configuration de l'installation +my_path = os.path.dirname(os.path.abspath(__file__)) +DATASSIM_INSTALL_DIR = my_path + "/../daEficas" +sys.path[:0]=[DATASSIM_INSTALL_DIR] + +# +# ================================================ +# Specialization of the EficasWrapper for DATASSIM +# ================================================ +# +class DatassimEficasWrapper(EficasWrapper): + def __init__(self, parent, code="DATASSIM"): + EficasWrapper.__init__(self, parent, code) + + def fileSave(self): + """ + @overload + """ + qtEficas.Appli.fileSave(self) + self.notifyObserver(EficasEvent.EVENT_TYPES.SAVE) + + def fileSaveAs(self): + """ + @overload + """ + qtEficas.Appli.fileSaveAs(self) + self.notifyObserver(EficasEvent.EVENT_TYPES.SAVE) + + def getCurrentFileName(self): + index = self.viewmanager.myQtab.currentIndex() + print index + rtn = "" + if index > 0 : + rtn = self.viewmanager.myQtab.tabText(index) + return rtn diff --git a/src/daSalome/daGUI/daEficasWrapper/eficasWrapper.py.in b/src/daSalome/daGUI/daEficasWrapper/eficasWrapper.py.in new file mode 100644 index 0000000..4b646df --- /dev/null +++ b/src/daSalome/daGUI/daEficasWrapper/eficasWrapper.py.in @@ -0,0 +1,278 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2010 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 +# + +__author__="aribes/gboulant" + +# HACK - Pour l'instant +import logging +logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(message)s') +logger = logging.getLogger() + +class DevelException(Exception): + def __init__(self, message): + """Canonical constructor""" + Exception.__init__(self,message) + +# +# ============================================================================== +# Interface of an eficas observer (for implementing the subject/observer pattern) +# ============================================================================== +# +from daGuiImpl.enumerate import Enumerate +class EficasObserver: + """ + This class specifies the interface of an eficas observer. See example at the + bottom of this file. + """ + def processEficasEvent(self, eficasWrapper, eficasEvent): + """ + This function should be implemented in the concrete Observer. + @param eficasWrapper the instance of the source EficasWrapper + @param eficasEvent the emitted event (instance of EficasEvent) + """ + raise DevelException("processEficasEvent not implemented yet") + +class EficasEvent: + EVENT_TYPES=Enumerate([ + 'CLOSE', + 'SAVE' + ]) + + def __init__(self,eventType,callbackId=None): + """ + Creates an eficas event to be used by an EficasWrapper to notify an + EficasObserver. + + The eventType depends of the context of creation. It specify the nature + of the event inside EficasWrapper that triggers the creation of this instance. + + The callbackId is an object used by the EficasObserver to map the + notification (this received event) with the initial event (callback) + This object can be anything and has to be defined through a convention + that both the EficasWrapper and the EficasObserver can understand. + Typically, the eficas observer set the callback id to the eficas wrapper + before running the asynchronous show. Then, when an event is raised by + the eficas wrapper toward its observer, it embeds the callback id so that + the observer can match the received event to the initial trigger context. + + @param the eventType to be choosen in the EVENT_TYPES + @param callbackId an arbitrary data object + """ + if not self.EVENT_TYPES.isValid(eventType): + raise DevelException("The event type "+str(eventType)+" is not defined") + + self.eventType = eventType + self.callbackId = callbackId + +# +# ============================================================================== +# Definition of an Eficas wrapper for integration to the SALOME framework +# This wrapper is not specific to the OMA context and is intended not to be. +# ============================================================================== +# +eficasPath = '@EFICAS_DIR@' +import os +if os.environ.has_key( "EFICAS_ROOT"): + eficasPath = os.environ["EFICAS_ROOT"] +import sys +sys.path[:0]=[eficasPath, + os.path.join( eficasPath,'Editeur'), + os.path.join( eficasPath,'UiQT4'), + ] + +import Editeur +from InterfaceQT4 import qtEficas + +class EficasWrapper(qtEficas.Appli): + + def __init__(self, parent, code , fichier=None, module="Eficas", version=None, salome=0): + + self.__codetype = code + self.__jdc = None + + # variables for the notification procedure + self.__observer = None + self.__callbackId = None + + # The convention is that the python pref associated to the code are in + # a folder whose name is the code name with lower letters but the + # first that is in capital letter. + pathCode=code[0]+code[1:].lower() + sys.path[:0]=[os.path.join(eficasPath,pathCode)] + + if Editeur.__dict__.has_key( 'session' ): + from Editeur import session + eficasArg = [] + eficasArg += sys.argv + if fichier: + eficasArg += [ fichier ] + if version: + eficasArg += [ "-c", version ] + else : + print "noversion" + session.parse( eficasArg ) + + qtEficas.Appli.__init__( self,code=code,salome=salome,parent=parent) + + def setCodeType(self,code): + self.__codetype = code + + def setJdc(self, value, name=None): + self.__jdc = value + if self.__jdc is None: + editor = self.viewmanager.getEditor() + editor.fichier = os.path.abspath(unicode("/tmp/eficasfile.comm")) + else: + fichier = os.path.abspath(unicode("/tmp/eficasfile.comm")) + f = open(fichier,'w') + f.write(self.__jdc) + f.close() + editor=self.viewmanager.getEditor( fichier,units=None) + + # _TODO_ set the name and indicate that it's not used to ask a filename + #index=self.viewmanager.myQtab.currentIndex() + #self.viewmanager.myQtab.setTabText(index,fileName) + + def getJdc(self): + # First update the jdc attribute and return the value + self.__jdc = self.__getJdcText() + return self.__jdc + + # ========================================================================== + # Function for the notification interface between an EficasWrapper an an + # EficasObserver. + # + def addObserver(self, observer): + """ + In fact, only one observer may be defined for the moment. + """ + try: + observer.processEficasEvent + except: + raise DevelException("the argument should implement the function processEficasEvent") + self.__observer = observer + + def notifyObserver(self, eventType): + eficasEvent = EficasEvent(eventType, self.getCallbackId()) + self.__observer.processEficasEvent(self, eficasEvent) + + def setCallbackId(self, callbackId): + """ + This function should be used to define the callback identifier that + will be used by the observer to map the notifcation event with the source + event. + @param callbackId Any object to be used in the notification interface + """ + self.__callbackId = callbackId + + def getCallbackId(self): + return self.__callbackId + + # ========================================================================== + # Functions to handle the Eficas callback functions + # + def closeEvent(self,event): + """ + This method is a notification function called at the end of the eficas + processes quit/exit. + This close event has to be overload to handle the management of data + saving process, and to prevent the application from bug when reopen + eficas after a first close operation inside SALOME. + @overload + """ + logger.debug("#### Handle closeEvent") + + # Get the data and notify the observers of the event + # _TODO_ get data + self.notifyObserver(EficasEvent.EVENT_TYPES.CLOSE) + self.fileCloseAll() + + # This is to prevent from bug when reopen eficas after a first close + # operation inside SALOME. + import InterfaceQT4.readercata + if hasattr(InterfaceQT4.readercata,'reader') : + del InterfaceQT4.readercata.reader + event.accept() + + + def fileSave(self): + """ + @overload + """ + logger.debug("This is my fileSave") + # Notify the observers of the event + self.notifyObserver(EficasEvent.EVENT_TYPES.SAVE) + + qtEficas.Appli.fileSave(self) + + + def fileSaveAs(self): + """ + @overload + """ + logger.debug("This is my fileSaveAs") + self.fileSave() + + def fileClose(self): + """ + @overload + """ + logger.debug("This is my fileClose") + qtEficas.Appli.fileClose(self) + + def fileCloseAll(self): + """ + @overload + """ + logger.debug("This is my fileCloseAll") + qtEficas.Appli.fileCloseAll(self) + + # ========================================================================== + # helper functions + # + def __getJdcText(self): + """ + This function returns the current jdc in text format. + """ + index=self.viewmanager.myQtab.currentIndex() + if index < 0 : return + + editor=self.viewmanager.dict_editors[index] + format = self.format_fichier + strSource = str( editor.get_text_JDC(format) ) + return strSource + + def displayNew(self): + self.fileNew() + self.show() + + # ========================================================================== + # Test functions ... + # + def __displayData(self, textjdc): + index=self.viewmanager.myQtab.currentIndex() + if index < 0 : return + + editor=self.viewmanager.dict_editors[index] + + jdc=editor.readercata.cata[0].JdC(procedure=textjdc,appli=editor,cata=editor.readercata.cata,cata_ord_dico=editor.readercata.cata_ordonne_dico,nom="JDC name",rep_mat=editor.CONFIGURATION.rep_mat) + self.viewmanager.getEditor("toto.com",jdc) + + diff --git a/src/daSalome/daGUI/daGuiImpl/Makefile.am b/src/daSalome/daGUI/daGuiImpl/Makefile.am index b0781de..2c142b4 100644 --- a/src/daSalome/daGUI/daGuiImpl/Makefile.am +++ b/src/daSalome/daGUI/daGuiImpl/Makefile.am @@ -25,6 +25,10 @@ mypkgpython_PYTHON = \ __init__.py \ DATASSIMGUI_impl.py \ datassimGuiManager.py \ + datassimGuiHelper.py \ + datassimModuleHelper.py \ + datassimStudyEditor.py \ + studyedit.py \ enumerate.py diff --git a/src/daSalome/daGUI/daGuiImpl/datassimGuiHelper.py b/src/daSalome/daGUI/daGuiImpl/datassimGuiHelper.py new file mode 100644 index 0000000..be7bee9 --- /dev/null +++ b/src/daSalome/daGUI/daGuiImpl/datassimGuiHelper.py @@ -0,0 +1,121 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2010 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 +# + +__author__="aribes/gboulant" + +import salome +# Get SALOME PyQt interface +import SalomePyQt +__sgPyQt = SalomePyQt.SalomePyQt() + +import datassimModuleHelper +from PyQt4 import QtGui,QtCore + +def waitCursor(): + QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor)) + +def restoreCursor(): + QtGui.QApplication.restoreOverrideCursor() + +def gui_warning(parent, msg="An error occurs" ): + """ + This function displays a message dialog box displaying the specified message. + """ + QtGui.QMessageBox.warning( parent, "Alerte", msg) + +def getActiveStudyId(): + """ + This function returns the id of the active study. The concept of active study + makes sens only in the GUI context. + """ + return __sgPyQt.getStudyId() + + +def getActiveStudy(): + """ + This function returns the active study reference. The concept of active study + makes sens only in the GUI context. + """ + studyId = getActiveStudyId()() + study = datassimModuleHelper.getStudyManager().GetStudyByID( studyId ) + return study + +def refreshObjectBrowser(): + """ + Refresh the graphical representation of the SALOME study, in case where the + GUI is working. + """ + if salome.sg is not None: + salome.sg.updateObjBrowser(0) + +def getSelectedItem(salomeStudyId=getActiveStudyId()): + """ + Get the current selection. If more than one item are selected, the + only first is considered. The object is return (not the id). + The item can be of any type, no control is done in this function. + """ + if salome.sg is None: + raise Exception("GuiHelper.getSelectedItem can't be used without the GUI context") + + salomeStudy = datassimModuleHelper.getStudyManager().GetStudyByID( salomeStudyId ) + + item = None + listEntries=salome.sg.getAllSelected() + if len(listEntries) >= 1: + entry = listEntries[0] + item = salomeStudy.FindObjectID( entry ) + + return item + +def getAllSelected(salomeStudyId): + """ + Returns all selected items in the specified study. + """ + if salome.sg is None: + raise OmaException("getSelectedItem can't be used without the GUI context", OmaException.TYPES.DEVEL) + + study = datassimModuleHelper.getStudyManager().GetStudyByID( salomeStudyId ) + selcount = salome.sg.SelectedCount() + seltypes = {} + for i in range( selcount ): + __incObjToMap( seltypes, datassimModuleHelper.getObjectID( study, salome.sg.getSelected( i ) ) ) + pass + return selcount, seltypes + +def __incObjToMap( m, id ): + """ + Increment object counter in the specified map. + Not to be used outside this module. + """ + if id not in m: m[id] = 0 + m[id] += 1 + pass + +def getDesktop(): + """ + Returns the active Desktop. Usefull to set the relative position of a dialog box + """ + return __sgPyQt.getDesktop() + +def warning(msg): + """ + This function displays a message dialog box displaying the specified message. + """ + gui_warning(getDesktop(),msg) diff --git a/src/daSalome/daGUI/daGuiImpl/datassimGuiManager.py b/src/daSalome/daGUI/daGuiImpl/datassimGuiManager.py index 811ef9d..9d53d56 100644 --- a/src/daSalome/daGUI/daGuiImpl/datassimGuiManager.py +++ b/src/daSalome/daGUI/daGuiImpl/datassimGuiManager.py @@ -31,6 +31,12 @@ import SalomePyQt sgPyQt = SalomePyQt.SalomePyQt() from daGuiImpl.enumerate import Enumerate +from daEficasWrapper.datassimEficasWrapper import DatassimEficasWrapper +from daEficasWrapper.eficasWrapper import EficasObserver +from daEficasWrapper.eficasWrapper import EficasEvent +import datassimGuiHelper +import datassimStudyEditor + # # ============================================================================== # Classes to manage the building of UI components @@ -67,12 +73,11 @@ class DatassimGuiUiComponentBuilder: sgPyQt.createMenu(a, mid) sgPyQt.createTool(a, tid) -class DatassimGuiActionImpl(): +class DatassimGuiActionImpl(EficasObserver): """ This class implements the ui actions concerning the management of oma study cases. """ - __dlgNewStudyCase = None __dlgEficasWrapper = None def __init__(self): @@ -80,7 +85,8 @@ class DatassimGuiActionImpl(): # This dialog is created once so that it can be recycled for each call # to newOmaCase(). #self.__dlgNewStudyCase = DlgNewStudyCase() - #self.__dlgEficasWrapper = OmaEficasWrapper(parent=SalomePyQt.SalomePyQt().getDesktop()) + self.__dlgEficasWrapper = DatassimEficasWrapper(parent=SalomePyQt.SalomePyQt().getDesktop()) + self.__dlgEficasWrapper.addObserver(self) # ========================================================================== # Processing of ui actions @@ -101,3 +107,47 @@ class DatassimGuiActionImpl(): def newDatassimCase(self): print "newDatassimCase" + salomeStudyId = datassimGuiHelper.getActiveStudyId() + salomeStudyItem = datassimStudyEditor.addInStudy(salomeStudyId, "newDatassimCase") + datassimGuiHelper.refreshObjectBrowser() + callbackId = [salomeStudyId, salomeStudyItem] + self.__dlgEficasWrapper.setCallbackId(callbackId) + self.__dlgEficasWrapper.displayNew() + + # ========================================================================== + # Processing notifications from eficas + # + __processOptions={ + EficasEvent.EVENT_TYPES.CLOSE : "_processEficasCloseEvent", + EficasEvent.EVENT_TYPES.SAVE : "_processEficasSaveEvent" + } + def processEficasEvent(self, eficasWrapper, eficasEvent): + """ + Implementation of the interface EficasObserver. The implementation is a + switch on the possible types of events defined in EficasEvent.EVENT_TYPES. + @overload + """ + functionName = self.__processOptions.get(eficasEvent.eventType, lambda : "_processEficasUnknownEvent") + return getattr(self,functionName)(eficasWrapper, eficasEvent) + + def _processEficasCloseEvent(self, eficasWrapper, eficasEvent): + print "This is the process of EficasCloseEvent" + print "Remove datassim case in study if empty..." + pass + + def _processEficasSaveEvent(self, eficasWrapper, eficasEvent): + callbackId = eficasEvent.callbackId + if callbackId is None: + raise DevelException("the callback data should not be None. Can't guess what are the study and case") + [targetSalomeStudyId,targetSalomeStudyItem] = callbackId + if ( targetSalomeStudyId is None ) or ( targetSalomeStudyItem is None ): + raise DevelException("the parameters targetSalomeStudyId and targetSalomeStudyItem should not be None") + + file_name = eficasWrapper.getCurrentFileName() + datassimStudyEditor.updateItem(targetSalomeStudyId, targetSalomeStudyItem, file_name) + #studyCase = omaStudyEditor.getOmaCaseFromItem(targetSalomeStudyId, targetSalomeStudyItem) + #studyCase = eficasWrapper.getData(studyCase) + #logger.debug("jdc="+str(studyCase.userdata.getJdc())) + + def _processEficasUnknownEvent(self, eficasWrapper, eficasEvent): + print "Unknown Eficas Event" diff --git a/src/daSalome/daGUI/daGuiImpl/datassimModuleHelper.py b/src/daSalome/daGUI/daGuiImpl/datassimModuleHelper.py new file mode 100644 index 0000000..c30957f --- /dev/null +++ b/src/daSalome/daGUI/daGuiImpl/datassimModuleHelper.py @@ -0,0 +1,211 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# +# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +# +# 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 +# +# --- +# Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +# --- +# + +__all__ = [ + "moduleID", + "objectID", + "unknownID", + "componentName", + "modulePixmap", + "verbose", + "getORB", + "getNS", + "getLCC", + "getStudyManager", + "getEngine", + "getEngineIOR", + "findOrCreateComponent", + "getObjectID", + ] + +from omniORB import CORBA +from SALOME_NamingServicePy import SALOME_NamingServicePy_i +from LifeCycleCORBA import LifeCycleCORBA +import SALOMEDS +import SALOMEDS_Attributes_idl + +#import OMA_ORB + +### +# Get OMA module's ID +### +def moduleID(): + MODULE_ID = 1100 + return MODULE_ID + +### +# Get OMA object's ID +### +def objectID(): + OBJECT_ID = 1110 + return OBJECT_ID + +### +# Get unknown ID +### +def unknownID(): + FOREIGN_ID = -1 + return FOREIGN_ID + +def componentName(): + """ + This provide the name of the module component to be associated to the study. + """ + # Note that this name should be (i) the name used for the class implementing + # the component CORBA interface and (ii) the name used to declare the component + # in the catalog of the module. + return "DATASSIM" + +# _MEM_ we use here the tr() translation methode to manage constant parameters +# in the application. We could have specified instead constant values directly +# in the code below. It's a matter of convenience. +from PyQt4.QtCore import QObject +QObjectTR=QObject() + +def componentUserName(): + return "DATASSIM" + +def modulePixmap(): + """ + Get the reference pixmap for this module. + """ + return "DATASSIM_small.png" + +__verbose__ = None +def verbose(): + global __verbose__ + if __verbose__ is None: + try: + __verbose__ = int( os.getenv( 'SALOME_VERBOSE', 0 ) ) + except: + __verbose__ = 0 + pass + pass + return __verbose__ + +### +# Get ORB reference +### +__orb__ = None +def getORB(): + global __orb__ + if __orb__ is None: + __orb__ = CORBA.ORB_init( [''], CORBA.ORB_ID ) + pass + return __orb__ + +### +# Get naming service instance +### +__naming_service__ = None +def getNS(): + global __naming_service__ + if __naming_service__ is None: + __naming_service__ = SALOME_NamingServicePy_i( getORB() ) + pass + return __naming_service__ + +## +# Get life cycle CORBA instance +## +__lcc__ = None +def getLCC(): + global __lcc__ + if __lcc__ is None: + __lcc__ = LifeCycleCORBA( getORB() ) + pass + return __lcc__ + +## +# Get study manager +### +__study_manager__ = None +def getStudyManager(): + global __study_manager__ + if __study_manager__ is None: + obj = getNS().Resolve( '/myStudyManager' ) + __study_manager__ = obj._narrow( SALOMEDS.StudyManager ) + pass + return __study_manager__ + +### +# Get OMA engine +### +__engine__ = None +def getEngine(): + global __engine__ + if __engine__ is None: + __engine__ = getLCC().FindOrLoadComponent( "FactoryServer", componentName() ) + pass + return __engine__ + +### +# Get OMA engine IOR +### +def getEngineIOR(): + IOR = "" + if getORB() and getEngine(): + IOR = getORB().object_to_string( getEngine() ) + pass + return IOR + +### +# Find or create OMA component object in a study +### +def findOrCreateComponent( study ): + father = study.FindComponent( componentName() ) + if father is None: + builder = study.NewBuilder() + father = builder.NewComponent( componentName() ) + attr = builder.FindOrCreateAttribute( father, "AttributeName" ) + attr.SetValue( componentName() ) + attr = builder.FindOrCreateAttribute( father, "AttributePixMap" ) + attr.SetPixMap( modulePixmap() ) + attr = builder.FindOrCreateAttribute( father, "AttributeLocalID" ) + attr.SetValue( moduleID() ) + try: + builder.DefineComponentInstance( father, getEngine() ) + pass + except: + pass + pass + return father + +### +# Get object's ID +### +def getObjectID( study, entry ): + ID = unknownID() + if study and entry: + sobj = study.FindObjectID( entry ) + if sobj is not None: + test, anAttr = sobj.FindAttribute( "AttributeLocalID" ) + if test: ID = anAttr._narrow( SALOMEDS.AttributeLocalID ).Value() + pass + pass + return + diff --git a/src/daSalome/daGUI/daGuiImpl/datassimStudyEditor.py b/src/daSalome/daGUI/daGuiImpl/datassimStudyEditor.py new file mode 100644 index 0000000..42eac25 --- /dev/null +++ b/src/daSalome/daGUI/daGuiImpl/datassimStudyEditor.py @@ -0,0 +1,122 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2010 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 +# + +__author__="aribes/gboulant" + +from enumerate import Enumerate +import studyedit +import datassimModuleHelper + +# +# ============================================================================== +# Constant parameters and identifiers +# ============================================================================== +# +DATASSIM_ITEM_TYPES = Enumerate([ + "DATASSIM_CASE", +]) + +# +# ============================================================================== +# Function dedicated to the data management in the salome study +# ============================================================================== +# +# For developpers, note that the data structures used here are: +# - the SALOME study whose API is defined by SALOMEDS::Study +# - an item in a study whose API is defined by SALOMEDS:SObject +# - a study component, whose API is defined by SALOMEDS::SComponent +# a SComponent is a reference in a study toward a SALOME component +# + +def addInStudy(salomeStudyId, datassimCase): + """ + This function adds in the specified SALOME study a study entry that corresponds + to the specified datassim case. This study case is put in a folder under + the DATASSIM component and is identified by the case name. + """ + + studyEditor = studyedit.getStudyEditor(salomeStudyId) + + datassimRootEntry = studyEditor.findOrCreateComponent( + engineName = datassimModuleHelper.componentName(), + componentName = datassimModuleHelper.componentUserName()) + + itemName = datassimCase + itemValue = "" + itemType = DATASSIM_ITEM_TYPES.DATASSIM_CASE + + salomeStudyItem = studyEditor.createItem( + datassimRootEntry, itemName, + comment = itemValue, + typeId = itemType) + # _MEM_ Note that we use the comment attribute to store the serialize + # description of the data. + + return salomeStudyItem + +def updateItem(salomeStudyId, salomeStudyItem, datassimCase): + + studyEditor = studyedit.getStudyEditor(salomeStudyId) + + itemName = datassimCase + itemValue = "" + + studyEditor.setItem(salomeStudyItem, + name = itemName, + comment = itemValue) + +def removeItem(salomeStudyId, item): + """ + Remove the item from the specified study. + """ + if not isValidDatassimCaseItem(salomeStudyId, item): + return False + studyEditor = studyedit.getStudyEditor(salomeStudyId) + return studyEditor.removeItem(item,True) + + +def isValidDatassimCaseItem(salomeStudyId,item): + """ + Return true if the specified item corresponds to a valid datassimCase + """ + if item is None: + return False + + studyEditor = studyedit.getStudyEditor(salomeStudyId) + itemType = studyEditor.getTypeId(item) + if itemType != DATASSIM_ITEM_TYPES.DATASSIM_CASE: + return False + + return True + + +def getDatassimCaseFromItem(salomeStudyId, item): + """ + Get the datassim case from the selected item. + Note that the study must be specify to retrieve the attributes value from + the item reference. The attribute values are stored in the study object. + """ + if not isValidDatassimCaseItem(salomeStudyId, item): + return None + + itemName = item.GetName() + itemValue = item.GetComment() + return itemName + diff --git a/src/daSalome/daGUI/daGuiImpl/studyedit.py b/src/daSalome/daGUI/daGuiImpl/studyedit.py new file mode 100644 index 0000000..f513b14 --- /dev/null +++ b/src/daSalome/daGUI/daGuiImpl/studyedit.py @@ -0,0 +1,443 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2007-2009 EDF R&D +# +# This file is part of PAL_SRC. +# +# PAL_SRC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PAL_SRC 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with PAL_SRC; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +""" +This module provides a new class :class:`StudyEditor` to complement +:class:`Study` and :class:`StudyBuilder` classes. +""" + +import re +import salome + +_editors = {} +_DEFAULT_CONTAINER = "FactoryServer" + +def getActiveStudyId(): + """ + Return the ID of the active study. In GUI mode, this function is equivalent + to ``salome.sg.getActiveStudyId()``. Outside GUI, it returns + ``salome.myStudyId`` variable. + """ + salome.salome_init() + if salome.hasDesktop(): + return salome.sg.getActiveStudyId() + else: + return salome.myStudyId + +def getStudyEditor(studyId = None): + """ + Return a :class:`StudyEditor` instance to edit the study with ID + `studyId`. If `studyId` is :const:`None`, return an editor for the current + study. + """ + if studyId is None: + studyId = getActiveStudyId() + if not _editors.has_key(studyId): + _editors[studyId] = StudyEditor(studyId) + return _editors[studyId] + + +class StudyEditor: + """ + This class provides utility methods to complement :class:`Study` and + :class:`StudyBuilder` classes. Those methods may be moved in those classes + in the future. The parameter `studyId` defines the ID of the study to + edit. If it is :const:`None`, the edited study will be the current study. + The preferred way to get a StudyEditor object is through the method + :meth:`getStudyEditor` which allows to reuse existing instances. + + .. attribute:: studyId + + This instance attribute contains the ID of the edited study. This + attribute should not be modified. + + .. attribute:: study + + This instance attribute contains the underlying :class:`Study` object. + It can be used to access the study but the attribute itself should not + be modified. + + .. attribute:: builder + + This instance attribute contains the underlying :class:`StudyBuilder` + object. It can be used to edit the study but the attribute itself + should not be modified. + + """ + def __init__(self, studyId = None): + salome.salome_init() + if studyId is None: + studyId = getActiveStudyId() + self.studyId = studyId + self.study = salome.myStudyManager.GetStudyByID(studyId) + if self.study is None: + raise Exception("Can't create StudyEditor object: " + "Study %d doesn't exist" % studyId) + self.builder = self.study.NewBuilder() + + def findOrCreateComponent(self, engineName, componentName = None, + icon = None, loadEngine = True, + containerName = _DEFAULT_CONTAINER): + """ + Find a component corresponding to the engine named `engineName` in the + study, or create it if none is found. Then eventually load the + corresponding engine and the CORBA objects of this component. + + :type engineName: string + :param engineName: name of the engine corresponding to the component. + + :type componentName: string + :param componentName: name of the new component if created. If + :const:`None`, use `engineName` instead. + + :type icon: string + :param icon: icon for the new component (attribute "AttributePixMap"). + + :type loadEngine: boolean + :param loadEngine: If :const:`True`, find or load the corresponding + engine and associate it with the component. + + :type containerName: string + :param containerName: name of the container in which the component + should be loaded. + + :return: the SComponent found or created. + + """ + sComponent = self.study.FindComponent(engineName) + if sComponent is None: + sComponent = self.builder.NewComponent(engineName) + if componentName is None: + componentName = engineName + self.builder.SetName(sComponent, componentName) + + if icon is not None: + self.setIcon(sComponent, icon) + + if loadEngine: + self.loadComponentEngine(sComponent, containerName) + + return sComponent + + def loadComponentEngine(self, sComponent, + containerName = _DEFAULT_CONTAINER): + """ + Load the engine corresponding to `sComponent` in the container + `containerName`, associate the engine with the component and load the + CORBA objects of this component in the study. + """ + engine = salome.lcc.FindOrLoadComponent(containerName, + sComponent.GetComment()) + self.builder.LoadWith(sComponent, engine) + + def getOrLoadObject(self, item): + """ + Get the CORBA object associated with the SObject `item`, eventually by + first loading it with the corresponding engine. + """ + object = item.GetObject() + if object is None: # the engine has not been loaded yet + sComponent = item.GetFatherComponent() + self.loadComponentEngine(sComponent) + object = item.GetObject() + return object + + def findOrCreateItem(self, fatherItem, name, fileType = None, + fileName = None, comment = None, icon = None, + IOR = None, typeId = None): + """ + Find an object under `fatherItem` in the study with the given + attributes. Return the first one found if at least one exists, + otherwise create a new one with the given attributes and return it. + + See :meth:`setItem` for the description of the parameters. + """ + sObject = self.findItem(fatherItem, name, fileType, fileName, comment, + icon, IOR, typeId) + if sObject is None: + sObject = self.createItem(fatherItem, name, fileType, fileName, + comment, icon, IOR, typeId) + return sObject + + def findItem(self, fatherItem, name = None, fileType = None, + fileName = None, comment = None, icon = None, IOR = None, + typeId = None): + """ + Find an item with given attributes under `fatherItem` in the study. If + none is found, return :const:`None`. If several items correspond to + the parameters, only the first one is returned. The search is made + only on given parameters (i.e. not :const:`None`). To look explicitly + for an empty attribute, use an empty string in the corresponding + parameter. + + See :meth:`setItem` for the description of the parameters. + """ + foundItem = None; + childIterator = self.study.NewChildIterator(fatherItem) + while childIterator.More() and foundItem is None: + childItem = childIterator.Value() + if childItem and \ + (name is None or childItem.GetName() == name) and \ + (fileType is None or \ + self.getFileType(childItem) == fileType) and \ + (fileName is None or \ + self.getFileName(childItem) == fileName) and \ + (comment is None or childItem.GetComment() == comment) and \ + (icon is None or \ + self.getIcon(childItem) == icon) and \ + (IOR is None or childItem.GetIOR() == IOR and \ + (typeId is None or \ + self.getTypeId(childItem) == typeId)): + foundItem = childItem + childIterator.Next() + return foundItem + + def createItem(self, fatherItem, name, fileType = None, fileName = None, + comment = None, icon = None, IOR = None, typeId = None): + """ + Create a new object named `name` under `fatherItem` in the study, with + the given attributes. If an object named `name` already exists under + the father object, the new object is created with a new name `name_X` + where X is the first available index. + + :type fatherItem: SObject + :param fatherItem: item under which the new item will be added. + + :return: new SObject created in the study + + See :meth:`setItem` for the description of the other parameters. + """ + aSObject = self.builder.NewObject(fatherItem) + + aChildIterator = self.study.NewChildIterator(fatherItem) + aMaxId = -1 + aLength = len(name) + aDelim = "_" + anIdRE = re.compile("^" + aDelim + "[0-9]+") + aNameRE = re.compile("^" + name) + while aChildIterator.More(): + aSObj = aChildIterator.Value() + aChildIterator.Next() + aName = aSObj.GetName() + if re.match(aNameRE,aName): + aTmp = aName[aLength:] + if re.match(anIdRE,aTmp): + import string + anId = string.atol(aTmp[1:]) + if aMaxId < anId: + aMaxId = anId + pass + pass + elif aMaxId < 0: + aMaxId = 0 + pass + pass + pass + + aMaxId = aMaxId + 1 + aName = name + if aMaxId > 0: + aName = aName + aDelim + str(aMaxId) + pass + + self.setItem(aSObject, aName, fileType, fileName, comment, icon, + IOR, typeId) + + return aSObject + + def setItem(self, item, name = None, fileType = None, fileName = None, + comment = None, icon = None, IOR = None, typeId = None): + """ + Modify the attributes of an item in the study. Unspecified attributes + (i.e. those set to :const:`None`) are left unchanged. + + :type item: SObject + :param item: item to modify. + + :type name: string + :param name: item name (attribute 'AttributeName'). + + :type fileType: string + :param fileType: item file type (attribute 'AttributeFileType'). + + :type fileName: string + :param fileName: item file name (attribute + 'AttributeExternalFileDef'). + + :type comment: string + :param comment: item comment (attribute 'AttributeComment'). Note that + this attribute will appear in the 'Value' column in + the object browser. + + :type icon: string + :param icon: item icon name (attribute 'AttributePixMap'). + + :type IOR: string + :param IOR: IOR of a CORBA object associated with the item + (attribute 'AttributeIOR'). + + :type typeId: integer + :param typeId: item type (attribute 'AttributeLocalID'). + """ + print "setItem (ID=%s): name=%s, fileType=%s, fileName=%s, comment=%s, icon=%s, IOR=%s" % (item.GetID(), name, fileType, fileName, comment, icon, IOR) + # Explicit cast is necessary for unicode to string conversion + if name is not None: + self.builder.SetName(item, str(name)) + if fileType is not None: + self.setFileType(item, fileType) + if fileName is not None: + self.setFileName(item, fileName) + if comment is not None: + self.builder.SetComment(item, str(comment)) + if icon is not None: + self.setIcon(item, icon) + if IOR is not None: + self.builder.SetIOR(item, str(IOR)) + if typeId is not None: + self.setTypeId(item, typeId) + + def removeItem(self, item, withChildren = False ): + """ + Remove the given item from the study (the item still is in + the study after the removal) + @param item: the browser object to be removed + @param withChildren: remove children if True + """ + ok = False + try: + if withChildren: + self.builder.RemoveObjectWithChildren(item) + else: + self.builder.RemoveObject(item) + ok = True + except: + ok = False + + return ok + + def setItemAtTag(self, fatherItem, tag, name = None, fileType = None, + fileName = None, comment = None, icon = None, IOR = None, + typeId = None): + """ + Find an item tagged `tag` under `fatherItem` in the study tree or + create it if there is none, then set its attributes. + + :type fatherItem: SObject + :param fatherItem: item under which the tagged item will be looked for + and eventually created. + + :type tag: integer + :param tag: tag of the item to look for. + + :return: the SObject at `tag` if found or created successfully, or + :const:`None` if an error happened. + + See :meth:`setItem` for the description of the other parameters. + """ + found, sObj = fatherItem.FindSubObject(tag) + if not found: + sObj = self.builder.NewObjectToTag(fatherItem, tag) + self.setItem(sObj, name, fileType, fileName, comment, icon, + IOR, typeId) + return sObj + + def getAttributeValue(self, sObject, attributeName, default = None): + """ + Return the value of the attribute named `attributeName` on the object + `sObject`, or `default` if the attribute doesn't exist. + """ + value = default + found, attr = self.builder.FindAttribute(sObject, attributeName) + if found: + value = attr.Value() + return value + + def setAttributeValue(self, sObject, attributeName, attributeValue): + """ + Set the value of the attribute named `attributeName` on the object + `sObject` to the value `attributeValue`. + """ + attr = self.builder.FindOrCreateAttribute(sObject, attributeName) + attr.SetValue(attributeValue) + + def getTypeId(self, sObject): + """ + Return the value of the attribute "AttributeLocalID" of the object + `sObject`, or :const:`None` if it is not set. + """ + return self.getAttributeValue(sObject, "AttributeLocalID") + + def setTypeId(self, sObject, value): + """ + Set the attribute "AttributeLocalID" of the object `sObject` to the + value `value`. + """ + self.setAttributeValue(sObject, "AttributeLocalID", value) + + def getFileType(self, sObject): + """ + Return the value of the attribute "AttributeFileType" of the object + `sObject`, or an empty string if it is not set. + """ + return self.getAttributeValue(sObject, "AttributeFileType", "") + + def setFileType(self, sObject, value): + """ + Set the attribute "AttributeFileType" of the object `sObject` to the + value `value`. + """ + # Explicit cast is necessary for unicode to string conversion + self.setAttributeValue(sObject, "AttributeFileType", str(value)) + + def getFileName(self, sObject): + """ + Return the value of the attribute "AttributeExternalFileDef" of the + object `sObject`, or an empty string if it is not set. + """ + return self.getAttributeValue(sObject, "AttributeExternalFileDef", "") + + def setFileName(self, sObject, value): + """ + Set the attribute "AttributeExternalFileDef" of the object `sObject` + to the value `value`. + """ + # Explicit cast is necessary for unicode to string conversion + self.setAttributeValue(sObject, "AttributeExternalFileDef", + str(value)) + + def getIcon(self, sObject): + """ + Return the value of the attribute "AttributePixMap" of the object + `sObject`, or an empty string if it is not set. + """ + value = "" + found, attr = self.builder.FindAttribute(sObject, "AttributePixMap") + if found and attr.HasPixMap(): + value = attr.GetPixMap() + return value + + def setIcon(self, sObject, value): + """ + Set the attribute "AttributePixMap" of the object `sObject` to the + value `value`. + """ + attr = self.builder.FindOrCreateAttribute(sObject, "AttributePixMap") + # Explicit cast is necessary for unicode to string conversion + attr.SetPixMap(str(value)) -- 2.39.2