1 # Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License.
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # Lesser General Public License for more details.
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 This module is imported from C++ SalomeApp_Application and initialized
22 (call to initialize function with 4 parameters) module : 0 if it's
23 plugins manager at the application level 1 if it is at the module
24 level name : the name of the plugins manager. This name is used to
25 build the name of the plugins files basemenuname : the name of the
26 menu into we want to add the menu of the plugins ("Tools" for example)
27 menuname : the name of plugins menu
29 A plugins manager is created when calling initialize.
31 The plugins manager creates a submenu <menuname> in the <basemenuname>
34 The plugins manager searches in $HOME/.config/salome/Plugins,
35 $HOME/$APPLI/Plugins, $SALOME_PLUGINS_PATH directories files named
36 <name>_plugins.py and executes them.
38 These files should contain python code that register functions into
41 Example of a plugins manager with name salome. It searches files with
42 name salome_plugins.py (example follows)::
44 import salome_pluginsmanager
47 from PyQt4.QtGui import QMessageBox
48 QMessageBox.about(None, "About SALOME pluginmanager", "SALOME plugins manager in SALOME virtual application ")
50 salome_pluginsmanager.AddFunction('About plugins','About SALOME pluginmanager',about)
52 All entries in menu are added in the same order as the calls to
53 AddFunction. It is possible to customize this presentation by getting
54 the entries list (salome_pluginsmanager.entries()) and modifying it in
55 place. For example, you can do that :
56 salome_pluginsmanager.entries().sort() to order them alphabetically or
57 salome_pluginsmanager.entries().remove("a") to remove the entry named
60 It is possible to put entries in submenus. You only need to give a
61 name with / to the entry. for example::
63 salome_pluginsmanager.AddFunction('a/b/About','About SALOME pluginmanager',about)
65 will add 2 submenus a and b before creating the entry.
67 In short to add a plugin:
69 1. import the python module salome_pluginsmanager (in your
70 salome_plugins.py or <module>_plugins.py)
72 2. write a function with one argument context (it's an object with 3
75 3. register the function with a call to AddFunction (entry in menu plugins,
80 - sg : the SALOME Swig interface
81 - studyId : the SALOME studyId that must be used to execute the plugin
82 - study : the SALOME study object that must be used to execute the plugin
86 import os,sys,traceback
87 from PyQt4 import QtGui
88 from PyQt4 import QtCore
93 if sys.platform == "win32":
96 # Get SALOME PyQt interface
98 sgPyQt = SalomePyQt.SalomePyQt()
100 # Get SALOME Swig interface
101 import libSALOME_Swig
102 sg = libSALOME_Swig.SALOMEGUI_Swig()
105 current_plugins_manager=None
107 def initialize(module,name,basemenuname,menuname):
108 if not plugins.has_key(name):
114 studyId=sg.getActiveStudyId()
115 if plugins[name].has_key(studyId):return
116 plugins[name][studyId]=PluginsManager(module,name,basemenuname,menuname)
118 plugins[name].append(PluginsManager(module,name,basemenuname,menuname))
121 def __init__(self,sgpyqt):
123 self.studyId=salome.sg.getActiveStudyId()
124 self.study= salome.myStudyManager.GetStudyByID(self.studyId)
126 def find_menu(smenu):
127 lmenus=smenu.split("|")
128 main=lmenus.pop(0).strip()
129 menu=sgPyQt.getPopupMenu(main)
130 return findMenu(lmenus,menu)
132 def findMenu(lmenu,menu):
133 if not lmenu:return menu
134 m=lmenu.pop(0).strip()
135 for a in menu.actions():
138 return findMenu(lmenu,a.menu())
140 PLUGIN_PATH_PATTERN="share/salome/plugins"
141 MATCH_ENDING_PATTERN="_plugins.py"
142 from salome.kernel.syshelper import walktree
143 from salome.kernel.logger import Logger
144 #from salome.kernel.termcolor import GREEN
145 logger=Logger("PluginsManager") #,color=GREEN)
146 # VSR 21/11/2011 : do not show infos in the debug mode
149 class PluginsManager:
150 def __init__(self,module,name,basemenuname,menuname):
152 self.basemenuname=basemenuname
153 self.menuname=menuname
160 self.plugins_files=[]
162 # MODULES plugins directory.
163 # The SALOME modules may provides natively some plugins. These
164 # MODULES plugins are supposed to be located in the
165 # installation folder of the module, in the subdirectory
166 # "share/salome/plugins". We first look for these directories.
167 for key in os.environ.keys():
168 if key.endswith("_ROOT_DIR"):
169 rootpath=os.environ[key]
170 dirpath=os.path.join(rootpath,PLUGIN_PATH_PATTERN)
171 if os.path.isdir(dirpath) and dirpath not in self.plugindirs:
172 logger.debug("Looking for plugins in the directory %s ..."%dirpath)
173 walktree(dirpath,self.analyseFile)
175 # USER plugins directory
176 user_dir = os.path.expanduser("~/.config/salome/Plugins")
177 self.plugindirs.append(user_dir)
178 logger.info("The user directory %s has been added to plugin paths"%user_dir)
179 # obsolete: USER plugins directory
180 # (for compatibility reasons only; new plugins should be stored in ~/.config/salome/Plugins)
181 user_obsolete_dir = os.path.expanduser("~/.salome/Plugins")
182 self.plugindirs.append(user_obsolete_dir)
183 logger.info("The user directory %s has been added to plugin paths (deprecated)"%user_obsolete_dir)
185 # APPLI plugins directory
186 appli=os.getenv("APPLI")
188 appli_dir=os.path.join(os.path.expanduser("~"),appli,"Plugins")
189 self.plugindirs.append(appli_dir)
190 logger.info("The APPLI directory %s has been added to plugin paths"%appli_dir)
192 #SALOME_PLUGINS_PATH environment variable (list of directories separated by ":")
193 pluginspath=os.getenv("SALOME_PLUGINS_PATH")
195 for directory in pluginspath.split(SEP):
196 self.plugindirs.append(directory)
197 logger.info("The directory %s has been added to plugin paths"%directory)
199 self.basemenu = find_menu(self.basemenuname)
202 self.menu=QtGui.QMenu(self.menuname)
203 mid=sgPyQt.createMenu(self.menu.menuAction(),self.basemenuname)
205 self.menu=QtGui.QMenu(self.menuname,self.basemenu)
206 self.basemenu.addMenu(self.menu)
208 self.menu.menuAction().setVisible(False)
210 self.basemenu.connect(self.basemenu, QtCore.SIGNAL("aboutToShow()"), self.importPlugins)
212 def analyseFile(self,filename):
214 This function checks if the specified file is a plugins python
215 module and add the directory name of this file to the list of
216 plugin paths. This function is aimed to be used as the callback
217 function of the walktree algorithm.
219 if str(filename).endswith(MATCH_ENDING_PATTERN):
220 dirpath=os.path.dirname(filename)
221 if dirpath not in self.plugindirs:
222 self.plugindirs.append(dirpath)
223 logger.debug("The directory %s has been added to plugin paths"%dirpath)
225 def AddFunction(self,name,description,script):
226 """ Add a plugin function
228 self.registry[name]=script,description
229 self.entries.append(name)
231 def handler(obj=self,script=script):
233 script(Context(sgPyQt))
235 s=traceback.format_exc()
236 QtGui.QMessageBox.warning(None,"Exception occured",s)
238 self.handlers[name]=handler
240 def importPlugins(self):
241 """Execute the salome_plugins file that contains plugins definition """
242 studyId=sg.getActiveStudyId()
245 self.menu.menuAction().setVisible(False)
247 elif self.lasttime ==0:
248 salome.salome_init(embedded=1)
253 plugins_file_name=self.name+MATCH_ENDING_PATTERN
254 for directory in self.plugindirs:
255 plugins_file = os.path.join(directory,plugins_file_name)
256 if os.path.isfile(plugins_file):
257 plugins_files.append((directory,plugins_file))
258 lasttime=max(lasttime,os.path.getmtime(plugins_file))
262 if not plugins_files:
263 self.registry.clear()
264 self.handlers.clear()
268 self.menu.menuAction().setVisible(False)
271 if self.plugins_files != plugins_files or lasttime > self.lasttime:
272 global current_plugins_manager
273 current_plugins_manager=self
274 self.registry.clear()
275 self.handlers.clear()
277 self.lasttime=lasttime
278 for directory,plugins_file in plugins_files:
279 if directory not in sys.path:
280 sys.path.insert(0,directory)
282 execfile(plugins_file,globals(),{})
284 logger.fatal("Error while loading plugins from file %s"%plugins_file)
285 traceback.print_exc()
289 def updateMenu(self):
290 """Update the Plugins menu"""
292 for entry in self.entries:
293 names=entry.split("/")
294 if len(names) < 1:continue
298 #create or get submenus
300 for action in parentMenu.actions():
303 submenus[str(menu.title())]=menu
304 while len(names) > 1:
306 if submenus.has_key(name):
309 amenu=QtGui.QMenu(name,parentMenu)
310 parentMenu.addMenu(amenu)
315 act=parentMenu.addAction(name,self.handlers[entry])
316 act.setStatusTip(self.registry[entry][1])
318 self.menu.menuAction().setVisible(True)
320 def AddFunction(name,description,script):
321 """ Add a plugin function
322 Called by a user to register a function (script)
324 return current_plugins_manager.AddFunction(name,description,script)
327 """ Return the list of entries in menu: can be sorted or modified in place to customize menu content """
328 return current_plugins_manager.entries