Skip to content

Commit c86e095

Browse files
author
wonder
committed
Python support: use a wrapper for import statement that tracks modules of plugins.
This enables complete unload and reload of plugins: - qgis.utils.unloadPlugin(name) - now removes also plugin's modules (files) from python - qgis.utils.reloadPlugin(name) - unloads and starts the plugin again (using fresh source files) git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@12690 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent c953e5c commit c86e095

File tree

2 files changed

+98
-18
lines changed

2 files changed

+98
-18
lines changed

python/utils.py

+94-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
"""
23
QGIS utilities module
34
@@ -6,6 +7,9 @@
67
from PyQt4.QtCore import QCoreApplication
78
import sys
89
import traceback
10+
import glob
11+
import os.path
12+
import re
913

1014

1115
#######################
@@ -94,6 +98,30 @@ def uninstallConsoleHooks():
9498
# list of active (started) plugins
9599
active_plugins = []
96100

101+
# list of plugins in plugin directory and home plugin directory
102+
available_plugins = []
103+
104+
105+
def updateAvailablePlugins():
106+
from qgis.core import QgsApplication
107+
pythonPath = unicode(QgsApplication.pkgDataPath()) + "/python"
108+
homePythonPath = unicode(QgsApplication.qgisSettingsDirPath()) + "/python"
109+
110+
plugins = map(os.path.basename, glob.glob(pythonPath + "/plugins/*"))
111+
homePlugins = map(os.path.basename, glob.glob(homePythonPath + "/plugins/*"))
112+
113+
# merge the lists
114+
for p in homePlugins:
115+
if p not in plugins:
116+
plugins.append(p)
117+
118+
global available_plugins
119+
available_plugins = plugins
120+
121+
# update list on start
122+
updateAvailablePlugins()
123+
124+
97125
def pluginMetadata(packageName, fct):
98126
""" fetch metadata from a plugin """
99127
try:
@@ -103,7 +131,7 @@ def pluginMetadata(packageName, fct):
103131
return "__error__"
104132

105133
def loadPlugin(packageName):
106-
""" load plugin's package and ensure that plugin is reloaded when changed """
134+
""" load plugin's package """
107135

108136
try:
109137
__import__(packageName)
@@ -117,7 +145,6 @@ def loadPlugin(packageName):
117145
# retry
118146
try:
119147
__import__(packageName)
120-
reload(packageName)
121148
return True
122149
except:
123150
msgTemplate = QCoreApplication.translate("Python", "Couldn't load plugin '%1' from ['%2']")
@@ -140,6 +167,7 @@ def startPlugin(packageName):
140167
try:
141168
plugins[packageName] = package.classFactory(iface)
142169
except:
170+
_unloadPluginModules(packageName)
143171
msg = QCoreApplication.translate("Python", "%1 due an error when calling its classFactory() method").arg(errMsg)
144172
showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
145173
return False
@@ -148,6 +176,8 @@ def startPlugin(packageName):
148176
try:
149177
plugins[packageName].initGui()
150178
except:
179+
del plugins[packageName]
180+
_unloadPluginModules(packageName)
151181
msg = QCoreApplication.translate("Python", "%1 due an error when calling its initGui() method" ).arg( errMsg )
152182
showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
153183
return False
@@ -169,15 +199,77 @@ def unloadPlugin(packageName):
169199
plugins[packageName].unload()
170200
del plugins[packageName]
171201
active_plugins.remove(packageName)
202+
_unloadPluginModules(packageName)
172203
return True
173204
except Exception, e:
174205
msg = QCoreApplication.translate("Python", "Error while unloading plugin %1").arg(packageName)
175206
showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
176207
return False
177208

209+
210+
def _unloadPluginModules(packageName):
211+
""" unload plugin package with all its modules (files) """
212+
global _plugin_modules
213+
mods = _plugin_modules[packageName]
214+
215+
for mod in mods:
216+
# if it looks like a Qt resource file, try to do a cleanup
217+
# otherwise we might experience a segfault next time the plugin is loaded
218+
# because Qt will try to access invalid plugin resource data
219+
if "resources" in mod:
220+
try:
221+
sys.modules[mod].qCleanupResources()
222+
except:
223+
pass
224+
# try to remove the module from python
225+
try:
226+
del sys.modules[mod]
227+
except:
228+
pass
229+
# remove the plugin entry
230+
del _plugin_modules[packageName]
231+
232+
178233
def isPluginLoaded(packageName):
179234
""" find out whether a plugin is active (i.e. has been started) """
180235
global plugins, active_plugins
181236

182237
if not plugins.has_key(packageName): return False
183238
return (packageName in active_plugins)
239+
240+
241+
def reloadPlugin(packageName):
242+
""" unload and start again a plugin """
243+
global active_plugins
244+
if packageName not in active_plugins:
245+
return # it's not active
246+
247+
unloadPlugin(packageName)
248+
loadPlugin(packageName)
249+
startPlugin(packageName)
250+
251+
252+
#######################
253+
# IMPORT wrapper
254+
255+
import __builtin__
256+
257+
_builtin_import = __builtin__.__import__
258+
_plugin_modules = { }
259+
260+
def _import(name, globals={}, locals={}, fromlist=[], level=-1):
261+
""" wrapper around builtin import that keeps track of loaded plugin modules """
262+
mod = _builtin_import(name, globals, locals, fromlist, level)
263+
264+
if mod and '__file__' in mod.__dict__:
265+
module_name = mod.__name__
266+
package_name = module_name.split('.')[0]
267+
# check whether the module belongs to one of our plugins
268+
if package_name in available_plugins:
269+
if package_name not in _plugin_modules:
270+
_plugin_modules[package_name] = set()
271+
_plugin_modules[package_name].add(module_name)
272+
273+
return mod
274+
275+
__builtin__.__import__ = _import

src/python/qgspythonutilsimpl.cpp

+4-16
Original file line numberDiff line numberDiff line change
@@ -424,23 +424,11 @@ QString QgsPythonUtilsImpl::homePluginsPath()
424424

425425
QStringList QgsPythonUtilsImpl::pluginList()
426426
{
427-
QDir pluginDir( QgsPythonUtilsImpl::pluginsPath(), "*",
428-
QDir::Name | QDir::IgnoreCase, QDir::Dirs | QDir::NoDotAndDotDot );
427+
runString( "qgis.utils.updateAvailablePlugins()" );
429428

430-
QDir homePluginDir( QgsPythonUtilsImpl::homePluginsPath(), "*",
431-
QDir::Name | QDir::IgnoreCase, QDir::Dirs | QDir::NoDotAndDotDot );
432-
433-
QStringList pluginList = pluginDir.entryList();
434-
435-
for ( uint i = 0; i < homePluginDir.count(); i++ )
436-
{
437-
QString packageName = homePluginDir[i];
438-
if ( !pluginList.contains( packageName ) )
439-
pluginList.append( packageName );
440-
441-
}
442-
443-
return pluginList;
429+
QString output;
430+
evalString( "'\\n'.join(qgis.utils.available_plugins)", output );
431+
return output.split( QChar( '\n' ) );
444432
}
445433

446434
QString QgsPythonUtilsImpl::getPluginMetadata( QString pluginName, QString function )

0 commit comments

Comments
 (0)