From c49b632809f89d610d5320012e8b63cf107ae3f9 Mon Sep 17 00:00:00 2001 From: "J. Victor Martins" Date: Fri, 28 Nov 2014 18:41:46 -0200 Subject: [PATCH 1/6] Initial version of experimental port to PyGObjects --- ugtk2/__init__.py | 83 ++++++++++++++++++++++ ugtk2/methods.py | 171 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 ugtk2/__init__.py create mode 100644 ugtk2/methods.py diff --git a/ugtk2/__init__.py b/ugtk2/__init__.py new file mode 100644 index 0000000..1974d48 --- /dev/null +++ b/ugtk2/__init__.py @@ -0,0 +1,83 @@ +# python-ugtk - a functional layer upon PyGObject +# Copyright (C) 2014, J. Victor Martins +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Contributor(s): J. Victor Martins +# + +"""ugtk is a module providing a functional layer upon PyGObject/GTK+.""" + + +class Actions(object): + def __init__(self, method, actions): + self.method = method + self.actions = actions + + +class ActionHandler(object): + """Represent a ugtk action handler class.""" + + signals = [] + set_actions = {} + + def handle(self, widget, actions): + """Apply actions to the widget the method.""" + + self.on_start_dispatch(method, widget, actions) + self.__dispatch_set_action(widget, actions) + # Dispatch the remaining actions to callbacks, ignore if they + # don't exist ... + for action in actions.keys(): + callback_name = 'on_%s__callback' % action + if hasattr(self, callback_name): + callback = getattr(self, callback_name) + callback(method, widget, actions.pop(action)) + self.on_finalize_dispatch(method, widget, actions) + + def __dispatch_set_action(self, widget, actions): + for action in self.set_actions: + if action in actions: + method = self.set_actions[action] + if not method: + method = "set_%s" % action + getattr(widget, method)(actions.pop(action)) + + def on_start_dispatch(self, method, widget, actions): + """Called before any other operation during a dispatch.""" + pass + + def on_finalize_dispatch(self, method, widget, actions): + """Called after all other operations during a dispatch.""" + pass + + def on_create(self, klass, actions): + """Called to create an object of the class.""" + raise ValueError("creating widget for class %s is not supported" + % klass.__name__) + + +_dispatchers = {} + +def register(gtk_cls, dispatcher_cls): + """Register a new dispatcher for the selected GTK class.""" + try: + registered[gtk_cls] + except KeyError: + if not issubclass(dispatcher_cls, ActionDispatcher): + raise ValueError('%s must be a subclass of %s to be registered as ' + 'a ugtk action dispatcher.' + % (klass.__name__, ActionDispatcher.__name__)) + + registered[klass] = dispatcher_class diff --git a/ugtk2/methods.py b/ugtk2/methods.py new file mode 100644 index 0000000..22a7473 --- /dev/null +++ b/ugtk2/methods.py @@ -0,0 +1,171 @@ +# python-ugtk - a functional layer upon PyGObject +# Copyright (C) 2014, J. Victor Martins +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Contributor(s): J. Victor Martins +# + + +import sys +import inspect +import functools +from gi.repository import GObject, Gtk + + +_agent_types = {} + + +def _execute(method, actions, *, klass=None, widget=None): + """Execute actions over a GTK class/widget. + + Each action is to be applied in the class or a parent class in + MRO, as long as if there is an action handler available for that + class. + + """ + + if bool(klass) == bool(widget): + raise ValueError("You can't specify both klass and widget") + + if widget: + klass = widget.__class__ + + for subclass in klass.__mro__: + try: + agent = _agent_types[subclass](method, actions, widget) + except KeyError: + # don't apply actions to a class that doesn't have an + # agent, but ignore in the case of subclasses + if subclass == klass: + raise ValueError("missing handler for class: %s" + % klass.__name__) + else: + widget = agent() + + if actions: + # Some action were not used, currently this is an error: + raise ValueError("Unknow action(s) for '%s' on %s: %s." + % (method, klass.__name__, ', '.join(actions))) + + return widget + + +def new(klass, **actions): + return _execute('new', actions, klass=klass) + +def set(widget, **actions): + return _execute('set', actions, widget=widget) + +def add(widget, **actions): + return _execute('add', actions, widget=widget) + + +def agent(gtk_cls): + # TODO is there some wraps equivalent for classes? + @functools.wraps(cls) + def decorator(cls): + cls.klass = gtk_cls + if hasattr(cls, 'set_actions'): + for action, function in list(cls.set_actions.items()): + if not function: + function = 'set_%s' % action + if not hasattr(gtk_cls, action): + raise ValueError( + 'agent for %s: missing set action function: %s' + % (gtk_cls.__name__, function)) + cls.set_actions[action] = function + _agent_types[gtk_cls] = cls + return cls + return decorator + + +class Agent(object): + + klass = None + set_actions = {} + + def __init__(self, method, actions, widget): + self.method = method + self.action_items = [] + for action in list(actions.keys()): + callbacks = [ + (widget, self.set_actions.get(action, 'set_%s' % action)), + (self, 'on_perform__%s' % action) ] + for obj, attr in callbacks: + try: + self.action_items.append( + ( getattr(target, name), actions.pop(action) )) + except KeyError: + pass + + if method == 'new' and widget is None: + self.widget = self.on_create() + else: + self.widget = widget + + def __call__(self): + """Performing the actions.""" + + self.on_pre() + + for func, args in self.action_items: + if args is not tuple: + args = (args,) + func(*args) + + self.on_post() + + return self.widget + + def on_create(self): + return self.klass() + + def on_pre(self): + pass + + def on_post(self): + pass + + +@agent(GObject) +class GObjectAgent(Agent): + def on_perform__connect(self, signals): + if type(signals) != dict: + raise ValueError("'connect' action arg must be a dict") + for signal in list(signals.keys()): + self.widget.connect(signal, signals.pop(signal)) + +@agent(Gtk.Widget) +class WidgetAgent(Agent): + def on_pre(self): + if self.method == 'new': + self.widget.show() + + if 'hide' in self.actions and 'show' in self.actions: + raise ValueError("can't have both 'hide' and 'show' actions") + + + def on_hide__callback(self, method, widget, value): + if value: + widget.hide() + else: + widget.show() + + + def on_show__callback(self, method, widget, value): + if value: + widget.show() + else: + widget.hide() From b04e4a55f18c87e260e4e7960a330f9fb1597d37 Mon Sep 17 00:00:00 2001 From: "J. Victor Martins" Date: Mon, 1 Dec 2014 19:17:40 -0200 Subject: [PATCH 2/6] Major refactor for python 3 and pyGI support --- ugtk.py | 338 +++++++++++++++++++++++++++++++++++ ugtk/__init__.py | 23 --- ugtk/dispatchers/__init__.py | 56 ------ ugtk/dispatchers/base.py | 77 -------- ugtk/dispatchers/default.py | 231 ------------------------ ugtk/methods.py | 61 ------- ugtk2/__init__.py | 201 +++++++++++++++------ ugtk2/methods.py | 169 ------------------ 8 files changed, 485 insertions(+), 671 deletions(-) create mode 100644 ugtk.py delete mode 100644 ugtk/__init__.py delete mode 100644 ugtk/dispatchers/__init__.py delete mode 100644 ugtk/dispatchers/base.py delete mode 100644 ugtk/dispatchers/default.py delete mode 100644 ugtk/methods.py diff --git a/ugtk.py b/ugtk.py new file mode 100644 index 0000000..2ded2af --- /dev/null +++ b/ugtk.py @@ -0,0 +1,338 @@ +# python-ugtk - a functional layer upon PyGObject +# Copyright (C) 2014, J. Victor Martins +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Contributor(s): J. Victor Martins +# + + +"""ugtk provides a functional layer upon PyGObject/GTK+.""" + + +import sys +import re +import inspect +import functools +from gi.repository import GObject, Gtk + + +_handlers_table = {} + +# +# Dispatching +# + +def _dispatch(method, actions, *, klass=None, widget=None): + """Dispatch actions over to GTK class/widget. + + Each action is to be applied in the class or a parent class in + MRO, as long as if there is an action handler available for that + class. + + """ + + if bool(klass) == bool(widget): + raise ValueError("You can't specify both klass and widget") + + if widget: + klass = type(widget) + + handlers = [] + for subclass in klass.__mro__: + try: + handlers.append(_handlers_table[subclass](method, actions)) + except KeyError: + # don't apply actions to a class that doesn't have an + # handler, but ignore in the case of subclasses + if subclass == klass: + raise ValueError("missing handler for class: %s" + % klass.__name__) + + for handler in handlers: + widget = handler(widget) + + if actions: + # Some actions were not used, currently this is an error: + raise ValueError("Unknow action(s) for method '%s' on %s: %s." + % (method, klass.__name__, ', '.join(actions))) + + return widget + + +def new(klass, **actions): + return _dispatch('new', actions, klass=klass) + +def set(widget, **actions): + return _dispatch('set', actions, widget=widget) + +def add(widget, **actions): + return _dispatch('add', actions, widget=widget) + +# +# Base Handler class and decorator +# + +def handler(gtk_cls): + """Class decorator for ugtk handler classes.""" + def decorator(cls): + cls._klass = gtk_cls + if hasattr(cls, 'setters'): + for action, function in list(cls.setters.items()): + if not function: + function = 'set_%s' % action + if not hasattr(gtk_cls, function): + raise ValueError( + 'handler: %s missing set action function: %s' + % (gtk_cls.__name__, function)) + cls._setters[action] = function + _handlers_table[gtk_cls] = cls + return cls + return decorator + + +class Handler: + + _methods = ('set', 'new') + _klass = None + _setters = {} + + def __init__(self, method, actions): + if method not in self._methods: + raise ValueError('unknown method: %s' % method) + self.method = method + self.actions = actions + + def __call__(self, widget): + if self.is_new and widget is None: + widget = self.on_create(self._klass) + callbacks = {} + for action, args in self.actions.items(): + try: + func_name = self._setters[action] + except KeyError: + try: + callbacks[action] = ( + getattr(self, 'on_action__%s' % action), [args]) + except AttributeError: + pass + else: + callbacks[action] = ( + self.__on_action_set, [func_name, args]) + self.on_pre(widget) + for action, (func, args) in callbacks.items(): + keep = func(widget, *args) + if not keep: + del self.actions[action] + self.on_post(widget) + return widget + + @property + def is_set(self): + return self.method == 'set' + + @property + def is_new(self): + return self.method == 'new' + + def __on_action_set(self, widget, func_name, args): + getattr(widget, func_name)(args) + return False + + def on_create(self, klass): + return klass() + + def on_pre(self, widget): + pass + + def on_post(self, widget): + pass + +# +# Default Handlers +# + +@handler(GObject) +class GObjectHandler(Handler): + def on_action__connect(self, widget, signals): + if type(signals) != dict: + raise ValueError("'connect' action arg must be a dict") + for signal in list(signals.keys()): + widget.connect(signal, signals.pop(signal)) + + +@handler(Gtk.StatusIcon) +class StatusIcondHandler(Handler): + pass + + +@handler(Gtk.Widget) +class WidgetHandler(Handler): + def on_pre(self, widget): + if self.is_new: + widget.show() + + if 'hide' in self.actions and 'show' in self.actions: + raise ValueError("can't have both 'hide' and 'show' actions") + + def on_action__hide(self, widget, value): + if value: + widget.hide() + else: + widget.show() + + def on_action__show(self, widget, value): + if value: + widget.show() + else: + widget.hide() + + +@handler(Gtk.Container) +class ContainerHandler(Handler): + + setters = { 'border_width': None, + 'child': 'add' } + + +@handler(Gtk.TreeView) +class TreeView(Handler): + + setters = { 'model': None, + 'headers_visible': None, + 'headers_clickable': None } + + def on_action__selection_mode(self, tview, arg): + tview.get_selection().set_mode(arg) + + def on_action__selection_connect(self, tview, arg): + if type(arg) is not dict: + raise ValueError("'connect' action arg must be a dict") + for a in arg: + tview.get_selection().connect(a, arg[a]) + + def on_action__columns(self, tview, arg): + if method == 'set': + # remove all current columns in tview... + for col in tview.get_columns(): + tview.remove_column(col) + + for col in arg: + tview.append_column(col) + + def on_action__text_column(self, tview, arg): + col = Gtk.TreeViewColumn(arg['title'], + Gtk.CellRendererText(), + text=arg['text']) + if 'position' in arg: + tview.insert_column(col, arg['position']) + else: + tview.append_column(col) + + +@handler(Gtk.Window) +class WindowHandler(Handler): + + setters = { 'title': None, + 'border': 'set_border_width' } + + def on_create(self, klass): + return klass(self.actions.pop('type', 'toplevel')) + + def on_pre(self, widget): + if self.is_new: + widget.set_default_size( + self.actions.pop('default_width', -1), + self.actions.pop('default_height', -1)) + + def on_action__resize(self, widget, value): + if type(value) != tuple: + ValueError("'resize' action argument must be a tuple (width, height)") + if self.is_set: + widget.resize(*value) + + +@handler(Gtk.Label) +class LabelHandler(Handler): + + setters = {'text': None, 'selectable': None} + + +@handler(Gtk.Entry) +class EntryHandler(Handler): + + set_actions = {'max': 'set_max_length', + 'text': ''} + + def on_create(self, klass): + return klass(self.actions.pop('max', 0)) + + +@handler(Gtk.Box) +class Box(Handler): + + setters = {'homogeneous': None, 'spacing': None} + + def on_pre(self, widget): + self.pack_args = {'expand': True, 'fill': True, 'padding': 0, 'order': 'start'} + for arg, default in list(self.pack_args.items()): + try: + self.pack_args[arg] = self.actions.pop(arg) + except KeyError: + pass + + def on_action__children(self, widget, arg): + """Callback for children action. + + 'children' argument is a list of gtk.Widget or dicts. + """ + + if self.is_set: + # remove all children from this box... + for chd in box.get_children(): + box.remove(chd) + + for child in arg: + pack_args = {} + if type(child) is tuple: + (child, pack_args) = child + for k,v in self.pack_args: + if k not in pack_args: + pack_args[k] = v + else: + pack_args.update(self.pack_args) + if not isinstance(child, Gtk.Widget): + raise ValueError("`child' action is not a Gtk.Widget") + func = getattr(widget, 'pack_%s' % pack_args['order']) + func(child, *[ pack_args[p] for p in ('expand', 'fill', 'padding') ]) + child.show() + + +@handler(Gtk.VBox) +class VBox(Handler): + pass + + +@handler(Gtk.HBox) +class HBox(Handler): + pass + + +@handler(Gtk.Button) +class Button(Handler): + + setters = { 'label': None } + + def on_create(self, klass): + return klass(self.actions.pop('label', None)) diff --git a/ugtk/__init__.py b/ugtk/__init__.py deleted file mode 100644 index db8a310..0000000 --- a/ugtk/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -## -## Copyright (C) 2010 J. Victor Martins, -## All rights reserved -## -## This program is free software: you can redistribute it and/or modify it -## under the terms of the GNU General Public License as published by the Free -## Software Foundation, either version 3 of the License, or any later version. -## -## This program 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 General Public License for -## more details. -## -## You should have received a copy of the GNU General Public License along with -## this program. If not, see . -## -## -## Contributor(s): J. Victor Martins -## - -from ugtk.methods import new, set, add - -__all__ = ['new', 'set', 'add'] diff --git a/ugtk/dispatchers/__init__.py b/ugtk/dispatchers/__init__.py deleted file mode 100644 index 21d3930..0000000 --- a/ugtk/dispatchers/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- Mode: Python; coding: utf-8 -*- - -## -## Copyright (C) 2013 J. Victor Martins -## All rights reserved -## -## This program is free software: you can redistribute it and/or modify it -## under the terms of the GNU General Public License as published by the Free -## Software Foundation, either version 3 of the License, or any later version. -## -## This program 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 General Public License for -## more details. -## -## You should have received a copy of the GNU General Public License along with -## this program. If not, see . -## -## Contributor(s): J. Victor Duarte Martins -## - -import inspect -from ugtk.dispatchers import default -from ugtk.dispatchers.base import ActionDispatcher, DISPATCHER_CLASS_ATTR - - -registered = {} - - -def register(klass, dispatcher_class): - # Can't have two dispatchers for the same class: - assert(registered.get(klass) == None) - if not issubclass(dispatcher_class, ActionDispatcher): - raise ValueError('%s must be a subclass of %s to be registered as ' - 'a ugtk action dispatcher.' - % (klass.__name__, ActionDispatcher.__name__)) - - registered[klass] = dispatcher_class - -def create_dispatcher(klass): - if klass not in registered: - return None - return registered[klass]() - - -# Initialize default dispatchers: go into the default module, look for -# all classes inherited from ActionDispatcher and register if they -# have the DISPATCHER_CLASS_ATTR atttribute ... - -for _, klass in inspect.getmembers(default, - lambda o: inspect.isclass(o) \ - and issubclass(o, ActionDispatcher)): - try: - register(getattr(klass, DISPATCHER_CLASS_ATTR), klass) - except AttributeError: - pass diff --git a/ugtk/dispatchers/base.py b/ugtk/dispatchers/base.py deleted file mode 100644 index 90f019a..0000000 --- a/ugtk/dispatchers/base.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- Mode: Python; coding: utf-8 -*- - -## -## Copyright (C) 2013 J. Victor Martins -## All rights reserved -## -## This program is free software: you can redistribute it and/or modify it -## under the terms of the GNU General Public License as published by the Free -## Software Foundation, either version 3 of the License, or any later version. -## -## This program 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 General Public License for -## more details. -## -## You should have received a copy of the GNU General Public License along with -## this program. If not, see . -## -## Contributor(s): J. Victor Duarte Martins -## - -import gtk -import gobject - - -DISPATCHER_CLASS_ATTR = '__ugtk_dispatcher_class' - - -def dispatcher_for(klass): - """Class decorator to mark a class as a ugtk action dispatcher, by - placing a speciall class attribute.""" - def class_decorator(decorated_klass): - setattr(decorated_klass, DISPATCHER_CLASS_ATTR, klass) - return decorated_klass - return class_decorator - - -class ActionDispatcher(object): - """Base class for ugtk action dispatchers.""" - - signals = [] - set_actions = {} - - def dispatch(self, method, widget, actions): - """Method for dispatching actions.""" - - self.on_start_dispatch(method, widget, actions) - self.__dispatch_set_action(widget, actions) - # Dispatch the remaining actions to callbacks, ignore if they - # don't exist ... - for action in actions.keys(): - callback_name = 'on_%s__callback' % action - if hasattr(self, callback_name): - callback = getattr(self, callback_name) - callback(method, widget, actions.pop(action)) - self.on_finalize_dispatch(method, widget, actions) - - def __dispatch_set_action(self, widget, actions): - for action in self.set_actions: - if action in actions: - method = self.set_actions[action] - if not method: - method = "set_%s" % action - getattr(widget, method)(actions.pop(action)) - - def on_start_dispatch(self, method, widget, actions): - """Called before any other operation during a dispatch.""" - pass - - def on_finalize_dispatch(self, method, widget, actions): - """Called after all other operations during a dispatch.""" - pass - - def on_create(self, klass, actions): - """Called to create an object of the class.""" - raise ValueError("creating widget for class %s is not supported" - % klass.__name__) diff --git a/ugtk/dispatchers/default.py b/ugtk/dispatchers/default.py deleted file mode 100644 index 272ac11..0000000 --- a/ugtk/dispatchers/default.py +++ /dev/null @@ -1,231 +0,0 @@ -# -*- Mode: Python; coding: utf-8 -*- - -## -## Copyright (C) 2013 J. Victor Martins -## All rights reserved -## -## This program is free software: you can redistribute it and/or modify it -## under the terms of the GNU General Public License as published by the Free -## Software Foundation, either version 3 of the License, or any later version. -## -## This program 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 General Public License for -## more details. -## -## You should have received a copy of the GNU General Public License along with -## this program. If not, see . -## -## Contributor(s): J. Victor Duarte Martins -## -""" -Default ugtk dispatchers. -""" - -import gtk -import gobject -from ugtk.dispatchers.base import ActionDispatcher, dispatcher_for - - -@dispatcher_for(gobject.GObject) -class GObject(ActionDispatcher): - - def on_connect__callback(self, method, widget, signals): - if type(signals) != dict: - raise ValueError("'connect' action arg must be a dict") - for signal in signals.keys(): - widget.connect(signal, signals.pop(signal)) - - -@dispatcher_for(gtk.Widget) -class Widget(ActionDispatcher): - - def on_start_dispatch(self, method, widget, actions): - if method == 'new': - widget.show() - - if 'hide' in actions and 'show' in actions: - raise ValueError("can't have both 'hide' and 'show' actions") - - - def on_hide__callback(self, method, widget, value): - if value: - widget.hide() - else: - widget.show() - - - def on_show__callback(self, method, widget, value): - if value: - widget.show() - else: - widget.hide() - - -@dispatcher_for(gtk.Container) -class Container(ActionDispatcher): - - set_actions = {'border_width': '', 'child': 'add'} - - -@dispatcher_for(gtk.Label) -class Label(ActionDispatcher): - - signals = ['copy_clipboard', 'move_cursor', 'populate_popup'] - set_actions = {'text': '', 'selectable': ''} - - def on_create(self, klass, actions): - widget = gtk.Label() - return widget - - -@dispatcher_for(gtk.TreeSortable) -class TreeSortable(ActionDispatcher): - - def on_sort_column_id__callback(self, method, tree, arg): - tree.set_sort_column_id(*arg) - - -@dispatcher_for(gtk.TreeView) -class TreeView(ActionDispatcher): - - set_actions = {'model': '', 'headers_visible': '', 'headers_clickable': ''} - - def on_create(self, klass, actions): - return gtk.TreeView() - - def on_selection_mode__callback(self, method, tview, arg): - tview.get_selection().set_mode(arg) - - def on_selection_connect__callback(self, method, tview, arg): - if arg.__class__ != dict: - raise ValueError("'connect' action arg must be a dict") - for a in arg: - tview.get_selection().connect(a, arg[a]) - - def on_columns__callback(self, method, tview, arg): - if method == 'set': - # remove all current columns in tview... - for col in tview.get_columns(): - tview.remove_column(col) - - for col in arg: - tview.append_column(col) - - def on_text_column__callback(self, method, tview, arg): - col = gtk.TreeViewColumn(arg['title'], - gtk.CellRendererText(), - text=arg['text']) - if 'position' in arg: - tview.insert_column(col, arg['position']) - else: - tview.append_column(col) - - -@dispatcher_for(gtk.ScrolledWindow) -class ScrolledWindow(ActionDispatcher): - - set_actions = {'hadjusment': '', 'vadjusment': ''} - - def on_create(self, klass, actions): - hadjusment = actions.pop('hadjusment', None) - vadjusment = actions.pop('vadjusment', None) - return gtk.ScrolledWindow(hadjusment, vadjusment) - - -@dispatcher_for(gtk.Box) -class Box(ActionDispatcher): - - set_actions = {'homogeneous': '', 'spacing': ''} - - def on_start_dispatch(self, method, widget, actions): - self.specs = {} - for pack_action in ('expand', 'fill', 'padding', 'order'): - try: - self.specs[pack_action] = actions.pop(pack_action) - except KeyError: - continue - - def on_children__callback(self, method, box, arg): - """Callback for children action. - - 'children' argument is a list of gtk.Widget or dicts. - """ - - if method == 'set': - # remove all children from this box... - for chd in box.get_children(): - box.remove(chd) - - for a in arg: - if a.__class__ == tuple: - (child, specs) = a - self.specs.update(specs) - elif issubclass(a.__class__, gtk.Widget): - child = a - else: - msg = ("'children' action with argument which " - "isn't a gtk.Widget or a tuple.") - raise ValueError(msg) - method = 'pack_%s' % self.specs.pop('order', 'start') - getattr(box, method)(child, **self.specs) - child.show() - - -@dispatcher_for(gtk.VBox) -class VBox(ActionDispatcher): - - def on_create(self, klass, actions): - return gtk.VBox() - - -@dispatcher_for(gtk.HBox) -class HBox(ActionDispatcher): - - def on_create(self, klass, actions): - return gtk.HBox() - - -@dispatcher_for(gtk.Button) -class Button(ActionDispatcher): - - set_actions = {'label': ''} - - def on_create(self, klass, actions): - label = actions.pop('label', None) - return gtk.Button(label) - - -@dispatcher_for(gtk.Entry) -class Entry(ActionDispatcher): - - set_actions = {'max': 'set_max_length', - 'text': ''} - - def on_create(self, klass, actions): - max = actions.pop('max', 0) - return gtk.Entry(max) - - -@dispatcher_for(gtk.Window) -class Window(ActionDispatcher): - - set_actions = {'title': '', - 'border': 'set_border_width'} - - def on_create(self, klass, actions): - return gtk.Window(actions.pop('type', 'toplevel')) - - - def on_start_dispatch(self, method, window, actions): - if method == 'new': - window.set_default_size(actions.pop('default_width', -1), - actions.pop('default_height', -1)) - - - def on_resize__callback(self, method, window, value): - if type(value) != tuple: - ValueError("'resize' action arg must be a tuple (width, height)") - - if method in ('set'): - widget.resize(*value) diff --git a/ugtk/methods.py b/ugtk/methods.py deleted file mode 100644 index c178912..0000000 --- a/ugtk/methods.py +++ /dev/null @@ -1,61 +0,0 @@ -## -## Copyright (C) 2010 J. Victor Martins, -## All rights reserved -## -## This program is free software: you can redistribute it and/or modify it -## under the terms of the GNU General Public License as published by the Free -## Software Foundation, either version 3 of the License, or any later version. -## -## This program 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 General Public License for -## more details. -## -## You should have received a copy of the GNU General Public License along with -## this program. If not, see . -## -## -## Contributor(s): J. Victor Martins -## - -import gtk -import inspect -import ugtk.dispatchers - - -def new(klass, **actions): - return _execute_method('new', klass, None, actions) - - -def set(widget, **actions): - return _execute_method('set', widget.__class__, widget, actions) - - -def add(widget, **actions): - return _execute_method('add', widget.__class__, widget, actions) - - -def _execute_method(method, klass, widget, actions): - """Dispatch actions for each subclass of widget.""" - - for subclass in inspect.getmro(klass): - dispatcher = ugtk.dispatchers.create_dispatcher(subclass) - if dispatcher == None: - if subclass == klass: - # ugtk doesn't support calling a method for this class: - raise ValueError("ugtk.%s is not supported for class %s" - % (method, klass.__name__)) - else: - # We create the object here, instead of dispatching - # creation to the dispatcher ... - if subclass == klass and method == 'new': - widget = dispatcher.on_create(klass, actions) - dispatcher.dispatch(method, widget, actions) - - if actions: - # Some action were not used, currently this is an error: - raise ValueError("Unknow action(s) for '%s' on %s: %s." - % (method, - klass.__name__, - ', '.join(actions))) - return widget diff --git a/ugtk2/__init__.py b/ugtk2/__init__.py index 1974d48..0abdf5b 100644 --- a/ugtk2/__init__.py +++ b/ugtk2/__init__.py @@ -17,67 +17,160 @@ # Contributor(s): J. Victor Martins # + """ugtk is a module providing a functional layer upon PyGObject/GTK+.""" -class Actions(object): - def __init__(self, method, actions): +import sys +import re +import inspect +import functools +from gi.repository import GObject, Gtk + + +_handlers_table = {} + + +def _dispatch(method, actions, *, klass=None, widget=None): + """Dispatch actions over to GTK class/widget. + + Each action is to be applied in the class or a parent class in + MRO, as long as if there is an action handler available for that + class. + + """ + + if bool(klass) == bool(widget): + raise ValueError("You can't specify both klass and widget") + + if widget: + klass = type(widget) + + for subclass in klass.__mro__: + try: + handler = _handlers_table[subclass](method, actions, widget) + except KeyError: + # don't apply actions to a class that doesn't have an + # agent, but ignore in the case of subclasses + if subclass == klass: + raise ValueError("missing handler for class: %s" + % klass.__name__) + else: + widget = agent() + + if actions: + # Some actions were not used, currently this is an error: + raise ValueError("Unknow action(s) for '%s' on %s: %s." + % (method, klass.__name__, ', '.join(actions))) + + return widget + + +def new(klass, **actions): + return _dispatch('new', actions, klass=klass) + +def set(widget, **actions): + return _dispatch('set', actions, widget=widget) + +def add(widget, **actions): + return _dispatch('add', actions, widget=widget) + + +def handler(gtk_cls): + """Class decorator for ugtk handler classes.""" + @functools.wraps(cls) + def decorator(cls): + cls._klass = gtk_cls + if hasattr(cls, '_setters'): + for action, function in list(cls._setters.items()): + if not function: + function = 'set_%s' % action + if not hasattr(gtk_cls, function): + raise ValueError( + 'handler: %s missing set action function: %s' + % (gtk_cls.__name__, function)) + cls._setters[action] = function + _agent_types[gtk_cls] = cls + return cls + return decorator + + +class Handler(object): + + _klass = None + + def __init__(self, method, actions, widget): self.method = method - self.actions = actions - - -class ActionHandler(object): - """Represent a ugtk action handler class.""" - - signals = [] - set_actions = {} - - def handle(self, widget, actions): - """Apply actions to the widget the method.""" - - self.on_start_dispatch(method, widget, actions) - self.__dispatch_set_action(widget, actions) - # Dispatch the remaining actions to callbacks, ignore if they - # don't exist ... - for action in actions.keys(): - callback_name = 'on_%s__callback' % action - if hasattr(self, callback_name): - callback = getattr(self, callback_name) - callback(method, widget, actions.pop(action)) - self.on_finalize_dispatch(method, widget, actions) - - def __dispatch_set_action(self, widget, actions): - for action in self.set_actions: - if action in actions: - method = self.set_actions[action] - if not method: - method = "set_%s" % action - getattr(widget, method)(actions.pop(action)) - - def on_start_dispatch(self, method, widget, actions): - """Called before any other operation during a dispatch.""" + self.action_items = [] + for action in list(actions.keys()): + for obj, attr in [ + ( widget, self._setters.get(action, 'set_%s' % action) ), + ( self, 'on_action__%s' % action ) ]: + try: + self.action_items.append( + ( getattr(target, name), actions.pop(action) )) + except KeyError: + pass + if method == 'new' and widget is None: + self.widget = self.on_create() + else: + self.widget = widget + + def __call__(self): + self.on_pre() + for func, args in self.action_items: + if args is not tuple: + args = (args,) + func(*args) + self.on_post() + return self.widget + + def on_create(self): + return self.klass() + + def on_pre(self): pass - def on_finalize_dispatch(self, method, widget, actions): - """Called after all other operations during a dispatch.""" + def on_post(self): pass - def on_create(self, klass, actions): - """Called to create an object of the class.""" - raise ValueError("creating widget for class %s is not supported" - % klass.__name__) - - -_dispatchers = {} -def register(gtk_cls, dispatcher_cls): - """Register a new dispatcher for the selected GTK class.""" - try: - registered[gtk_cls] - except KeyError: - if not issubclass(dispatcher_cls, ActionDispatcher): - raise ValueError('%s must be a subclass of %s to be registered as ' - 'a ugtk action dispatcher.' - % (klass.__name__, ActionDispatcher.__name__)) +@handler(GObject) +class GObjectHandler(Handler): + def on_action__connect(self, signals): + if type(signals) != dict: + raise ValueError("'connect' action arg must be a dict") + for signal, in list(signals.items()): + self.widget.connect(signal, signals.pop(signal)) + +@handler(Gtk.Widget) +class WidgetHandler(Agent): + def on_pre(self): + if self.method == 'new': + self.widget.show() + + if 'hide' in self.actions and 'show' in self.actions: + raise ValueError("can't have both 'hide' and 'show' actions") + + def on_action__hide(self, value): + if value: + widget.hide() + else: + widget.show() + + def on_action__show(self, value): + if value: + widget.show() + else: + widget.hide() + +@handler(Gtk.Container) +class ContainerHandler(Handler): + + _setters = {'border_width': None, 'child': 'add'} + +@handler(Gtk.Label) +class LabelHandler(Hanlder): + + _setters = {'text': None, 'selectable': None} - registered[klass] = dispatcher_class diff --git a/ugtk2/methods.py b/ugtk2/methods.py index 22a7473..139597f 100644 --- a/ugtk2/methods.py +++ b/ugtk2/methods.py @@ -1,171 +1,2 @@ -# python-ugtk - a functional layer upon PyGObject -# Copyright (C) 2014, J. Victor Martins -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Contributor(s): J. Victor Martins -# -import sys -import inspect -import functools -from gi.repository import GObject, Gtk - - -_agent_types = {} - - -def _execute(method, actions, *, klass=None, widget=None): - """Execute actions over a GTK class/widget. - - Each action is to be applied in the class or a parent class in - MRO, as long as if there is an action handler available for that - class. - - """ - - if bool(klass) == bool(widget): - raise ValueError("You can't specify both klass and widget") - - if widget: - klass = widget.__class__ - - for subclass in klass.__mro__: - try: - agent = _agent_types[subclass](method, actions, widget) - except KeyError: - # don't apply actions to a class that doesn't have an - # agent, but ignore in the case of subclasses - if subclass == klass: - raise ValueError("missing handler for class: %s" - % klass.__name__) - else: - widget = agent() - - if actions: - # Some action were not used, currently this is an error: - raise ValueError("Unknow action(s) for '%s' on %s: %s." - % (method, klass.__name__, ', '.join(actions))) - - return widget - - -def new(klass, **actions): - return _execute('new', actions, klass=klass) - -def set(widget, **actions): - return _execute('set', actions, widget=widget) - -def add(widget, **actions): - return _execute('add', actions, widget=widget) - - -def agent(gtk_cls): - # TODO is there some wraps equivalent for classes? - @functools.wraps(cls) - def decorator(cls): - cls.klass = gtk_cls - if hasattr(cls, 'set_actions'): - for action, function in list(cls.set_actions.items()): - if not function: - function = 'set_%s' % action - if not hasattr(gtk_cls, action): - raise ValueError( - 'agent for %s: missing set action function: %s' - % (gtk_cls.__name__, function)) - cls.set_actions[action] = function - _agent_types[gtk_cls] = cls - return cls - return decorator - - -class Agent(object): - - klass = None - set_actions = {} - - def __init__(self, method, actions, widget): - self.method = method - self.action_items = [] - for action in list(actions.keys()): - callbacks = [ - (widget, self.set_actions.get(action, 'set_%s' % action)), - (self, 'on_perform__%s' % action) ] - for obj, attr in callbacks: - try: - self.action_items.append( - ( getattr(target, name), actions.pop(action) )) - except KeyError: - pass - - if method == 'new' and widget is None: - self.widget = self.on_create() - else: - self.widget = widget - - def __call__(self): - """Performing the actions.""" - - self.on_pre() - - for func, args in self.action_items: - if args is not tuple: - args = (args,) - func(*args) - - self.on_post() - - return self.widget - - def on_create(self): - return self.klass() - - def on_pre(self): - pass - - def on_post(self): - pass - - -@agent(GObject) -class GObjectAgent(Agent): - def on_perform__connect(self, signals): - if type(signals) != dict: - raise ValueError("'connect' action arg must be a dict") - for signal in list(signals.keys()): - self.widget.connect(signal, signals.pop(signal)) - -@agent(Gtk.Widget) -class WidgetAgent(Agent): - def on_pre(self): - if self.method == 'new': - self.widget.show() - - if 'hide' in self.actions and 'show' in self.actions: - raise ValueError("can't have both 'hide' and 'show' actions") - - - def on_hide__callback(self, method, widget, value): - if value: - widget.hide() - else: - widget.show() - - - def on_show__callback(self, method, widget, value): - if value: - widget.show() - else: - widget.hide() From 72d31912f61f5be23f5a0b420835bcd278bcbc5d Mon Sep 17 00:00:00 2001 From: "J. Victor Martins" Date: Mon, 1 Dec 2014 19:18:56 -0200 Subject: [PATCH 3/6] Remove old refactor --- ugtk2/__init__.py | 176 ---------------------------------------------- ugtk2/methods.py | 2 - 2 files changed, 178 deletions(-) delete mode 100644 ugtk2/__init__.py delete mode 100644 ugtk2/methods.py diff --git a/ugtk2/__init__.py b/ugtk2/__init__.py deleted file mode 100644 index 0abdf5b..0000000 --- a/ugtk2/__init__.py +++ /dev/null @@ -1,176 +0,0 @@ -# python-ugtk - a functional layer upon PyGObject -# Copyright (C) 2014, J. Victor Martins -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Contributor(s): J. Victor Martins -# - - -"""ugtk is a module providing a functional layer upon PyGObject/GTK+.""" - - -import sys -import re -import inspect -import functools -from gi.repository import GObject, Gtk - - -_handlers_table = {} - - -def _dispatch(method, actions, *, klass=None, widget=None): - """Dispatch actions over to GTK class/widget. - - Each action is to be applied in the class or a parent class in - MRO, as long as if there is an action handler available for that - class. - - """ - - if bool(klass) == bool(widget): - raise ValueError("You can't specify both klass and widget") - - if widget: - klass = type(widget) - - for subclass in klass.__mro__: - try: - handler = _handlers_table[subclass](method, actions, widget) - except KeyError: - # don't apply actions to a class that doesn't have an - # agent, but ignore in the case of subclasses - if subclass == klass: - raise ValueError("missing handler for class: %s" - % klass.__name__) - else: - widget = agent() - - if actions: - # Some actions were not used, currently this is an error: - raise ValueError("Unknow action(s) for '%s' on %s: %s." - % (method, klass.__name__, ', '.join(actions))) - - return widget - - -def new(klass, **actions): - return _dispatch('new', actions, klass=klass) - -def set(widget, **actions): - return _dispatch('set', actions, widget=widget) - -def add(widget, **actions): - return _dispatch('add', actions, widget=widget) - - -def handler(gtk_cls): - """Class decorator for ugtk handler classes.""" - @functools.wraps(cls) - def decorator(cls): - cls._klass = gtk_cls - if hasattr(cls, '_setters'): - for action, function in list(cls._setters.items()): - if not function: - function = 'set_%s' % action - if not hasattr(gtk_cls, function): - raise ValueError( - 'handler: %s missing set action function: %s' - % (gtk_cls.__name__, function)) - cls._setters[action] = function - _agent_types[gtk_cls] = cls - return cls - return decorator - - -class Handler(object): - - _klass = None - - def __init__(self, method, actions, widget): - self.method = method - self.action_items = [] - for action in list(actions.keys()): - for obj, attr in [ - ( widget, self._setters.get(action, 'set_%s' % action) ), - ( self, 'on_action__%s' % action ) ]: - try: - self.action_items.append( - ( getattr(target, name), actions.pop(action) )) - except KeyError: - pass - if method == 'new' and widget is None: - self.widget = self.on_create() - else: - self.widget = widget - - def __call__(self): - self.on_pre() - for func, args in self.action_items: - if args is not tuple: - args = (args,) - func(*args) - self.on_post() - return self.widget - - def on_create(self): - return self.klass() - - def on_pre(self): - pass - - def on_post(self): - pass - - -@handler(GObject) -class GObjectHandler(Handler): - def on_action__connect(self, signals): - if type(signals) != dict: - raise ValueError("'connect' action arg must be a dict") - for signal, in list(signals.items()): - self.widget.connect(signal, signals.pop(signal)) - -@handler(Gtk.Widget) -class WidgetHandler(Agent): - def on_pre(self): - if self.method == 'new': - self.widget.show() - - if 'hide' in self.actions and 'show' in self.actions: - raise ValueError("can't have both 'hide' and 'show' actions") - - def on_action__hide(self, value): - if value: - widget.hide() - else: - widget.show() - - def on_action__show(self, value): - if value: - widget.show() - else: - widget.hide() - -@handler(Gtk.Container) -class ContainerHandler(Handler): - - _setters = {'border_width': None, 'child': 'add'} - -@handler(Gtk.Label) -class LabelHandler(Hanlder): - - _setters = {'text': None, 'selectable': None} - diff --git a/ugtk2/methods.py b/ugtk2/methods.py deleted file mode 100644 index 139597f..0000000 --- a/ugtk2/methods.py +++ /dev/null @@ -1,2 +0,0 @@ - - From 0017c018d88a7689693dac2e66f55a198d8e4be9 Mon Sep 17 00:00:00 2001 From: "J. Victor Martins" Date: Mon, 1 Dec 2014 19:59:32 -0200 Subject: [PATCH 4/6] Fix typo --- README | 141 +++++++++++++++++++++++++++----------------------------- ugtk.py | 6 +-- 2 files changed, 71 insertions(+), 76 deletions(-) diff --git a/README b/README index ce8cd69..52f5e1f 100644 --- a/README +++ b/README @@ -1,92 +1,87 @@ -python-ugtk is a functional layer upon pygtk. The purpose is provide -a more maintanable, clean and organized code for GUI. + Introduction +============== -The pyugtk layer is based on the concept of actions. An ugtk action -is a series of gtk methods called on a widget. And each widget -determines a set of actions that it can apply. +``python-ugtk`` is a functional layer upon `PyGobject +`_. The +purpose of this is to provide a different perspective to GUI +programming towards more maintanable, clean and organized code. -To apply actions you must use a ugtk method. There are two of them: +The ugtk layer is based on the concept of actions. An ugtk action is +a series of directives that can be performed over gtk widgets, usually +by calling their methods, setting their properties or hooking +callbacks to their signals and so on. Each widget class determines a +set of actions that it support, so the set of availables actions for +each widget is based on its inheritance. + +To apply actions you must use a ugtk method, by specifying a kwargs +for each action (where the key is the action name, the value the +action argument). There are currently two of them: ugtk.new(gtkclass, **actions) ugtk.set(gtkwidget, **actions) + ugtk.add(gtkwidget, **actions) + The first is used when you have no widget yet and you want it created. The second serves only the purpose of applying actions. -To specify action to methods you use the kwargs syntax, where the key -is the action name and the value is the action parameter (which can be -any python object). +Actions change their behaviour depending on the ugtk method used to +perform them. For example, specifiying ``children=list`` action on a +`Gtk.HBox' object using ugtk.add() is different from using ugtk.set() +(the later will reset the box childrens, while the former will always +append). -Some actions are available for both methods (e.g. text=str is provided -both for ugtk.new and ugtk.add on gtk.Label -- which actually calls -gtk.Label.set_text(str)). Others are only available for each method. -Some actions for both methods may behave differently depending on -which method they are being applied from. +Some actions are available for both methods (e.g. ``text=str`` is +provided both for ugtk.new() and ugtk.add() with ``Gtk.Label`` -- +which actually calls gtk.Label.set_text(str)). Others are only +available for each method. Some actions for both methods may behave +differently depending on which method they are being applied from. To better organize actions we can group them by functionality: -- set actions: actions that in practice just call - widget.set_ACTIONNAME on methods passing their value as parameter. +set actions + Actions that in will eventually call ``widget.set_ACTIONNAME`` or + similar on methods passing their arguments. -- wrapper actions: actions that generate a series of methods call on - the widget. E.g. gtkset(hbox, child_tight=[label, entry]) will call - gtk.Box.pack_start(0, label) and gtk.Box.pack_start(0, entry) +wrapper actions + Actions that generate a series of methods calls on the widget. + E.g. ``gtkset(hbox, child_tight=[label, entry])`` will call + ``gtk.Box.pack_start(0, label)`` and ``gtk.Box.pack_start(0, + entry)``. To provide a functional paradigm each ugtk method returns the widget being manipulated, so users can do things like: ->>> win = ugtk.new(gtk.Window, ->>> title="pyugtk 'Hello, World'", ->>> child=ugtk.new(gtk.Label, ->>> text="Hello, World!")) - - -How to add your own dispatchers -================================ - -In case you've a class you would like to apply ugtk actions first you -need to create inherit from ugtk.dispatchers.base.ActionDispatcher. -Then you need to register it so ugtk will call it to dispatch actions: - ->>> import ugtk ->>> import ugtk.dispatcher ->>> ->>> class MyDispatcher(ugtk.dispatchers.base.ActionDispatcher): -... pass # ... implement the dispatcher methods and callbacks -... ->>> class MyClass: -... pass # your new widget -... ->>> ugtk.dispatcher.register(MyClass, MyDispatcher) ->>> - -Now you can: - ->>> myobj = ugtk.new(MyClass, action='My action parameter') ->>> ugtk.set(myobj, another_action='Another action parameter') ->>> - -If MyClass inherits from gtk classes (in fact any other registered -class) ugtk you will be able to use actions from those classes aswell. - ->>> import gtk ->>> class MyWindow(gtk.Window): -... pass # you class implemetation here... -... ->>> # register MyWindow as a ugtk dispatcher... ->>> ugtk.new(MyWindow, -... action='You action parameter', -... title="My Window Title!") -... ->>> - -gtkadd -======= - -Used to create the widget. Actions - -gtk.Label ---------- - - +:: + win = ugtk.new( + Gtk.Window, + title="ugtk 'Hello, World' window", + connect={'delete-event': Gtk.main_quit } + ) + + ugtk.set( + win, + child=ugtk.new( + Gtk.VBox, + children=[ + ugtk.new( + Gtk.Label, + text="Hello, World!" + ), + ( + ugtk.new(Gtk.Button, + label="Close", + connect={ + 'clicked': + lambda widget: win.close() + }), + { 'expand': False } + ), + ], + expand=True + ) + ) + + Gtk.main() + del win diff --git a/ugtk.py b/ugtk.py index 2ded2af..a6cefd4 100644 --- a/ugtk.py +++ b/ugtk.py @@ -65,7 +65,7 @@ def _dispatch(method, actions, *, klass=None, widget=None): if actions: # Some actions were not used, currently this is an error: - raise ValueError("Unknow action(s) for method '%s' on %s: %s." + raise ValueError("unknown action(s) for method '%s' on %s: %s." % (method, klass.__name__, ', '.join(actions))) return widget @@ -163,7 +163,7 @@ def on_post(self, widget): # Default Handlers # -@handler(GObject) +@handler(GObject.Object) class GObjectHandler(Handler): def on_action__connect(self, widget, signals): if type(signals) != dict: @@ -307,7 +307,7 @@ def on_action__children(self, widget, arg): pack_args = {} if type(child) is tuple: (child, pack_args) = child - for k,v in self.pack_args: + for k,v in self.pack_args.items(): if k not in pack_args: pack_args[k] = v else: From 83a538e2d26dc195f7ec4def7bfe0f3cc93dfb29 Mon Sep 17 00:00:00 2001 From: "J. Victor Martins" Date: Mon, 1 Dec 2014 20:00:49 -0200 Subject: [PATCH 5/6] Update README --- README => README.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.rst (100%) diff --git a/README b/README.rst similarity index 100% rename from README rename to README.rst From e0b08605f666ba04a2caa212946b749767a9f7cb Mon Sep 17 00:00:00 2001 From: "J. Victor Martins" Date: Mon, 1 Dec 2014 20:02:46 -0200 Subject: [PATCH 6/6] Fix README --- README.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 52f5e1f..c8bb5b1 100644 --- a/README.rst +++ b/README.rst @@ -15,7 +15,7 @@ each widget is based on its inheritance. To apply actions you must use a ugtk method, by specifying a kwargs for each action (where the key is the action name, the value the -action argument). There are currently two of them: +action argument). There are currently two of them:: ugtk.new(gtkclass, **actions) @@ -28,7 +28,7 @@ The second serves only the purpose of applying actions. Actions change their behaviour depending on the ugtk method used to perform them. For example, specifiying ``children=list`` action on a -`Gtk.HBox' object using ugtk.add() is different from using ugtk.set() +``Gtk.HBox`` object using ugtk.add() is different from using ugtk.set() (the later will reset the box childrens, while the former will always append). @@ -51,9 +51,8 @@ wrapper actions entry)``. To provide a functional paradigm each ugtk method returns the widget -being manipulated, so users can do things like: +being manipulated, so users can do things like:: -:: win = ugtk.new( Gtk.Window, title="ugtk 'Hello, World' window",