1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 This module is imported from C++ SalomeApp_Application and initialized
23 (call to initialize function with 4 parameters)
24 module : 0 if it is plugins manager at the application level, 1 if it is at the module level
25 name : the name of the plugins manager. This name is used to build the name of the plugins files
26 basemenuname : the name of the 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 "a".
59 It is possible to put entries in submenus. You only need to give a
60 name with / to the entry. for example::
62 salome_pluginsmanager.AddFunction('a/b/About','About SALOME pluginmanager',about)
64 will add 2 submenus a and b before creating the entry.
66 In short to add a plugin:
68 1. import the python module salome_pluginsmanager (in your
69 salome_plugins.py or <module>_plugins.py)
71 2. write a function with one argument context (it's an object with 3
74 3. register the function with a call to AddFunction (entry in menu plugins,
79 - sg : the SALOME Swig interface
80 - studyId : the SALOME studyId that must be used to execute the plugin
81 - study : the SALOME study object that must be used to execute the plugin
85 import os,sys,traceback
86 from PyQt4 import QtGui
87 from PyQt4 import QtCore
92 if sys.platform == "win32":
95 # Get SALOME PyQt interface
97 sgPyQt = SalomePyQt.SalomePyQt()
99 # Get SALOME Swig interface
100 import libSALOME_Swig
101 sg = libSALOME_Swig.SALOMEGUI_Swig()
104 current_plugins_manager=None
106 def initialize(module,name,basemenuname,menuname):
107 if not plugins.has_key(name):
113 d=sgPyQt.getDesktop()
114 if plugins[name].has_key(d):return
115 plugins[name][d]=PluginsManager(module,name,basemenuname,menuname)
117 plugins[name].append(PluginsManager(module,name,basemenuname,menuname))
120 def __init__(self,sgpyqt):
122 self.studyId=salome.sg.getActiveStudyId()
123 self.study= salome.myStudyManager.GetStudyByID(self.studyId)
125 def find_menu(smenu):
126 lmenus=smenu.split("|")
127 main=lmenus.takeFirst().trimmed()
128 menu=sgPyQt.getPopupMenu(main)
129 return findMenu(lmenus,menu)
131 def findMenu(lmenu,menu):
132 if not lmenu:return menu
133 m=lmenu.takeFirst().trimmed()
134 for a in menu.actions():
137 return findMenu(lmenu,a.menu())
139 PLUGIN_PATH_PATTERN="share/salome/plugins"
140 MATCH_ENDING_PATTERN="_plugins.py"
141 from salome.kernel.syshelper import walktree
142 from salome.kernel.logger import Logger
143 #from salome.kernel.termcolor import GREEN
144 logger=Logger("PluginsManager") #,color=GREEN)
145 # VSR 21/11/2011 : do not show infos in the debug mode
148 class PluginsManager:
149 def __init__(self,module,name,basemenuname,menuname):
151 self.basemenuname=QtCore.QString.fromUtf8(basemenuname)
152 self.menuname=QtCore.QString.fromUtf8(menuname)
159 self.plugins_files=[]
161 # MODULES plugins directory.
162 # The SALOME modules may provides natively some plugins. These
163 # MODULES plugins are supposed to be located in the
164 # installation folder of the module, in the subdirectory
165 # "share/salome/plugins". We first look for these directories.
166 for key in os.environ.keys():
167 if key.endswith("_ROOT_DIR"):
168 rootpath=os.environ[key]
169 dirpath=os.path.join(rootpath,PLUGIN_PATH_PATTERN)
170 if os.path.isdir(dirpath) and dirpath not in self.plugindirs:
171 logger.debug("Looking for plugins in the directory %s ..."%dirpath)
172 walktree(dirpath,self.analyseFile)
174 # USER plugins directory
175 user_dir = os.path.expanduser("~/.config/salome/Plugins")
176 self.plugindirs.append(user_dir)
177 logger.info("The user directory %s has been added to plugin paths"%user_dir)
178 # obsolete: USER plugins directory
179 # (for compatibility reasons only; new plugins should be stored in ~/.config/salome/Plugins)
180 user_obsolete_dir = os.path.expanduser("~/.salome/Plugins")
181 self.plugindirs.append(user_obsolete_dir)
182 logger.info("The user directory %s has been added to plugin paths (deprecated)"%user_obsolete_dir)
184 # APPLI plugins directory
185 appli=os.getenv("APPLI")
187 appli_dir=os.path.join(os.path.expanduser("~"),appli,"Plugins")
188 self.plugindirs.append(appli_dir)
189 logger.info("The APPLI directory %s has been added to plugin paths"%appli_dir)
191 #SALOME_PLUGINS_PATH environment variable (list of directories separated by ":")
192 pluginspath=os.getenv("SALOME_PLUGINS_PATH")
194 for directory in pluginspath.split(SEP):
195 self.plugindirs.append(directory)
196 logger.info("The directory %s has been added to plugin paths"%directory)
198 self.basemenu = find_menu(self.basemenuname)
201 self.menu=QtGui.QMenu(self.menuname)
202 mid=sgPyQt.createMenu(self.menu.menuAction(),self.basemenuname)
204 self.menu=QtGui.QMenu(self.menuname,self.basemenu)
205 self.basemenu.addMenu(self.menu)
207 self.menu.menuAction().setVisible(False)
209 self.basemenu.connect(self.basemenu, QtCore.SIGNAL("aboutToShow()"), self.importPlugins)
211 def analyseFile(self,filename):
213 This function checks if the specified file is a plugins python
214 module and add the directory name of this file to the list of
215 plugin paths. This function is aimed to be used as the callback
216 function of the walktree algorithm.
218 if str(filename).endswith(MATCH_ENDING_PATTERN):
219 dirpath=os.path.dirname(filename)
220 if dirpath not in self.plugindirs:
221 self.plugindirs.append(dirpath)
222 logger.debug("The directory %s has been added to plugin paths"%dirpath)
224 def AddFunction(self,name,description,script):
225 """ Add a plugin function
227 self.registry[name]=script,description
228 self.entries.append(name)
230 def handler(obj=self,script=script):
232 script(Context(sgPyQt))
234 s=traceback.format_exc()
235 QtGui.QMessageBox.warning(None,"Exception occured",s)
237 self.handlers[name]=handler
239 def importPlugins(self):
240 """Execute the salome_plugins file that contains plugins definition """
241 studyId=sg.getActiveStudyId()
244 self.menu.menuAction().setVisible(False)
246 elif self.lasttime ==0 or salome.myStudy == None:
247 salome.salome_init(embedded=1)
252 plugins_file_name=self.name+MATCH_ENDING_PATTERN
253 for directory in self.plugindirs:
254 plugins_file = os.path.join(directory,plugins_file_name)
255 if os.path.isfile(plugins_file):
256 plugins_files.append((directory,plugins_file))
257 lasttime=max(lasttime,os.path.getmtime(plugins_file))
261 if not plugins_files:
262 self.registry.clear()
263 self.handlers.clear()
267 self.menu.menuAction().setVisible(False)
270 if self.plugins_files != plugins_files or lasttime > self.lasttime:
271 global current_plugins_manager
272 current_plugins_manager=self
273 self.registry.clear()
274 self.handlers.clear()
276 self.lasttime=lasttime
277 for directory,plugins_file in plugins_files:
278 logger.debug("look for python path: %s"%directory)
279 if directory not in sys.path:
280 sys.path.insert(0,directory)
281 logger.debug("The directory %s has been added to PYTHONPATH"%directory)
283 execfile(plugins_file,globals(),{})
285 logger.fatal("Error while loading plugins from file %s"%plugins_file)
286 traceback.print_exc()
290 def updateMenu(self):
291 """Update the Plugins menu"""
293 for entry in self.entries:
294 names=entry.split("/")
295 if len(names) < 1:continue
299 #create or get submenus
301 for action in parentMenu.actions():
304 submenus[str(menu.title())]=menu
305 while len(names) > 1:
307 if submenus.has_key(name):
310 amenu=QtGui.QMenu(name,parentMenu)
311 parentMenu.addMenu(amenu)
316 act=parentMenu.addAction(name,self.handlers[entry])
317 act.setStatusTip(self.registry[entry][1])
319 self.menu.menuAction().setVisible(True)
321 def AddFunction(name,description,script):
322 """ Add a plugin function
323 Called by a user to register a function (script)
325 return current_plugins_manager.AddFunction(name,description,script)
328 """ Return the list of entries in menu: can be sorted or modified in place to customize menu content """
329 return current_plugins_manager.entries