1
+ # -*- coding: utf-8 -*-
1
2
"""
2
3
QGIS utilities module
3
4
6
7
from PyQt4 .QtCore import QCoreApplication
7
8
import sys
8
9
import traceback
10
+ import glob
11
+ import os .path
12
+ import re
9
13
10
14
11
15
#######################
@@ -94,6 +98,30 @@ def uninstallConsoleHooks():
94
98
# list of active (started) plugins
95
99
active_plugins = []
96
100
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
+
97
125
def pluginMetadata (packageName , fct ):
98
126
""" fetch metadata from a plugin """
99
127
try :
@@ -103,7 +131,7 @@ def pluginMetadata(packageName, fct):
103
131
return "__error__"
104
132
105
133
def loadPlugin (packageName ):
106
- """ load plugin's package and ensure that plugin is reloaded when changed """
134
+ """ load plugin's package """
107
135
108
136
try :
109
137
__import__ (packageName )
@@ -117,7 +145,6 @@ def loadPlugin(packageName):
117
145
# retry
118
146
try :
119
147
__import__ (packageName )
120
- reload (packageName )
121
148
return True
122
149
except :
123
150
msgTemplate = QCoreApplication .translate ("Python" , "Couldn't load plugin '%1' from ['%2']" )
@@ -140,6 +167,7 @@ def startPlugin(packageName):
140
167
try :
141
168
plugins [packageName ] = package .classFactory (iface )
142
169
except :
170
+ _unloadPluginModules (packageName )
143
171
msg = QCoreApplication .translate ("Python" , "%1 due an error when calling its classFactory() method" ).arg (errMsg )
144
172
showException (sys .exc_type , sys .exc_value , sys .exc_traceback , msg )
145
173
return False
@@ -148,6 +176,8 @@ def startPlugin(packageName):
148
176
try :
149
177
plugins [packageName ].initGui ()
150
178
except :
179
+ del plugins [packageName ]
180
+ _unloadPluginModules (packageName )
151
181
msg = QCoreApplication .translate ("Python" , "%1 due an error when calling its initGui() method" ).arg ( errMsg )
152
182
showException (sys .exc_type , sys .exc_value , sys .exc_traceback , msg )
153
183
return False
@@ -169,15 +199,77 @@ def unloadPlugin(packageName):
169
199
plugins [packageName ].unload ()
170
200
del plugins [packageName ]
171
201
active_plugins .remove (packageName )
202
+ _unloadPluginModules (packageName )
172
203
return True
173
204
except Exception , e :
174
205
msg = QCoreApplication .translate ("Python" , "Error while unloading plugin %1" ).arg (packageName )
175
206
showException (sys .exc_type , sys .exc_value , sys .exc_traceback , msg )
176
207
return False
177
208
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
+
178
233
def isPluginLoaded (packageName ):
179
234
""" find out whether a plugin is active (i.e. has been started) """
180
235
global plugins , active_plugins
181
236
182
237
if not plugins .has_key (packageName ): return False
183
238
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
0 commit comments