PluginManager/PluginManager.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the Plugin Manager.
8 """
9
10 import os
11 import sys
12 import imp
13
14 from PyQt4.QtCore import *
15 from PyQt4.QtGui import QPixmap, QMessageBox
16
17 from PluginExceptions import *
18
19 import UI.PixmapCache
20
21 import Utilities
22 import Preferences
23
24 from eric4config import getConfig
25
26 class PluginManager(QObject):
27 """
28 Class implementing the Plugin Manager.
29
30 @signal shutdown() emitted at shutdown of the IDE
31 @signal pluginAboutToBeActivated(modulName, pluginObject) emitted just before a
32 plugin is activated
33 @signal pluginActivated(modulName, pluginObject) emitted just after a plugin
34 was activated
35 @signal allPlugginsActivated() emitted at startup after all plugins have
36 been activated
37 @signal pluginAboutToBeDeactivated(modulName, pluginObject) emitted just before a
38 plugin is deactivated
39 @signal pluginDeactivated(modulName, pluginObject) emitted just after a plugin
40 was deactivated
41 """
42 def __init__(self, parent = None, doLoadPlugins = True, develPlugin = None):
43 """
44 Constructor
45
46 The Plugin Manager deals with three different plugin directories.
47 The first is the one, that is part of eric4 (eric4/Plugins). The
48 second one is the global plugin directory called 'eric4plugins',
49 which is located inside the site-packages directory. The last one
50 is the user plugin directory located inside the .eric4 directory
51 of the users home directory.
52
53 @param parent reference to the parent object (QObject)
54 @keyparam doLoadPlugins flag indicating, that plugins should
55 be loaded (boolean)
56 @keyparam develPlugin filename of a plugin to be loaded for
57 development (string)
58 """
59 QObject.__init__(self, parent)
60
61 self.__ui = parent
62 self.__develPluginFile = develPlugin
63 self.__develPluginName = None
64
65 self.__inactivePluginsKey = "PluginManager/InactivePlugins"
66
67 self.pluginDirs = {
68 "eric4" : os.path.join(getConfig('ericDir'), "Plugins"),
69 "global" : os.path.join(Utilities.getPythonModulesDirectory(),
70 "eric4plugins"),
71 "user" : os.path.join(Utilities.getConfigDir(), "eric4plugins"),
72 }
73 self.__priorityOrder = ["eric4", "global", "user"]
74
75 self.__defaultDownloadDir = os.path.join(Utilities.getConfigDir(), "Downloads")
76
77 self.__activePlugins = {}
78 self.__inactivePlugins = {}
79 self.__onDemandActivePlugins = {}
80 self.__onDemandInactivePlugins = {}
81 self.__activeModules = {}
82 self.__inactiveModules = {}
83 self.__onDemandActiveModules = {}
84 self.__onDemandInactiveModules = {}
85 self.__failedModules = {}
86
87 self.__foundCoreModules = []
88 self.__foundGlobalModules = []
89 self.__foundUserModules = []
90
91 self.__modulesCount = 0
92
93 pdirsExist, msg = self.__pluginDirectoriesExist()
94 if not pdirsExist:
95 raise PluginPathError(msg)
96
97 if doLoadPlugins:
98 if not self.__pluginModulesExist():
99 raise PluginModulesError
100
101 self.__insertPluginsPaths()
102
103 self.__loadPlugins()
104
105 self.__checkPluginsDownloadDirectory()
106
107 def finalizeSetup(self):
108 """
109 Public method to finalize the setup of the plugin manager.
110 """
111 for module in self.__onDemandInactiveModules.values() + \
112 self.__onDemandActiveModules.values():
113 if hasattr(module, "moduleSetup"):
114 module.moduleSetup()
115
116 def getPluginDir(self, key):
117 """
118 Public method to get the path of a plugin directory.
119
120 @return path of the requested plugin directory (string)
121 """
122 if key not in ["global", "user"]:
123 return None
124 else:
125 try:
126 return self.pluginDirs[key]
127 except KeyError:
128 return None
129
130 def __pluginDirectoriesExist(self):
131 """
132 Private method to check, if the plugin folders exist.
133
134 If the plugin folders don't exist, they are created (if possible).
135
136 @return tuple of a flag indicating existence of any of the plugin
137 directories (boolean) and a message (string)
138 """
139 if self.__develPluginFile:
140 path = Utilities.splitPath(self.__develPluginFile)[0]
141 fname = os.path.join(path, "__init__.py")
142 if not os.path.exists(fname):
143 try:
144 f = open(fname, "wb")
145 f.close()
146 except IOError:
147 return (False,
148 self.trUtf8("Could not create a package for {0}.")\
149 .format(self.__develPluginFile))
150
151 if Preferences.getPluginManager("ActivateExternal"):
152 fname = os.path.join(self.pluginDirs["user"], "__init__.py")
153 if not os.path.exists(fname):
154 if not os.path.exists(self.pluginDirs["user"]):
155 os.mkdir(self.pluginDirs["user"], 0755)
156 try:
157 f = open(fname, "wb")
158 f.close()
159 except IOError:
160 del self.pluginDirs["user"]
161
162 if not os.path.exists(self.pluginDirs["global"]) and \
163 os.access(Utilities.getPythonModulesDirectory(), os.W_OK):
164 # create the global plugins directory
165 os.mkdir(self.pluginDirs["global"], 0755)
166 fname = os.path.join(self.pluginDirs["global"], "__init__.py")
167 f = open()
168 f.write('# -*- coding: utf-8 -*-' + os.linesep)
169 f.write(os.linesep)
170 f.write('"""' + os.linesep)
171 f.write('Package containing the global plugins.' + os.linesep)
172 f.write('"""' + os.linesep)
173 f.close()
174 if not os.path.exists(self.pluginDirs["global"]):
175 del self.pluginDirs["global"]
176 else:
177 del self.pluginDirs["user"]
178 del self.pluginDirs["global"]
179
180 if not os.path.exists(self.pluginDirs["eric4"]):
181 return (False,
182 self.trUtf8("The internal plugin directory <b>{0}</b> does not exits.")\
183 .format(self.pluginDirs["eric4"]))
184
185 return (True, "")
186
187 def __pluginModulesExist(self):
188 """
189 Private method to check, if there are plugins available.
190
191 @return flag indicating the availability of plugins (boolean)
192 """
193 if self.__develPluginFile and not os.path.exists(self.__develPluginFile):
194 return False
195
196 self.__foundCoreModules = self.getPluginModules(self.pluginDirs["eric4"])
197 if "global" in self.pluginDirs:
198 self.__foundGlobalModules = \
199 self.getPluginModules(self.pluginDirs["global"])
200 if "user" in self.pluginDirs:
201 self.__foundUserModules = \
202 self.getPluginModules(self.pluginDirs["user"])
203
204 return len(self.__foundCoreModules + self.__foundGlobalModules + \
205 self.__foundUserModules) > 0
206
207 def getPluginModules(self, pluginPath):
208 """
209 Public method to get a list of plugin modules.
210
211 @param pluginPath name of the path to search (string)
212 @return list of plugin module names (list of string)
213 """
214 pluginFiles = [f[:-3] for f in os.listdir(pluginPath) \
215 if self.isValidPluginName(f)]
216 return pluginFiles[:]
217
218 def isValidPluginName(self, pluginName):
219 """
220 Public methode to check, if a file name is a valid plugin name.
221
222 Plugin modules must start with "Plugin" and have the extension ".py".
223
224 @param pluginName name of the file to be checked (string)
225 @return flag indicating a valid plugin name (boolean)
226 """
227 return pluginName.startswith("Plugin") and pluginName.endswith(".py")
228
229 def __insertPluginsPaths(self):
230 """
231 Private method to insert the valid plugin paths intos the search path.
232 """
233 for key in self.__priorityOrder:
234 if key in self.pluginDirs:
235 if not self.pluginDirs[key] in sys.path:
236 sys.path.insert(2, self.pluginDirs[key])
237 UI.PixmapCache.addSearchPath(self.pluginDirs[key])
238
239 if self.__develPluginFile:
240 path = Utilities.splitPath(self.__develPluginFile)[0]
241 if not path in sys.path:
242 sys.path.insert(2, path)
243 UI.PixmapCache.addSearchPath(path)
244
245 def __loadPlugins(self):
246 """
247 Private method to load the plugins found.
248 """
249 develPluginName = ""
250 if self.__develPluginFile:
251 develPluginPath, develPluginName = \
252 Utilities.splitPath(self.__develPluginFile)
253 if self.isValidPluginName(develPluginName):
254 develPluginName = develPluginName[:-3]
255
256 for pluginName in self.__foundCoreModules:
257 # global and user plugins have priority
258 if pluginName not in self.__foundGlobalModules and \
259 pluginName not in self.__foundUserModules and \
260 pluginName != develPluginName:
261 self.loadPlugin(pluginName, self.pluginDirs["eric4"])
262
263 for pluginName in self.__foundGlobalModules:
264 # user plugins have priority
265 if pluginName not in self.__foundUserModules and \
266 pluginName != develPluginName:
267 self.loadPlugin(pluginName, self.pluginDirs["global"])
268
269 for pluginName in self.__foundUserModules:
270 if pluginName != develPluginName:
271 self.loadPlugin(pluginName, self.pluginDirs["user"])
272
273 if develPluginName:
274 self.loadPlugin(develPluginName, develPluginPath)
275 self.__develPluginName = develPluginName
276
277 def loadPlugin(self, name, directory, reload_ = False):
278 """
279 Public method to load a plugin module.
280
281 Initially all modules are inactive. Modules that are requested on
282 demand are sorted out and are added to the on demand list. Some
283 basic validity checks are performed as well. Modules failing these
284 checks are added to the failed modules list.
285
286 @param name name of the module to be loaded (string)
287 @param directory name of the plugin directory (string)
288 @param reload_ flag indicating to reload the module (boolean)
289 """
290 try:
291 fname = "%s.py" % os.path.join(directory, name)
292 module = imp.load_source(name, fname)
293 if not hasattr(module, "autoactivate"):
294 module.error = \
295 self.trUtf8("Module is missing the 'autoactivate' attribute.")
296 self.__failedModules[name] = module
297 raise PluginLoadError(name)
298 if getattr(module, "autoactivate"):
299 self.__inactiveModules[name] = module
300 else:
301 if not hasattr(module, "pluginType") or \
302 not hasattr(module, "pluginTypename"):
303 module.error = \
304 self.trUtf8("Module is missing the 'pluginType' "
305 "and/or 'pluginTypename' attributes.")
306 self.__failedModules[name] = module
307 raise PluginLoadError(name)
308 else:
309 self.__onDemandInactiveModules[name] = module
310 module.eric4PluginModuleName = name
311 module.eric4PluginModuleFilename = fname
312 self.__modulesCount += 1
313 if reload_:
314 reload(module)
315 except PluginLoadError:
316 print "Error loading plugin module:", name
317 except StandardError, err:
318 module = imp.new_module(name)
319 module.error = \
320 self.trUtf8("Module failed to load. Error: {0}").format(unicode(err))
321 self.__failedModules[name] = module
322 print "Error loading plugin module:", name
323 print unicode(err)
324
325 def unloadPlugin(self, name, directory):
326 """
327 Public method to unload a plugin module.
328
329 @param name name of the module to be unloaded (string)
330 @param directory name of the plugin directory (string)
331 @return flag indicating success (boolean)
332 """
333 fname = "%s.py" % os.path.join(directory, name)
334 if name in self.__onDemandActiveModules and \
335 self.__onDemandActiveModules[name].eric4PluginModuleFilename == fname:
336 # cannot unload an ondemand plugin, that is in use
337 return False
338
339 if name in self.__activeModules and \
340 self.__activeModules[name].eric4PluginModuleFilename == fname:
341 self.deactivatePlugin(name)
342
343 if name in self.__inactiveModules and \
344 self.__inactiveModules[name].eric4PluginModuleFilename == fname:
345 try:
346 del self.__inactivePlugins[name]
347 except KeyError:
348 pass
349 del self.__inactiveModules[name]
350 elif name in self.__onDemandInactiveModules and \
351 self.__onDemandInactiveModules[name].eric4PluginModuleFilename == fname:
352 try:
353 del self.__onDemandInactivePlugins[name]
354 except KeyError:
355 pass
356 del self.__onDemandInactiveModules[name]
357 elif name in self.__failedModules:
358 del self.__failedModules[name]
359
360 self.__modulesCount -= 1
361 return True
362
363 def removePluginFromSysModules(self, pluginName, package, internalPackages):
364 """
365 Public method to remove a plugin and all related modules from sys.modules.
366
367 @param pluginName name of the plugin module (string)
368 @param package name of the plugin package (string)
369 @param internalPackages list of intenal packages (list of string)
370 @return flag indicating the plugin module was found in sys.modules (boolean)
371 """
372 packages = [package] + internalPackages
373 found = False
374 if not package:
375 package = "__None__"
376 for moduleName in sys.modules.keys()[:]:
377 if moduleName == pluginName or moduleName.split(".")[0] in packages:
378 found = True
379 del sys.modules[moduleName]
380 return found
381
382 def initOnDemandPlugins(self):
383 """
384 Public method to create plugin objects for all on demand plugins.
385
386 Note: The plugins are not activated.
387 """
388 names = sorted(self.__onDemandInactiveModules.keys())
389 for name in names:
390 self.initOnDemandPlugin(name)
391
392 def initOnDemandPlugin(self, name):
393 """
394 Public method to create a plugin object for the named on demand plugin.
395
396 Note: The plugin is not activated.
397 """
398 try:
399 try:
400 module = self.__onDemandInactiveModules[name]
401 except KeyError:
402 return None
403
404 if not self.__canActivatePlugin(module):
405 raise PluginActivationError(module.eric4PluginModuleName)
406 version = getattr(module, "version")
407 className = getattr(module, "className")
408 pluginClass = getattr(module, className)
409 pluginObject = None
410 if name not in self.__onDemandInactivePlugins:
411 pluginObject = pluginClass(self.__ui)
412 pluginObject.eric4PluginModule = module
413 pluginObject.eric4PluginName = className
414 pluginObject.eric4PluginVersion = version
415 self.__onDemandInactivePlugins[name] = pluginObject
416 except PluginActivationError:
417 return None
418
419 def activatePlugins(self):
420 """
421 Public method to activate all plugins having the "autoactivate" attribute
422 set to True.
423 """
424 ial = Preferences.Prefs.settings.value(self.__inactivePluginsKey)
425 if ial.isValid():
426 savedInactiveList = ial.toStringList()
427 else:
428 savedInactiveList = None
429 if self.__develPluginName is not None and \
430 savedInactiveList is not None and \
431 self.__develPluginName in savedInactiveList:
432 savedInactiveList.remove(self.__develPluginName)
433 names = sorted(self.__inactiveModules.keys())
434 for name in names:
435 if savedInactiveList is None or name not in savedInactiveList:
436 self.activatePlugin(name)
437 self.emit(SIGNAL("allPlugginsActivated()"))
438
439 def activatePlugin(self, name, onDemand = False):
440 """
441 Public method to activate a plugin.
442
443 @param name name of the module to be activated
444 @keyparam onDemand flag indicating activation of an
445 on demand plugin (boolean)
446 @return reference to the initialized plugin object
447 """
448 try:
449 try:
450 if onDemand:
451 module = self.__onDemandInactiveModules[name]
452 else:
453 module = self.__inactiveModules[name]
454 except KeyError:
455 return None
456
457 if not self.__canActivatePlugin(module):
458 raise PluginActivationError(module.eric4PluginModuleName)
459 version = getattr(module, "version")
460 className = getattr(module, "className")
461 pluginClass = getattr(module, className)
462 pluginObject = None
463 if onDemand and name in self.__onDemandInactivePlugins:
464 pluginObject = self.__onDemandInactivePlugins[name]
465 elif not onDemand and name in self.__inactivePlugins:
466 pluginObject = self.__inactivePlugins[name]
467 else:
468 pluginObject = pluginClass(self.__ui)
469 self.emit(SIGNAL("pluginAboutToBeActivated"), name, pluginObject)
470 try:
471 obj, ok = pluginObject.activate()
472 except TypeError:
473 module.error = self.trUtf8("Incompatible plugin activation method.")
474 obj = None
475 ok = True
476 except StandardError, err:
477 module.error = unicode(err)
478 obj = None
479 ok = False
480 if not ok:
481 return None
482
483 self.emit(SIGNAL("pluginActivated"), name, pluginObject)
484 pluginObject.eric4PluginModule = module
485 pluginObject.eric4PluginName = className
486 pluginObject.eric4PluginVersion = version
487
488 if onDemand:
489 self.__onDemandInactiveModules.pop(name)
490 try:
491 self.__onDemandInactivePlugins.pop(name)
492 except KeyError:
493 pass
494 self.__onDemandActivePlugins[name] = pluginObject
495 self.__onDemandActiveModules[name] = module
496 else:
497 self.__inactiveModules.pop(name)
498 try:
499 self.__inactivePlugins.pop(name)
500 except KeyError:
501 pass
502 self.__activePlugins[name] = pluginObject
503 self.__activeModules[name] = module
504 return obj
505 except PluginActivationError:
506 return None
507
508 def __canActivatePlugin(self, module):
509 """
510 Private method to check, if a plugin can be activated.
511
512 @param module reference to the module to be activated
513 @return flag indicating, if the module satisfies all requirements
514 for being activated (boolean)
515 """
516 try:
517 if not hasattr(module, "version"):
518 raise PluginModuleFormatError(module.eric4PluginModuleName, "version")
519 if not hasattr(module, "className"):
520 raise PluginModuleFormatError(module.eric4PluginModuleName, "className")
521 className = getattr(module, "className")
522 if not hasattr(module, className):
523 raise PluginModuleFormatError(module.eric4PluginModuleName, className)
524 pluginClass = getattr(module, className)
525 if not hasattr(pluginClass, "__init__"):
526 raise PluginClassFormatError(module.eric4PluginModuleName,
527 className, "__init__")
528 if not hasattr(pluginClass, "activate"):
529 raise PluginClassFormatError(module.eric4PluginModuleName,
530 className, "activate")
531 if not hasattr(pluginClass, "deactivate"):
532 raise PluginClassFormatError(module.eric4PluginModuleName,
533 className, "deactivate")
534 return True
535 except PluginModuleFormatError, e:
536 print repr(e)
537 return False
538 except PluginClassFormatError, e:
539 print repr(e)
540 return False
541
542 def deactivatePlugin(self, name, onDemand = False):
543 """
544 Public method to deactivate a plugin.
545
546 @param name name of the module to be deactivated
547 @keyparam onDemand flag indicating deactivation of an
548 on demand plugin (boolean)
549 """
550 try:
551 if onDemand:
552 module = self.__onDemandActiveModules[name]
553 else:
554 module = self.__activeModules[name]
555 except KeyError:
556 return
557
558 if self.__canDeactivatePlugin(module):
559 pluginObject = None
560 if onDemand and name in self.__onDemandActivePlugins:
561 pluginObject = self.__onDemandActivePlugins[name]
562 elif not onDemand and name in self.__activePlugins:
563 pluginObject = self.__activePlugins[name]
564 if pluginObject:
565 self.emit(SIGNAL("pluginAboutToBeDeactivated"), name, pluginObject)
566 pluginObject.deactivate()
567 self.emit(SIGNAL("pluginDeactivated"), name, pluginObject)
568
569 if onDemand:
570 self.__onDemandActiveModules.pop(name)
571 self.__onDemandActivePlugins.pop(name)
572 self.__onDemandInactivePlugins[name] = pluginObject
573 self.__onDemandInactiveModules[name] = module
574 else:
575 self.__activeModules.pop(name)
576 try:
577 self.__activePlugins.pop(name)
578 except KeyError:
579 pass
580 self.__inactivePlugins[name] = pluginObject
581 self.__inactiveModules[name] = module
582
583 def __canDeactivatePlugin(self, module):
584 """
585 Private method to check, if a plugin can be deactivated.
586
587 @param module reference to the module to be deactivated
588 @return flag indicating, if the module satisfies all requirements
589 for being deactivated (boolean)
590 """
591 return getattr(module, "deactivateable", True)
592
593 def getPluginObject(self, type_, typename, maybeActive = False):
594 """
595 Public method to activate an ondemand plugin given by type and typename.
596
597 @param type_ type of the plugin to be activated (string)
598 @param typename name of the plugin within the type category (string)
599 @keyparam maybeActive flag indicating, that the plugin may be active
600 already (boolean)
601 @return reference to the initialized plugin object
602 """
603 for name, module in self.__onDemandInactiveModules.items():
604 if getattr(module, "pluginType") == type_ and \
605 getattr(module, "pluginTypename") == typename:
606 return self.activatePlugin(name, onDemand = True)
607
608 if maybeActive:
609 for name, module in self.__onDemandActiveModules.items():
610 if getattr(module, "pluginType") == type_ and \
611 getattr(module, "pluginTypename") == typename:
612 self.deactivatePlugin(name, onDemand = True)
613 return self.activatePlugin(name, onDemand = True)
614
615 return None
616
617 def getPluginInfos(self):
618 """
619 Public method to get infos about all loaded plugins.
620
621 @return list of tuples giving module name (string), plugin name (string),
622 version (string), autoactivate (boolean), active (boolean),
623 short description (string), error flag (boolean)
624 """
625 infos = []
626
627 for name in self.__activeModules.keys():
628 pname, shortDesc, error, version = \
629 self.__getShortInfo(self.__activeModules[name])
630 infos.append((name, pname, version, True, True, shortDesc, error))
631 for name in self.__inactiveModules.keys():
632 pname, shortDesc, error, version = \
633 self.__getShortInfo(self.__inactiveModules[name])
634 infos.append((name, pname, version, True, False, shortDesc, error))
635 for name in self.__onDemandActiveModules.keys():
636 pname, shortDesc, error, version = \
637 self.__getShortInfo(self.__onDemandActiveModules[name])
638 infos.append((name, pname, version, False, True, shortDesc, error))
639 for name in self.__onDemandInactiveModules.keys():
640 pname, shortDesc, error, version = \
641 self.__getShortInfo(self.__onDemandInactiveModules[name])
642 infos.append((name, pname, version, False, False, shortDesc, error))
643 for name in self.__failedModules.keys():
644 pname, shortDesc, error, version = \
645 self.__getShortInfo(self.__failedModules[name])
646 infos.append((name, pname, version, False, False, shortDesc, error))
647 return infos
648
649 def __getShortInfo(self, module):
650 """
651 Private method to extract the short info from a module.
652
653 @param module module to extract short info from
654 @return short info as a tuple giving plugin name (string),
655 short description (string), error flag (boolean) and
656 version (string)
657 """
658 name = getattr(module, "name", "")
659 shortDesc = getattr(module, "shortDescription", "")
660 version = getattr(module, "version", "")
661 error = getattr(module, "error", "") != ""
662 return name, shortDesc, error, version
663
664 def getPluginDetails(self, name):
665 """
666 Public method to get detailed information about a plugin.
667
668 @param name name of the module to get detailed infos about (string)
669 @return details of the plugin as a dictionary
670 """
671 details = {}
672
673 autoactivate = True
674 active = True
675
676 if name in self.__activeModules:
677 module = self.__activeModules[name]
678 elif name in self.__inactiveModules:
679 module = self.__inactiveModules[name]
680 active = False
681 elif name in self.__onDemandActiveModules:
682 module = self.__onDemandActiveModules[name]
683 autoactivate = False
684 elif name in self.__onDemandInactiveModules:
685 module = self.__onDemandInactiveModules[name]
686 autoactivate = False
687 active = False
688 elif name in self.__failedModules:
689 module = self.__failedModules[name]
690 autoactivate = False
691 active = False
692 else:
693 # should not happen
694 return None
695
696 details["moduleName"] = name
697 details["moduleFileName"] = getattr(module, "eric4PluginModuleFilename", "")
698 details["pluginName"] = getattr(module, "name", "")
699 details["version"] = getattr(module, "version", "")
700 details["author"] = getattr(module, "author", "")
701 details["description"] = getattr(module, "longDescription", "")
702 details["autoactivate"] = autoactivate
703 details["active"] = active
704 details["error"] = getattr(module, "error", "")
705
706 return details
707
708 def shutdown(self):
709 """
710 Public method called to perform actions upon shutdown of the IDE.
711 """
712 names = []
713 for name in self.__inactiveModules.keys():
714 names.append(name)
715 Preferences.Prefs.settings.setValue(self.__inactivePluginsKey, QVariant(names))
716
717 self.emit(SIGNAL("shutdown()"))
718
719 def getPluginDisplayStrings(self, type_):
720 """
721 Public method to get the display strings of all plugins of a specific type.
722
723 @param type_ type of the plugins (string)
724 @return dictionary with name as key and display string as value
725 (dictionary of string)
726 """
727 pluginDict = {}
728
729 for name, module in \
730 self.__onDemandActiveModules.items() + self.__onDemandInactiveModules.items():
731 if getattr(module, "pluginType") == type_ and \
732 getattr(module, "error", "") == "":
733 plugin_name = getattr(module, "pluginTypename")
734 if hasattr(module, "displayString"):
735 try:
736 disp = module.displayString()
737 except TypeError:
738 disp = getattr(module, "displayString")
739 if disp != "":
740 pluginDict[plugin_name] = disp
741 else:
742 pluginDict[plugin_name] = plugin_name
743
744 return pluginDict
745
746 def getPluginPreviewPixmap(self, type_, name):
747 """
748 Public method to get a preview pixmap of a plugin of a specific type.
749
750 @param type_ type of the plugin (string)
751 @param name name of the plugin type (string)
752 @return preview pixmap (QPixmap)
753 """
754 for modname, module in \
755 self.__onDemandActiveModules.items() + self.__onDemandInactiveModules.items():
756 if getattr(module, "pluginType") == type_ and \
757 getattr(module, "pluginTypename") == name:
758 if hasattr(module, "previewPix"):
759 return module.previewPix()
760 else:
761 return QPixmap()
762
763 return QPixmap()
764
765 def getPluginApiFiles(self, language):
766 """
767 Public method to get the list of API files installed by a plugin.
768
769 @param language language of the requested API files (string)
770 @return list of API filenames (list of string)
771 """
772 apis = []
773
774 for module in self.__activeModules.values() + \
775 self.__onDemandActiveModules.values():
776 if hasattr(module, "apiFiles"):
777 apis.extend(module.apiFiles(language))
778
779 return apis
780
781 def getPluginExeDisplayData(self):
782 """
783 Public method to get data to display information about a plugins
784 external tool.
785
786 @return list of dictionaries containing the data. Each dictionary must
787 either contain data for the determination or the data to be displayed.<br />
788 A dictionary of the first form must have the following entries:
789 <ul>
790 <li>programEntry - indicator for this dictionary form (boolean),
791 always True</li>
792 <li>header - string to be diplayed as a header (string)</li>
793 <li>exe - the executable (string)</li>
794 <li>versionCommand - commandline parameter for the exe (string)</li>
795 <li>versionStartsWith - indicator for the output line containing
796 the version (string)</li>
797 <li>versionPosition - number of element containing the
798 version (integer)</li>
799 <li>version - version to be used as default (string)</li>
800 <li>versionCleanup - tuple of two integers giving string positions
801 start and stop for the version string (tuple of integers)</li>
802 </ul>
803 A dictionary of the second form must have the following entries:
804 <ul>
805 <li>programEntry - indicator for this dictionary form (boolean),
806 always False</li>
807 <li>header - string to be diplayed as a header (string)</li>
808 <li>text - entry text to be shown (string)</li>
809 <li>version - version text to be shown (string)</li>
810 </ul>
811 """
812 infos = []
813
814 for module in self.__activeModules.values() + \
815 self.__inactiveModules.values():
816 if hasattr(module, "exeDisplayData"):
817 infos.append(module.exeDisplayData())
818 for module in self.__onDemandActiveModules.values() + \
819 self.__onDemandInactiveModules.values():
820 if hasattr(module, "exeDisplayData"):
821 infos.append(module.exeDisplayData())
822
823 return infos
824
825 def getPluginConfigData(self):
826 """
827 Public method to get the config data of all active, non on-demand plugins
828 used by the configuration dialog.
829
830 Plugins supporting this functionality must provide the plugin module
831 function 'getConfigData' returning a dictionary with unique keys
832 of lists with the following list contents:
833 <dl>
834 <dt>display string</dt>
835 <dd>string shown in the selection area of the configuration page.
836 This should be a localized string</dd>
837 <dt>pixmap name</dt>
838 <dd>filename of the pixmap to be shown next to the display string</dd>
839 <dt>page creation function</dt>
840 <dd>plugin module function to be called to create the configuration
841 page. The page must be subclasses from
842 Preferences.ConfigurationPages.ConfigurationPageBase and must
843 implement a method called 'save' to save the settings. A parent
844 entry will be created in the selection list, if this value is None.</dd>
845 <dt>parent key</dt>
846 <dd>dictionary key of the parent entry or None, if this defines a
847 toplevel entry.</dd>
848 <dt>reference to configuration page</dt>
849 <dd>This will be used by the configuration dialog and must always be None</dd>
850 </dl>
851 """
852 configData = {}
853 for module in self.__activeModules.values() + \
854 self.__onDemandActiveModules.values() + \
855 self.__onDemandInactiveModules.values():
856 if hasattr(module, 'getConfigData'):
857 configData.update(module.getConfigData())
858 return configData
859
860 def isPluginLoaded(self, pluginName):
861 """
862 Public method to check, if a certain plugin is loaded.
863
864 @param pluginName name of the plugin to check for (string or QString)
865 @return flag indicating, if the plugin is loaded (boolean)
866 """
867 return pluginName in self.__activeModules or \
868 pluginName in self.__inactiveModules or \
869 pluginName in self.__onDemandActiveModules or \
870 pluginName in self.__onDemandInactiveModules
871
872 def isPluginActive(self, pluginName):
873 """
874 Public method to check, if a certain plugin is active.
875
876 @param pluginName name of the plugin to check for (string or QString)
877 @return flag indicating, if the plugin is active (boolean)
878 """
879 return pluginName in self.__activeModules or \
880 pluginName in self.__onDemandActiveModules
881
882 ############################################################################
883 ## Specialized plugin module handling methods below
884 ############################################################################
885
886 ############################################################################
887 ## VCS related methods below
888 ############################################################################
889
890 def getVcsSystemIndicators(self):
891 """
892 Public method to get the Vcs System indicators.
893
894 Plugins supporting this functionality must support the module function
895 getVcsSystemIndicator returning a dictionary with indicator as key and
896 a tuple with the vcs name (string) and vcs display string (string).
897
898 @return dictionary with indicator as key and a list of tuples as values.
899 Each tuple contains the vcs name (string) and vcs display string (string).
900 """
901 vcsDict = {}
902
903 for name, module in \
904 self.__onDemandActiveModules.items() + self.__onDemandInactiveModules.items():
905 if getattr(module, "pluginType") == "version_control":
906 if hasattr(module, "getVcsSystemIndicator"):
907 res = module.getVcsSystemIndicator()
908 for indicator, vcsData in res.items():
909 if indicator in vcsDict:
910 vcsDict[indicator].append(vcsData)
911 else:
912 vcsDict[indicator] = [vcsData]
913
914 return vcsDict
915
916 def deactivateVcsPlugins(self):
917 """
918 Public method to deactivated all activated VCS plugins.
919 """
920 for name, module in self.__onDemandActiveModules.items():
921 if getattr(module, "pluginType") == "version_control":
922 self.deactivatePlugin(name, True)
923
924 def __checkPluginsDownloadDirectory(self):
925 """
926 Private slot to check for the existence of the plugins download directory.
927 """
928 downloadDir = Preferences.getPluginManager("DownloadPath")
929 if not downloadDir:
930 downloadDir = self.__defaultDownloadDir
931
932 if not os.path.exists(downloadDir):
933 try:
934 os.mkdir(downloadDir, 0755)
935 except (OSError, IOError), err:
936 # try again with (possibly) new default
937 downloadDir = self.__defaultDownloadDir
938 if not os.path.exists(downloadDir):
939 try:
940 os.mkdir(downloadDir, 0755)
941 except (OSError, IOError), err:
942 QMessageBox.critical(self.__ui,
943 self.trUtf8("Plugin Manager Error"),
944 self.trUtf8("""<p>The plugin download directory <b>{0}</b> """
945 """could not be created. Please configure it """
946 """via the configuration dialog.</p>"""
947 """<p>Reason: {1}</p>""")\
948 .format(downloadDir, unicode(err)))
949 downloadDir = ""
950
951 Preferences.setPluginManager("DownloadPath", downloadDir)
952
953 def preferencesChanged(self):
954 """
955 Public slot to react to changes in configuration.
956 """
957 self.__checkPluginsDownloadDirectory()

eric ide

mercurial