diff --git a/krita/plugins/extensions/pykrita/src/krita/__init__.py b/krita/plugins/extensions/pykrita/src/krita/__init__.py index 1c9a9d579b..b846e251fa 100644 --- a/krita/plugins/extensions/pykrita/src/krita/__init__.py +++ b/krita/plugins/extensions/pykrita/src/krita/__init__.py @@ -1,49 +1,46 @@ import pykrita import os import sys - -def pykritaEventHandler(event): - def _decorator(func): - setattr(pykrita, event, func) - del func - return _decorator +from .api import * +from .configuration import * +from .decorators import * def kDebug(text): '''Use KDE way to show debug info TODO Add a way to control debug output from partucular plugins (?) ''' plugin = sys._getframe(1).f_globals['__name__'] pykrita.kDebug('{}: {}'.format(plugin, text)) @pykritaEventHandler('_pluginLoaded') def on_load(plugin): if plugin in init.functions: # Call registered init functions for the plugin init.fire(plugin=plugin) del init.functions[plugin] return True @pykritaEventHandler('_pluginUnloading') def on_unload(plugin): if plugin in unload.functions: # Deinitialize plugin unload.fire(plugin=plugin) del unload.functions[plugin] return True @pykritaEventHandler('_pykritaLoaded') def on_pykrita_loaded(): kDebug('PYKRITA LOADED') return True @pykritaEventHandler('_pykritaUnloading') def on_pykrita_unloading(): kDebug('UNLOADING PYKRITA') return True diff --git a/krita/plugins/extensions/pykrita/src/krita/api.py b/krita/plugins/extensions/pykrita/src/krita/api.py new file mode 100644 index 0000000000..53bb8c43b5 --- /dev/null +++ b/krita/plugins/extensions/pykrita/src/krita/api.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# This file is part of Pate, Kate' Python scripting plugin. +# +# Copyright (C) 2006 Paul Giannaros +# Copyright (C) 2013 Shaheed Haque +# Copyright (C) 2013 Alex Turbov +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) version 3. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +'''Provide shortcuts to access kate internals from plugins''' + +import contextlib +import os +import sys + +import pykrita + +def objectIsAlive(obj): + ''' Test whether an object is alive; that is, whether the pointer + to the object still exists. ''' + import sip + try: + sip.unwrapinstance(obj) + except RuntimeError: + return False + return True + + +def kDebug(text): + '''Use KDE way to show debug info + + TODO Add a way to control debug output from partucular plugins (?) + ''' + plugin = sys._getframe(1).f_globals['__name__'] + pate.kDebug('{}: {}'.format(plugin, text)) + diff --git a/krita/plugins/extensions/pykrita/src/krita/configuration.py b/krita/plugins/extensions/pykrita/src/krita/configuration.py new file mode 100644 index 0000000000..9e94abcb08 --- /dev/null +++ b/krita/plugins/extensions/pykrita/src/krita/configuration.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2006 Paul Giannaros +# Copyright (C) 2013 Shaheed Haque +# Copyright (C) 2013 Alex Turbov +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) version 3. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +'''Configuration related stuff''' + +import pykrita # Built-in module +import sys + + +class Configuration: + '''A Configuration object provides a plugin-specific persistent + configuration dictionary. The configuration is saved and loaded from disk + automatically. + + Do not instantiate your own Configuration object; a plugin simply uses + pykrita.configuration and the class automatically creates a plugin-specific + dictionary to support it. + + Use a string key. Any Python type that can be pickled is can be used as a + value -- dictionaries, lists, numbers, strings, sets, and so on. + ''' + + def __init__(self, root): + self.root = root + + def __getitem__(self, key): + plugin = sys._getframe(1).f_globals['__name__'] + return self.root.get(plugin, {})[key] + + def __setitem__(self, key, value): + plugin = sys._getframe(1).f_globals['__name__'] + if plugin not in self.root: + self.root[plugin] = {} + self.root[plugin][key] = value + + def __delitem__(self, key): + plugin = sys._getframe(1).f_globals['__name__'] + del self.root.get(plugin, {})[key] + + def __contains__(self, key): + plugin = sys._getframe(1).f_globals['__name__'] + return key in self.root.get(plugin, {}) + + def __len__(self): + plugin = sys._getframe(1).f_globals['__name__'] + return len(self.root.get(plugin, {})) + + def __iter__(self): + plugin = sys._getframe(1).f_globals['__name__'] + return iter(self.root.get(plugin, {})) + + def __str__(self): + plugin = sys._getframe(1).f_globals['__name__'] + return str(self.root.get(plugin, {})) + + def __repr__(self): + plugin = sys._getframe(1).f_globals['__name__'] + return repr(self.root.get(plugin, {})) + + def keys(self): + '''Return the keys from the configuration dictionary.''' + plugin = sys._getframe(1).f_globals['__name__'] + return self.root.get(plugin, {}).keys() + + def values(self): + '''Return the values from the configuration dictionary.''' + plugin = sys._getframe(1).f_globals['__name__'] + return self.root.get(plugin, {}).values() + + def items(self): + '''Return the items from the configuration dictionary.''' + plugin = sys._getframe(1).f_globals['__name__'] + return self.root.get(plugin, {}).items() + + def get(self, key, default=None): + '''Fetch a configuration item using it's string key, returning + a given default if not found. + + Parameters: + * key - String key for item. + * default - Value to return if key is not found. + + Returns: + The item value for key, or the given default if not found. + ''' + plugin = sys._getframe(1).f_globals['__name__'] + try: + return self.root.get(plugin, {})[key] + except KeyError: + return default + + def pop(self, key): + '''Delete a configuration item using it's string key. + + Parameters: + * key - String key for item. + Returns: + The value of the removed item. + Throws: + KeyError if key doesn't exist. + ''' + plugin = sys._getframe(1).f_globals['__name__'] + value = self.root.get(plugin, {})[key] + del self.root.get(plugin, {})[key] + return value + + def save(self): + pykrita.saveConfiguration() + + def _name(self): + return sys._getframe(1).f_globals['__name__'] + + +globalConfiguration = pykrita.configuration +'''Configuration for all plugins. + +This can also be used by one plugin to access another plugin's configurations. +''' + +configuration = Configuration(pykrita.configuration) +'''Persistent configuration dictionary for this plugin.''' + + +sessionConfiguration = Configuration(pykrita.sessionConfiguration) +'''Per session persistent configuration dictionary for this plugin.''' diff --git a/krita/plugins/extensions/pykrita/src/krita/decorators.py b/krita/plugins/extensions/pykrita/src/krita/decorators.py new file mode 100644 index 0000000000..648d54b931 --- /dev/null +++ b/krita/plugins/extensions/pykrita/src/krita/decorators.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2006 Paul Giannaros +# Copyright (C) 2013 Shaheed Haque +# Copyright (C) 2013 Alex Turbov +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) version 3. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +'''Decorators used in plugins''' + +import functools +import inspect +import sys +import traceback + +from PyQt4 import QtCore, QtGui + +import pykrita + +from .api import * + +# +# initialization related stuff +# + +def pykritaEventHandler(event): + def _decorator(func): + setattr(pykrita, event, func) + del func + return _decorator + + +def _callAll(plugin, functions, *args, **kwargs): + if plugin in functions: + for f in functions[plugin]: + try: + f(*args, **kwargs) + except: + traceback.print_exc() + sys.stderr.write('\n') + # TODO Return smth to a caller, so in case of + # failed initialization it may report smth to the + # C++ level and latter can show an error to the user... + continue + + +def _simpleEventListener(func): + # automates the most common decorator pattern: calling a bunch + # of functions when an event has occurred + func.functions = dict() + func.fire = functools.partial(_callAll, functions=func.functions) + func.clear = func.functions.clear + return func + + +def _registerCallback(plugin, event, func): + if plugin not in event.functions: + event.functions[plugin] = set() + + event.functions[plugin].add(func) + return func + + +@_simpleEventListener +def init(func): + ''' The function will be called when particular plugin has loaded + and the configuration has been initiated + ''' + plugin = sys._getframe(1).f_globals['__name__'] + kDebug('@init: {}/{}'.format(plugin, func.__name__)) + return _registerCallback(plugin, init, func) + + +@_simpleEventListener +def unload(func): + ''' The function will be called when particular plugin is being + unloaded from memory. Clean up any widgets that you have added + to the interface (toolviews etc). + + ATTENTION Be really careful trying to access any window, view + or document from the @unload handler: in case of application + quit everything is dead already! + ''' + plugin = sys._getframe(1).f_globals['__name__'] + kDebug('@unload: {}/{}'.format(plugin, func.__name__)) + def _module_cleaner(): + kDebug('@unload/cleaner: {}/{}'.format(plugin, func.__name__)) + if plugin in init.functions: + kDebug('@unload/init-cleaner: {}/{}'.format(plugin, func.__name__)) + del init.functions[plugin] + + func() + + return _registerCallback(plugin, unload, _module_cleaner) + +