1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2016 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 qtsalome 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 qtsalome import *
91 if sys.platform == "win32":
94 # Get SALOME PyQt interface
96 sgPyQt = SalomePyQt.SalomePyQt()
98 # Get SALOME Swig interface
100 sg = libSALOME_Swig.SALOMEGUI_Swig()
103 current_plugins_manager=None
105 def initialize(module,name,basemenuname,menuname):
106 if name not in plugins:
112 d=sgPyQt.getDesktop()
113 if d in plugins[name]:return
114 plugins[name][d]=PluginsManager(module,name,basemenuname,menuname)
116 plugins[name].append(PluginsManager(module,name,basemenuname,menuname))
119 def __init__(self,sgpyqt):
121 self.studyId=salome.sg.getActiveStudyId()
122 self.study= salome.myStudyManager.GetStudyByID(self.studyId)
124 def find_menu(smenu):
125 lmenus=smenu.split("|")
126 # Take first element from the list
127 main=lmenus.pop(0).strip()
128 menu=sgPyQt.getPopupMenu(main)
129 return findMenu(lmenus,menu)
131 def findMenu(lmenu,menu):
132 if not lmenu:return menu
133 # Take first element from the list
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.
168 for key in os.environ.keys():
169 if key.endswith("_ROOT_DIR"):
170 rootpath=os.environ[key]
171 dirpath=os.path.join(rootpath,PLUGIN_PATH_PATTERN)
172 if os.path.isdir(dirpath) and dirpath not in self.plugindirs + searched:
173 logger.debug("Looking for plugins in the directory %s ..."%dirpath)
174 walktree(dirpath,self.analyseFile)
175 if dirpath not in self.plugindirs and dirpath not in searched:
176 searched.append(dirpath)
178 # USER plugins directory
179 user_dir = os.path.expanduser("~/.config/salome/Plugins")
180 self.plugindirs.append(user_dir)
181 logger.info("The user directory %s has been added to plugin paths"%user_dir)
182 # obsolete: USER plugins directory
183 # (for compatibility reasons only; new plugins should be stored in ~/.config/salome/Plugins)
184 user_obsolete_dir = os.path.expanduser("~/.salome/Plugins")
185 self.plugindirs.append(user_obsolete_dir)
186 logger.info("The user directory %s has been added to plugin paths (deprecated)"%user_obsolete_dir)
188 # APPLI plugins directory
189 appli=os.getenv("APPLI")
191 appli_dir=os.path.join(os.path.expanduser("~"),appli,"Plugins")
192 self.plugindirs.append(appli_dir)
193 logger.info("The APPLI directory %s has been added to plugin paths"%appli_dir)
195 #SALOME_PLUGINS_PATH environment variable (list of directories separated by ":")
196 pluginspath=os.getenv("SALOME_PLUGINS_PATH")
198 for directory in pluginspath.split(SEP):
199 self.plugindirs.append(directory)
200 logger.info("The directory %s has been added to plugin paths"%directory)
202 self.basemenu = find_menu(self.basemenuname)
205 self.menu=QMenu(self.menuname)
206 mid=sgPyQt.createMenu(self.menu.menuAction(),self.basemenuname)
208 self.menu=QMenu(self.menuname,self.basemenu)
209 self.basemenu.addMenu(self.menu)
211 self.menu.menuAction().setVisible(False)
213 self.basemenu.aboutToShow.connect(self.importPlugins)
215 def analyseFile(self,filename):
217 This function checks if the specified file is a plugins python
218 module and add the directory name of this file to the list of
219 plugin paths. This function is aimed to be used as the callback
220 function of the walktree algorithm.
222 if str(filename).endswith(MATCH_ENDING_PATTERN):
223 dirpath=os.path.dirname(filename)
224 if dirpath not in self.plugindirs:
225 self.plugindirs.append(dirpath)
226 logger.debug("The directory %s has been added to plugin paths"%dirpath)
228 def AddFunction(self,name,description,script):
229 """ Add a plugin function
231 self.registry[name]=script,description
232 self.entries.append(name)
234 def handler(obj=self,script=script):
236 script(Context(sgPyQt))
238 s=traceback.format_exc()
239 QMessageBox.warning(None,"Exception occured",s)
241 self.handlers[name]=handler
243 def importPlugins(self):
244 """Execute the salome_plugins file that contains plugins definition """
245 studyId=sg.getActiveStudyId()
248 self.menu.menuAction().setVisible(False)
250 elif self.lasttime ==0 or salome.myStudy == None:
251 salome.salome_init(embedded=1)
256 plugins_file_name=self.name+MATCH_ENDING_PATTERN
257 for directory in self.plugindirs:
258 plugins_file = os.path.join(directory,plugins_file_name)
259 if os.path.isfile(plugins_file):
260 plugins_files.append((directory,plugins_file))
261 lasttime=max(lasttime,os.path.getmtime(plugins_file))
265 if not plugins_files:
266 self.registry.clear()
267 self.handlers.clear()
271 self.menu.menuAction().setVisible(False)
274 if self.plugins_files != plugins_files or lasttime > self.lasttime:
275 global current_plugins_manager
276 current_plugins_manager=self
277 self.registry.clear()
278 self.handlers.clear()
280 self.lasttime=lasttime
281 for directory,plugins_file in plugins_files:
282 logger.debug("look for python path: %s"%directory)
283 if directory not in sys.path:
284 sys.path.insert(0,directory)
285 logger.debug("The directory %s has been added to PYTHONPATH"%directory)
287 exec(compile(open(plugins_file).read(), plugins_file, 'exec'),globals(),{})
289 logger.fatal("Error while loading plugins from file %s"%plugins_file)
290 traceback.print_exc()
294 def updateMenu(self):
295 """Update the Plugins menu"""
297 for entry in self.entries:
298 names=entry.split("/")
299 if len(names) < 1:continue
303 #create or get submenus
305 for action in parentMenu.actions():
308 submenus[str(menu.title())]=menu
309 while len(names) > 1:
314 amenu=QMenu(name,parentMenu)
315 parentMenu.addMenu(amenu)
320 act=parentMenu.addAction(name,self.handlers[entry])
321 act.setStatusTip(self.registry[entry][1])
323 self.menu.menuAction().setVisible(True)
325 def AddFunction(name,description,script):
326 """ Add a plugin function
327 Called by a user to register a function (script)
329 return current_plugins_manager.AddFunction(name,description,script)
332 """ Return the list of entries in menu: can be sorted or modified in place to customize menu content """
333 return current_plugins_manager.entries