46 pluginActivated = pyqtSignal(str, object) |
47 pluginActivated = pyqtSignal(str, object) |
47 allPlugginsActivated = pyqtSignal() |
48 allPlugginsActivated = pyqtSignal() |
48 pluginAboutToBeDeactivated = pyqtSignal(str, object) |
49 pluginAboutToBeDeactivated = pyqtSignal(str, object) |
49 pluginDeactivated = pyqtSignal(str, object) |
50 pluginDeactivated = pyqtSignal(str, object) |
50 |
51 |
51 def __init__(self, parent = None, doLoadPlugins = True, develPlugin = None): |
52 def __init__(self, parent=None, doLoadPlugins=True, develPlugin=None): |
52 """ |
53 """ |
53 Constructor |
54 Constructor |
54 |
55 |
55 The Plugin Manager deals with three different plugin directories. |
56 The Plugin Manager deals with three different plugin directories. |
56 The first is the one, that is part of eric5 (eric5/Plugins). The |
57 The first is the one, that is part of eric5 (eric5/Plugins). The |
57 second one is the global plugin directory called 'eric5plugins', |
58 second one is the global plugin directory called 'eric5plugins', |
58 which is located inside the site-packages directory. The last one |
59 which is located inside the site-packages directory. The last one |
59 is the user plugin directory located inside the .eric5 directory |
60 is the user plugin directory located inside the .eric5 directory |
60 of the users home directory. |
61 of the users home directory. |
61 |
62 |
62 @param parent reference to the parent object (QObject) |
63 @param parent reference to the parent object (QObject) |
63 @keyparam doLoadPlugins flag indicating, that plugins should |
64 @keyparam doLoadPlugins flag indicating, that plugins should |
64 be loaded (boolean) |
65 be loaded (boolean) |
65 @keyparam develPlugin filename of a plugin to be loaded for |
66 @keyparam develPlugin filename of a plugin to be loaded for |
66 development (string) |
67 development (string) |
67 """ |
68 """ |
68 QObject.__init__(self, parent) |
69 QObject.__init__(self, parent) |
69 |
70 |
70 self.__ui = parent |
71 self.__ui = parent |
72 self.__develPluginName = None |
73 self.__develPluginName = None |
73 |
74 |
74 self.__inactivePluginsKey = "PluginManager/InactivePlugins" |
75 self.__inactivePluginsKey = "PluginManager/InactivePlugins" |
75 |
76 |
76 self.pluginDirs = { |
77 self.pluginDirs = { |
77 "eric5" : os.path.join(getConfig('ericDir'), "Plugins"), |
78 "eric5": os.path.join(getConfig('ericDir'), "Plugins"), |
78 "global" : os.path.join(Utilities.getPythonModulesDirectory(), |
79 "global": os.path.join(Utilities.getPythonModulesDirectory(), |
79 "eric5plugins"), |
80 "eric5plugins"), |
80 "user" : os.path.join(Utilities.getConfigDir(), "eric5plugins"), |
81 "user": os.path.join(Utilities.getConfigDir(), "eric5plugins"), |
81 } |
82 } |
82 self.__priorityOrder = ["eric5", "global", "user"] |
83 self.__priorityOrder = ["eric5", "global", "user"] |
83 |
84 |
84 self.__defaultDownloadDir = os.path.join(Utilities.getConfigDir(), "Downloads") |
85 self.__defaultDownloadDir = os.path.join(Utilities.getConfigDir(), "Downloads") |
85 |
86 |
140 """ |
141 """ |
141 Private method to check, if the plugin folders exist. |
142 Private method to check, if the plugin folders exist. |
142 |
143 |
143 If the plugin folders don't exist, they are created (if possible). |
144 If the plugin folders don't exist, they are created (if possible). |
144 |
145 |
145 @return tuple of a flag indicating existence of any of the plugin |
146 @return tuple of a flag indicating existence of any of the plugin |
146 directories (boolean) and a message (string) |
147 directories (boolean) and a message (string) |
147 """ |
148 """ |
148 if self.__develPluginFile: |
149 if self.__develPluginFile: |
149 path = Utilities.splitPath(self.__develPluginFile)[0] |
150 path = Utilities.splitPath(self.__develPluginFile)[0] |
150 fname = os.path.join(path, "__init__.py") |
151 fname = os.path.join(path, "__init__.py") |
151 if not os.path.exists(fname): |
152 if not os.path.exists(fname): |
152 try: |
153 try: |
153 f = open(fname, "w") |
154 f = open(fname, "w") |
154 f.close() |
155 f.close() |
155 except IOError: |
156 except IOError: |
156 return (False, |
157 return (False, |
157 self.trUtf8("Could not create a package for {0}.")\ |
158 self.trUtf8("Could not create a package for {0}.")\ |
158 .format(self.__develPluginFile)) |
159 .format(self.__develPluginFile)) |
159 |
160 |
160 if Preferences.getPluginManager("ActivateExternal"): |
161 if Preferences.getPluginManager("ActivateExternal"): |
161 fname = os.path.join(self.pluginDirs["user"], "__init__.py") |
162 fname = os.path.join(self.pluginDirs["user"], "__init__.py") |
171 if not os.path.exists(self.pluginDirs["global"]) and \ |
172 if not os.path.exists(self.pluginDirs["global"]) and \ |
172 os.access(Utilities.getPythonModulesDirectory(), os.W_OK): |
173 os.access(Utilities.getPythonModulesDirectory(), os.W_OK): |
173 # create the global plugins directory |
174 # create the global plugins directory |
174 os.mkdir(self.pluginDirs["global"], 0o755) |
175 os.mkdir(self.pluginDirs["global"], 0o755) |
175 fname = os.path.join(self.pluginDirs["global"], "__init__.py") |
176 fname = os.path.join(self.pluginDirs["global"], "__init__.py") |
176 f = open(fname, "w", encoding = "utf-8") |
177 f = open(fname, "w", encoding="utf-8") |
177 f.write('# -*- coding: utf-8 -*-' + "\n") |
178 f.write('# -*- coding: utf-8 -*-' + "\n") |
178 f.write("\n") |
179 f.write("\n") |
179 f.write('"""' + "\n") |
180 f.write('"""' + "\n") |
180 f.write('Package containing the global plugins.' + "\n") |
181 f.write('Package containing the global plugins.' + "\n") |
181 f.write('"""' + "\n") |
182 f.write('"""' + "\n") |
281 |
282 |
282 if develPluginName: |
283 if develPluginName: |
283 self.loadPlugin(develPluginName, develPluginPath) |
284 self.loadPlugin(develPluginName, develPluginPath) |
284 self.__develPluginName = develPluginName |
285 self.__develPluginName = develPluginName |
285 |
286 |
286 def loadPlugin(self, name, directory, reload_ = False): |
287 def loadPlugin(self, name, directory, reload_=False): |
287 """ |
288 """ |
288 Public method to load a plugin module. |
289 Public method to load a plugin module. |
289 |
290 |
290 Initially all modules are inactive. Modules that are requested on |
291 Initially all modules are inactive. Modules that are requested on |
291 demand are sorted out and are added to the on demand list. Some |
292 demand are sorted out and are added to the on demand list. Some |
439 for name in names: |
440 for name in names: |
440 if savedInactiveList is None or name not in savedInactiveList: |
441 if savedInactiveList is None or name not in savedInactiveList: |
441 self.activatePlugin(name) |
442 self.activatePlugin(name) |
442 self.allPlugginsActivated.emit() |
443 self.allPlugginsActivated.emit() |
443 |
444 |
444 def activatePlugin(self, name, onDemand = False): |
445 def activatePlugin(self, name, onDemand=False): |
445 """ |
446 """ |
446 Public method to activate a plugin. |
447 Public method to activate a plugin. |
447 |
448 |
448 @param name name of the module to be activated |
449 @param name name of the module to be activated |
449 @keyparam onDemand flag indicating activation of an |
450 @keyparam onDemand flag indicating activation of an |
450 on demand plugin (boolean) |
451 on demand plugin (boolean) |
451 @return reference to the initialized plugin object |
452 @return reference to the initialized plugin object |
452 """ |
453 """ |
453 try: |
454 try: |
454 try: |
455 try: |
526 className = getattr(module, "className") |
527 className = getattr(module, "className") |
527 if not hasattr(module, className): |
528 if not hasattr(module, className): |
528 raise PluginModuleFormatError(module.eric5PluginModuleName, className) |
529 raise PluginModuleFormatError(module.eric5PluginModuleName, className) |
529 pluginClass = getattr(module, className) |
530 pluginClass = getattr(module, className) |
530 if not hasattr(pluginClass, "__init__"): |
531 if not hasattr(pluginClass, "__init__"): |
531 raise PluginClassFormatError(module.eric5PluginModuleName, |
532 raise PluginClassFormatError(module.eric5PluginModuleName, |
532 className, "__init__") |
533 className, "__init__") |
533 if not hasattr(pluginClass, "activate"): |
534 if not hasattr(pluginClass, "activate"): |
534 raise PluginClassFormatError(module.eric5PluginModuleName, |
535 raise PluginClassFormatError(module.eric5PluginModuleName, |
535 className, "activate") |
536 className, "activate") |
536 if not hasattr(pluginClass, "deactivate"): |
537 if not hasattr(pluginClass, "deactivate"): |
537 raise PluginClassFormatError(module.eric5PluginModuleName, |
538 raise PluginClassFormatError(module.eric5PluginModuleName, |
538 className, "deactivate") |
539 className, "deactivate") |
539 return True |
540 return True |
540 except PluginModuleFormatError as e: |
541 except PluginModuleFormatError as e: |
541 print(repr(e)) |
542 print(repr(e)) |
542 return False |
543 return False |
543 except PluginClassFormatError as e: |
544 except PluginClassFormatError as e: |
544 print(repr(e)) |
545 print(repr(e)) |
545 return False |
546 return False |
546 |
547 |
547 def deactivatePlugin(self, name, onDemand = False): |
548 def deactivatePlugin(self, name, onDemand=False): |
548 """ |
549 """ |
549 Public method to deactivate a plugin. |
550 Public method to deactivate a plugin. |
550 |
551 |
551 @param name name of the module to be deactivated |
552 @param name name of the module to be deactivated |
552 @keyparam onDemand flag indicating deactivation of an |
553 @keyparam onDemand flag indicating deactivation of an |
553 on demand plugin (boolean) |
554 on demand plugin (boolean) |
554 """ |
555 """ |
555 try: |
556 try: |
556 if onDemand: |
557 if onDemand: |
557 module = self.__onDemandActiveModules[name] |
558 module = self.__onDemandActiveModules[name] |
593 @return flag indicating, if the module satisfies all requirements |
594 @return flag indicating, if the module satisfies all requirements |
594 for being deactivated (boolean) |
595 for being deactivated (boolean) |
595 """ |
596 """ |
596 return getattr(module, "deactivateable", True) |
597 return getattr(module, "deactivateable", True) |
597 |
598 |
598 def getPluginObject(self, type_, typename, maybeActive = False): |
599 def getPluginObject(self, type_, typename, maybeActive=False): |
599 """ |
600 """ |
600 Public method to activate an ondemand plugin given by type and typename. |
601 Public method to activate an ondemand plugin given by type and typename. |
601 |
602 |
602 @param type_ type of the plugin to be activated (string) |
603 @param type_ type of the plugin to be activated (string) |
603 @param typename name of the plugin within the type category (string) |
604 @param typename name of the plugin within the type category (string) |
606 @return reference to the initialized plugin object |
607 @return reference to the initialized plugin object |
607 """ |
608 """ |
608 for name, module in list(self.__onDemandInactiveModules.items()): |
609 for name, module in list(self.__onDemandInactiveModules.items()): |
609 if getattr(module, "pluginType") == type_ and \ |
610 if getattr(module, "pluginType") == type_ and \ |
610 getattr(module, "pluginTypename") == typename: |
611 getattr(module, "pluginTypename") == typename: |
611 return self.activatePlugin(name, onDemand = True) |
612 return self.activatePlugin(name, onDemand=True) |
612 |
613 |
613 if maybeActive: |
614 if maybeActive: |
614 for name, module in list(self.__onDemandActiveModules.items()): |
615 for name, module in list(self.__onDemandActiveModules.items()): |
615 if getattr(module, "pluginType") == type_ and \ |
616 if getattr(module, "pluginType") == type_ and \ |
616 getattr(module, "pluginTypename") == typename: |
617 getattr(module, "pluginTypename") == typename: |
617 self.deactivatePlugin(name, onDemand = True) |
618 self.deactivatePlugin(name, onDemand=True) |
618 return self.activatePlugin(name, onDemand = True) |
619 return self.activatePlugin(name, onDemand=True) |
619 |
620 |
620 return None |
621 return None |
621 |
622 |
622 def getPluginInfos(self): |
623 def getPluginInfos(self): |
623 """ |
624 """ |
624 Public method to get infos about all loaded plugins. |
625 Public method to get infos about all loaded plugins. |
625 |
626 |
626 @return list of tuples giving module name (string), plugin name (string), |
627 @return list of tuples giving module name (string), plugin name (string), |
627 version (string), autoactivate (boolean), active (boolean), |
628 version (string), autoactivate (boolean), active (boolean), |
628 short description (string), error flag (boolean) |
629 short description (string), error flag (boolean) |
629 """ |
630 """ |
630 infos = [] |
631 infos = [] |
631 |
632 |
632 for name in list(self.__activeModules.keys()): |
633 for name in list(self.__activeModules.keys()): |
654 def __getShortInfo(self, module): |
655 def __getShortInfo(self, module): |
655 """ |
656 """ |
656 Private method to extract the short info from a module. |
657 Private method to extract the short info from a module. |
657 |
658 |
658 @param module module to extract short info from |
659 @param module module to extract short info from |
659 @return short info as a tuple giving plugin name (string), |
660 @return short info as a tuple giving plugin name (string), |
660 short description (string), error flag (boolean) and |
661 short description (string), error flag (boolean) and |
661 version (string) |
662 version (string) |
662 """ |
663 """ |
663 name = getattr(module, "name", "") |
664 name = getattr(module, "name", "") |
664 shortDesc = getattr(module, "shortDescription", "") |
665 shortDesc = getattr(module, "shortDescription", "") |
797 <li>header - string to be diplayed as a header (string)</li> |
798 <li>header - string to be diplayed as a header (string)</li> |
798 <li>exe - the executable (string)</li> |
799 <li>exe - the executable (string)</li> |
799 <li>versionCommand - commandline parameter for the exe (string)</li> |
800 <li>versionCommand - commandline parameter for the exe (string)</li> |
800 <li>versionStartsWith - indicator for the output line containing |
801 <li>versionStartsWith - indicator for the output line containing |
801 the version (string)</li> |
802 the version (string)</li> |
802 <li>versionPosition - number of element containing the |
803 <li>versionPosition - number of element containing the |
803 version (integer)</li> |
804 version (integer)</li> |
804 <li>version - version to be used as default (string)</li> |
805 <li>version - version to be used as default (string)</li> |
805 <li>versionCleanup - tuple of two integers giving string positions |
806 <li>versionCleanup - tuple of two integers giving string positions |
806 start and stop for the version string (tuple of integers)</li> |
807 start and stop for the version string (tuple of integers)</li> |
807 </ul> |
808 </ul> |
845 This should be a localized string</dd> |
846 This should be a localized string</dd> |
846 <dt>pixmap name</dt> |
847 <dt>pixmap name</dt> |
847 <dd>filename of the pixmap to be shown next to the display string</dd> |
848 <dd>filename of the pixmap to be shown next to the display string</dd> |
848 <dt>page creation function</dt> |
849 <dt>page creation function</dt> |
849 <dd>plugin module function to be called to create the configuration |
850 <dd>plugin module function to be called to create the configuration |
850 page. The page must be subclasses from |
851 page. The page must be subclasses from |
851 Preferences.ConfigurationPages.ConfigurationPageBase and must |
852 Preferences.ConfigurationPages.ConfigurationPageBase and must |
852 implement a method called 'save' to save the settings. A parent |
853 implement a method called 'save' to save the settings. A parent |
853 entry will be created in the selection list, if this value is None.</dd> |
854 entry will be created in the selection list, if this value is None.</dd> |
854 <dt>parent key</dt> |
855 <dt>parent key</dt> |
855 <dd>dictionary key of the parent entry or None, if this defines a |
856 <dd>dictionary key of the parent entry or None, if this defines a |
856 toplevel entry.</dd> |
857 toplevel entry.</dd> |
857 <dt>reference to configuration page</dt> |
858 <dt>reference to configuration page</dt> |
858 <dd>This will be used by the configuration dialog and must always be None</dd> |
859 <dd>This will be used by the configuration dialog and must always be None</dd> |
859 </dl> |
860 </dl> |
860 """ |
861 """ |