[pygtk] Glade XML wrapper and easy callbacks
Sridhar R
sridharinfinity at gmail.com
Sun Jul 25 16:23:08 WST 2004
Doug Quale <quale1 at charter.net> wrote:
> Sridhar R <sridharinfinity at gmail.com> writes:
>
> > I have made an utility class for making the job of using glade XML
> > file much easier. Can this be added to pygtk?
> >
> > Main class:
> > http://cs.annauniv.edu/~rsridhar/pub/python/snippets/glade/gwidget.py
> >
> > Example (not standalone, pulled from source tree):
> > http://cs.annauniv.edu/~rsridhar/pub/python/snippets/glade/gwidget-example.py
> >
> > Thoughts?
>
> About class GWidget itself, I have a few thoughts. The __init__()
> method lets the caller optionally turn off autoconnection of signals.
> This does no harm, but are there cases in which this is useful? I
> thought about this but decided that if you derive from a glade
> controller class you do it because you expect the on_widget__signal()
> methods to be connected as signal handlers.
It will be useful if the some handlers need to be called before the
on_widget__signal methods.
super(......
connect_my_custom_first_handlers()
self.autoconnect_signals()
>
> Specifying the glade file and toplevel widget name as class attributes
> is inflexible. It's also hard to overide in subclasses since you have
> to completely override the __init__() method if you want to specify
> the widget name dynamically. I think the glade file and toplevel
> widget name should be parameters to __init__().
To override that create similar Class Variables in derived classes
too. I used class variables purposedly. For each GWidget class, the
GLADE_FILE attribute is supposed to be constant for *all* instances,
bcoz the methods are written assuming that glade file, so the glade
file can't be changed for different instances as the methods remain
the same.
> For the signal handler method names I prefer to use
> after_widget__signal() instead of on_widget__signal__(). This is just
> a matter of taste. Using 'after_' has the disadvantage of taking
> another prefix in addition to 'on_', but it is more explicit. One
> tiny additional advantage that we don't often think of is that the
> on/after prefixes are easier to read aloud than the silent trailing
> double underscore.
That's probably right. Using 'after_' is more readable than suffix '__'
>
> Making the widgets available as attributes is a good idea. The
> Pythonic way to do this is to set the attribute when it is first
> accessed. Subsequent accesses won't trigger __getattr__() so you
> don't need a cache:
>
> def __getattr__(self, attr):
> new_widget = self._gladexml.get_widget(attr)
> if new_widget is None:
> raise AttributeError, 'Widget %r not found' % attr
> setattr(self, attr, new_widget)
> return new_widget
>
> Instead of making the widgets available as attributes, I use
> __getitem__() to allow dictionary access. This again is a matter of
> taste. Dictionary syntax is uglier than attribute syntax but it
> avoids the chance of stepping on a name that you want to use for
> another purpose.
I feel dictionary acess is approriate. I though self.entry1 is much
easier than self['entry']
>
> The real reason I chose dictionary syntax over attribute syntax is
> that I use the widget names as attributes for another purpose. The
> Python 2.2+ data descriptor facility allows using descriptors to
> provide attribute syntax to get and set widget values.
>
> class widget(object):
> """A Python data descriptor that gets and sets values from widgets.
>
> Use inside a class that derives from Gwidget to allow getting
> and setting widget values as attribute values. This works
> only for widget types that have a reasonable notion of value
> like gtk.Entry, gtk.SpinButton, gtk.Combo, gtk.ToggleButton,
> gtk.Label and gtk.TextView. Widgets that don't have an
> obvious value like gtk.TreeView are not supported. (Generally
> tree views will be given their own controllers or
> subcontrollers.)
> """
>
> def __init__(self, widget_name):
> self.widget_name = widget_name
>
> def __get__(self, obj, objtype):
> widget = obj[self.widget_name]
>
> if isinstance(widget, gtk.Label):
> return widget.get_label()
> elif isinstance(widget, gtk.SpinButton):
> return widget.get_value()
> elif isinstance(widget, gtk.Entry):
> return widget.get_text()
> elif isinstance(widget, gtk.Combo):
> return widget.get_entry().get_text()
> elif isinstance(widget, gtk.ToggleButton):
> if widget.get_inconsistent():
> return None
> else:
> return widget.get_active()
> elif isinstance(widget, gtk.TextView):
> buf = widget.get_buffer()
> start, end = buf.get_bounds()
> return buf.get_text(start, end, include_hidden_chars=True)
> else:
> raise ValueError, 'Unrecognized widget type %r' % (type(widget),)
>
> def __set__(self, obj, value):
> widget = obj[self.widget_name]
>
> if isinstance(widget, gtk.Label):
> widget.set_label(str(value))
> elif isinstance(widget, gtk.SpinButton):
> widget.set_value(value)
> elif isinstance(widget, gtk.Entry):
> widget.set_text(str(value))
> elif isinstance(widget, gtk.Combo):
> widget.get_entry().set_text(value)
> elif isinstance(widget, gtk.ToggleButton):
> widget.set_active(value)
> widget.set_inconsistent(value is None)
> elif isinstance(widget, gtk.TextView):
> buffer = widget.get_buffer()
> buffer.set_text(value)
> else:
> raise ValueError, 'Unrecognized widget type %r' % (type(widget),)
Introspection can be used to remove those if..else part.
>
> To use this, you put something like this in your class:
>
> class MyController(GWidget):
>
> name = widget('name') # name entry
> age = widget('age') # age spinbutton
>
> ...
>
> def somemethod(self, ...):
>
> # Here self.name will return the value in the name entry widget
> # and self.name = 'somebody' will set the value in the name entry
>
> _______________________________________________
> pygtk mailing list pygtk at daa.com.au
> http://www.daa.com.au/mailman/listinfo/pygtk
> Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/
>
--
Sridhar - http://www.cs.annauniv.edu/~rsridhar
Blog: http://www.livejournal.com/users/sridharinfinity
More information about the pygtk
mailing list