Back to index

salome-gui  6.5.0
salome_pluginsmanager.py
Go to the documentation of this file.
00001 # Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
00002 #
00003 # This library is free software; you can redistribute it and/or
00004 # modify it under the terms of the GNU Lesser General Public
00005 # License as published by the Free Software Foundation; either
00006 # version 2.1 of the License.
00007 #
00008 # This library is distributed in the hope that it will be useful,
00009 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011 # Lesser General Public License for more details.
00012 #
00013 # You should have received a copy of the GNU Lesser General Public
00014 # License along with this library; if not, write to the Free Software
00015 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00016 #
00017 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
00018 #
00019 
00020 """
00021 This module is imported from C++ SalomeApp_Application and initialized
00022 (call to initialize function with 4 parameters) module : 0 if it's
00023 plugins manager at the application level 1 if it is at the module
00024 level name : the name of the plugins manager. This name is used to
00025 build the name of the plugins files basemenuname : the name of the
00026 menu into we want to add the menu of the plugins ("Tools" for example)
00027 menuname : the name of plugins menu
00028 
00029 A plugins manager is created when calling initialize.
00030 
00031 The plugins manager creates a submenu <menuname> in the <basemenuname>
00032 menu.
00033 
00034 The plugins manager searches in $HOME/.config/salome/Plugins,
00035 $HOME/$APPLI/Plugins, $SALOME_PLUGINS_PATH directories files named
00036 <name>_plugins.py and executes them.
00037 
00038 These files should contain python code that register functions into
00039 the plugins manager.
00040 
00041 Example of a plugins manager with name salome. It searches files with
00042 name salome_plugins.py (example follows)::
00043 
00044   import salome_pluginsmanager
00045 
00046   def about(context):
00047     from PyQt4.QtGui import QMessageBox
00048     QMessageBox.about(None, "About SALOME pluginmanager", "SALOME plugins manager in SALOME virtual application ")
00049 
00050   salome_pluginsmanager.AddFunction('About plugins','About SALOME pluginmanager',about)
00051 
00052 All entries in menu are added in the same order as the calls to
00053 AddFunction.  It is possible to customize this presentation by getting
00054 the entries list (salome_pluginsmanager.entries()) and modifying it in
00055 place. For example, you can do that :
00056 salome_pluginsmanager.entries().sort() to order them alphabetically or
00057 salome_pluginsmanager.entries().remove("a") to remove the entry named
00058 "a".
00059 
00060 It is possible to put entries in submenus. You only need to give a
00061 name with / to the entry. for example::
00062 
00063   salome_pluginsmanager.AddFunction('a/b/About','About SALOME pluginmanager',about)
00064 
00065 will add 2 submenus a and b before creating the entry.
00066 
00067 In short to add a plugin:
00068 
00069   1. import the python module salome_pluginsmanager (in your
00070   salome_plugins.py or <module>_plugins.py)
00071 
00072   2. write a function with one argument context (it's an object with 3
00073   attributes)
00074 
00075   3. register the function with a call to AddFunction (entry in menu plugins,
00076   tooltip, function)
00077 
00078 context attributes:
00079 
00080   - sg : the SALOME Swig interface
00081   - studyId : the SALOME studyId that must be used to execute the plugin
00082   - study : the SALOME study object that must be used to execute the plugin
00083 
00084 """
00085 
00086 import os,sys,traceback
00087 from PyQt4 import QtGui
00088 from PyQt4 import QtCore
00089 
00090 import salome
00091 
00092 SEP=":"
00093 if sys.platform == "win32":
00094   SEP = ";"
00095 
00096 # Get SALOME PyQt interface
00097 import SalomePyQt
00098 sgPyQt = SalomePyQt.SalomePyQt()
00099 
00100 # Get SALOME Swig interface
00101 import libSALOME_Swig
00102 sg = libSALOME_Swig.SALOMEGUI_Swig()
00103 
00104 plugins={}
00105 current_plugins_manager=None
00106 
00107 def initialize(module,name,basemenuname,menuname):
00108   if not plugins.has_key(name):
00109     if module:
00110       plugins[name]={}
00111     else:
00112       plugins[name]=[]
00113   if module:
00114     studyId=sg.getActiveStudyId()
00115     if plugins[name].has_key(studyId):return
00116     plugins[name][studyId]=PluginsManager(module,name,basemenuname,menuname)
00117   else:
00118     plugins[name].append(PluginsManager(module,name,basemenuname,menuname))
00119 
00120 class Context:
00121     def __init__(self,sgpyqt):
00122         self.sg=sgpyqt
00123         self.studyId=salome.sg.getActiveStudyId()
00124         self.study= salome.myStudyManager.GetStudyByID(self.studyId)
00125 
00126 def find_menu(smenu):
00127   lmenus=smenu.split("|")
00128   main=lmenus.pop(0).strip()
00129   menu=sgPyQt.getPopupMenu(main)
00130   return findMenu(lmenus,menu)
00131 
00132 def findMenu(lmenu,menu):
00133   if not lmenu:return menu
00134   m=lmenu.pop(0).strip()
00135   for a in menu.actions():
00136     if a.menu():
00137       if a.text() == m:
00138         return findMenu(lmenu,a.menu())
00139 
00140 PLUGIN_PATH_PATTERN="share/salome/plugins"
00141 MATCH_ENDING_PATTERN="_plugins.py"
00142 from salome.kernel.syshelper import walktree
00143 from salome.kernel.logger import Logger
00144 #from salome.kernel.termcolor import GREEN
00145 logger=Logger("PluginsManager") #,color=GREEN)
00146 # VSR 21/11/2011 : do not show infos in the debug mode
00147 #logger.showDebug()
00148 
00149 class PluginsManager:
00150     def __init__(self,module,name,basemenuname,menuname):
00151         self.name=name
00152         self.basemenuname=basemenuname
00153         self.menuname=menuname
00154         self.module=module
00155         self.registry={}
00156         self.handlers={}
00157         self.entries=[]
00158         self.lasttime=0
00159         self.plugindirs=[]
00160         self.plugins_files=[]
00161 
00162         # MODULES plugins directory.
00163         # The SALOME modules may provides natively some plugins. These
00164         # MODULES plugins are supposed to be located in the
00165         # installation folder of the module, in the subdirectory
00166         # "share/salome/plugins". We first look for these directories.
00167         for key in os.environ.keys():
00168           if key.endswith("_ROOT_DIR"):
00169             rootpath=os.environ[key]
00170             dirpath=os.path.join(rootpath,PLUGIN_PATH_PATTERN)
00171             if os.path.isdir(dirpath) and dirpath not in self.plugindirs:
00172               logger.debug("Looking for plugins in the directory %s ..."%dirpath)
00173               walktree(dirpath,self.analyseFile)
00174 
00175         # USER plugins directory
00176         user_dir = os.path.expanduser("~/.config/salome/Plugins")
00177         self.plugindirs.append(user_dir)
00178         logger.info("The user directory %s has been added to plugin paths"%user_dir)
00179         # obsolete: USER plugins directory
00180         # (for compatibility reasons only; new plugins should be stored in ~/.config/salome/Plugins)
00181         user_obsolete_dir = os.path.expanduser("~/.salome/Plugins")
00182         self.plugindirs.append(user_obsolete_dir)
00183         logger.info("The user directory %s has been added to plugin paths (deprecated)"%user_obsolete_dir)
00184 
00185         # APPLI plugins directory
00186         appli=os.getenv("APPLI")
00187         if appli:
00188           appli_dir=os.path.join(os.path.expanduser("~"),appli,"Plugins")
00189           self.plugindirs.append(appli_dir)
00190           logger.info("The APPLI directory %s has been added to plugin paths"%appli_dir)
00191 
00192         #SALOME_PLUGINS_PATH environment variable (list of directories separated by ":")
00193         pluginspath=os.getenv("SALOME_PLUGINS_PATH")
00194         if pluginspath:
00195           for directory in pluginspath.split(SEP):
00196             self.plugindirs.append(directory)
00197             logger.info("The directory %s has been added to plugin paths"%directory)
00198 
00199         self.basemenu = find_menu(self.basemenuname)
00200 
00201         if self.module:
00202           self.menu=QtGui.QMenu(self.menuname)
00203           mid=sgPyQt.createMenu(self.menu.menuAction(),self.basemenuname)
00204         else:
00205           self.menu=QtGui.QMenu(self.menuname,self.basemenu)
00206           self.basemenu.addMenu(self.menu)
00207 
00208         self.menu.menuAction().setVisible(False)
00209 
00210         self.basemenu.connect(self.basemenu, QtCore.SIGNAL("aboutToShow()"), self.importPlugins)
00211 
00212     def analyseFile(self,filename):
00213       """
00214       This function checks if the specified file is a plugins python
00215       module and add the directory name of this file to the list of
00216       plugin paths. This function is aimed to be used as the callback
00217       function of the walktree algorithm.
00218       """
00219       if str(filename).endswith(MATCH_ENDING_PATTERN):
00220         dirpath=os.path.dirname(filename)
00221         if dirpath not in self.plugindirs:
00222           self.plugindirs.append(dirpath)
00223           logger.debug("The directory %s has been added to plugin paths"%dirpath)
00224         
00225     def AddFunction(self,name,description,script):
00226         """ Add a plugin function
00227         """
00228         self.registry[name]=script,description
00229         self.entries.append(name)
00230 
00231         def handler(obj=self,script=script):
00232           try:
00233             script(Context(sgPyQt))
00234           except:
00235             s=traceback.format_exc()
00236             QtGui.QMessageBox.warning(None,"Exception occured",s)
00237 
00238         self.handlers[name]=handler
00239 
00240     def importPlugins(self):
00241         """Execute the salome_plugins file that contains plugins definition """
00242         studyId=sg.getActiveStudyId()
00243         if studyId == 0:
00244           self.menu.clear()
00245           self.menu.menuAction().setVisible(False)
00246           return
00247         elif self.lasttime ==0:
00248           salome.salome_init(embedded=1)
00249 
00250         lasttime=0
00251 
00252         plugins_files=[]
00253         plugins_file_name=self.name+MATCH_ENDING_PATTERN
00254         for directory in self.plugindirs:
00255           plugins_file = os.path.join(directory,plugins_file_name)
00256           if os.path.isfile(plugins_file):
00257             plugins_files.append((directory,plugins_file))
00258             lasttime=max(lasttime,os.path.getmtime(plugins_file))
00259 
00260         plugins_files.sort()
00261 
00262         if not plugins_files:
00263           self.registry.clear()
00264           self.handlers.clear()
00265           self.entries=[]
00266           self.lasttime=0
00267           self.menu.clear()
00268           self.menu.menuAction().setVisible(False)
00269           return
00270 
00271         if self.plugins_files != plugins_files or lasttime > self.lasttime:
00272           global current_plugins_manager
00273           current_plugins_manager=self
00274           self.registry.clear()
00275           self.handlers.clear()
00276           self.entries=[]
00277           self.lasttime=lasttime
00278           for directory,plugins_file in plugins_files:
00279             if directory not in sys.path:
00280               sys.path.insert(0,directory)
00281             try:
00282               execfile(plugins_file,globals(),{})
00283             except:
00284               logger.fatal("Error while loading plugins from file %s"%plugins_file)
00285               traceback.print_exc()
00286 
00287           self.updateMenu()
00288 
00289     def updateMenu(self):
00290         """Update the Plugins menu"""
00291         self.menu.clear()
00292         for entry in self.entries:
00293           names=entry.split("/")
00294           if len(names) < 1:continue
00295           parentMenu=self.menu
00296 
00297           if len(names) > 1:
00298             #create or get submenus
00299             submenus={}
00300             for action in parentMenu.actions():
00301               menu=action.menu()
00302               if menu:
00303                 submenus[str(menu.title())]=menu
00304             while len(names) > 1:
00305               name=names.pop(0)
00306               if submenus.has_key(name):
00307                 amenu=submenus[name]
00308               else:
00309                 amenu=QtGui.QMenu(name,parentMenu)
00310                 parentMenu.addMenu(amenu)
00311                 submenus[name]=amenu
00312               parentMenu=amenu
00313 
00314           name=names.pop(0)
00315           act=parentMenu.addAction(name,self.handlers[entry])
00316           act.setStatusTip(self.registry[entry][1])
00317 
00318         self.menu.menuAction().setVisible(True)
00319 
00320 def AddFunction(name,description,script):
00321    """ Add a plugin function
00322        Called by a user to register a function (script)
00323    """
00324    return current_plugins_manager.AddFunction(name,description,script)
00325 
00326 def entries():
00327   """ Return the list of entries in menu: can be sorted or modified in place to customize menu content """
00328   return current_plugins_manager.entries