Bug 91423 - Python class to make this code simpler
Python class to make this code simpler
Status: CLOSED NOTABUG
Product: Red Hat Linux
Classification: Retired
Component: redhat-config-services (Show other bugs)
9
All Linux
low Severity medium
: ---
: ---
Assigned To: Daniel Walsh
http://ironorchid.com/jjinux/articles...
: FutureFeature
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 2003-05-22 10:53 EDT by Shannon -jj Behrens
Modified: 2014-01-21 17:48 EST (History)
2 users (show)

See Also:
Fixed In Version:
Doc Type: Enhancement
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 2004-09-13 11:11:14 EDT
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)

  None (edit)
Description Shannon -jj Behrens 2003-05-22 10:53:20 EDT
Description of problem:

I was using Red Hat's code (specifically redhat-config-services) to learn how to
use Glade, libglade, and Python to code GNOME GUI's.  Although I was unfamiliar
with libglade, I consider myself to be a Python expert.  I wrote a simple class
that can make some of the code in python-config-services unnecessary.  I thought
I might share this code with you in the hopes that it might be useful.

Version-Release number of selected component (if applicable): 0.8.4

How reproducible: Look at /usr/share/redhat-config-services/serviceconf.py

Steps to Reproduce:

Code such as:

        self.xml.signal_autoconnect(
            { "on_mainwin_delete_event" : self.quit,
              "on_mainwin_hide" : self.quit,
              "on_mnuRescan_activate" : self.on_mnuRescan_activate,
              "on_mnuSave_activate" : self.on_mnuSave_clicked,
              "on_mnuRevert_activate" : self.on_mnuRevert_clicked,
              "on_mnuExit_activate" : self.quit,
              "on_mnuStart_activate" : self.on_btnStart_clicked,
              "on_mnuStop_activate" : self.on_btnStop_clicked,
              "on_mnuRestart_activate" : self.on_btnRestart_clicked,
              "on_pmnStart_activate" : self.on_btnStart_clicked,
              "on_pmnStop_activate" : self.on_btnStop_clicked,
              "on_pmnRestart_activate" : self.on_btnRestart_clicked,
              "on_btnStart_clicked" : self.on_btnStart_clicked,
              "on_btnRestart_clicked" : self.on_btnRestart_clicked,
              "on_btnStop_clicked" : self.on_btnStop_clicked,
              "on_mnuAbout_activate" : self.on_mnuAbout_activate,
              "on_mnuManual_activate" : self.on_mnuManual_activate,
              "on_optRL3_activate" : self.on_optRL3_activate,
              "on_optRL4_activate" : self.on_optRL4_activate,
              "on_optRL5_activate" : self.on_optRL5_activate ,
              "on_pmnStart_activate" : self.on_btnStart_clicked,
              "on_pmnStop_activate" : self.on_btnStop_clicked,
              "on_pmnRestart_activate" : self.on_btnRestart_clicked } )

and

        self.mnuRescan = self.xml.get_widget("mnuRescan")
        self.mnuSave = self.xml.get_widget("mnuSave")
        self.mnuRevert = self.xml.get_widget("mnuRevert")
        self.mnuExit = self.xml.get_widget("mnuExit")
        self.mnuAbout = self.xml.get_widget("mnuAbout")
        self.mnuStart = self.xml.get_widget("mnuStart")
        self.mnuStop = self.xml.get_widget("mnuStop")
        self.mnuRestart = self.xml.get_widget("mnuRestart")
        self.optRL3 = self.xml.get_widget("optRL3")
        self.optRL4 = self.xml.get_widget("optRL4")
        self.optRL5 = self.xml.get_widget("optRL5")

can completely be handled by my short super-class.
    
Actual results: Code still needed.

Expected results: Code unnecessary.

Additional info:

I will attach my short class to this bug.  There's a very simple application
based on my simple class at
<http://ironorchid.com/jjinux/articles/pytemp-0.1.tar.gz>.
Comment 1 Shannon -jj Behrens 2003-05-22 10:54:41 EDT
"""This module contains the LibGladeApplication class."""

import signal
import gtk
from gtk import glade


class LibGladeApplication:

    """This is the base class for applications that use Glade.
    
    The following attributes are used:

    xml - This is an instance of glade.XML which encapsulates the Glade GUI.

    """

    def __init__(self, gladeFile):
        """Setup the appropriate signal handlers and call setHandlers.

        This must be called, but only after the subclass initializes the 
        xml attribute.
        
        """
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        self.xml = glade.XML(gladeFile)
        self.setHandlers()

    def setHandlers(self):
        """Automatically autoconnect all of the handlers.
        
        Any methods (even in subclasses) that start with "on_" will be treated
        as a handler that is automatically connected to by
        xml.signal_autoconnect.

        """
        handlers = {}
        for i in dir(self):
            if i.startswith("on_"):
                handlers[i] = getattr(self, i)
        self.xml.signal_autoconnect(handlers)

    def __getattr__(self, name):
        """If self doesn't have the attribute, check self.xml."""
        obj = self.xml.get_widget(name)
        if obj:
            return obj
        else:
            raise AttributeError("%s instance has no attribute '%s'" % 
                (self.__class__.__name__, name))

    def on_quit_activate(self, *args):
        """Ignore args and call gtk.mainquit()."""
        gtk.mainquit()
Comment 2 Shannon -jj Behrens 2003-05-22 10:56:28 EDT
How embarrassing--there was a stale comment in the __init__ method.  It should
be updated to:

    def __init__(self, gladeFile):
        """Setup the appropriate signal handlers and call setHandlers."""
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        self.xml = glade.XML(gladeFile)
        self.setHandlers()

Sorry!
Comment 3 Shannon -jj Behrens 2003-05-28 10:54:10 EDT
In the off chance that you guys actually are interested in this code, I've also
written a wrapper class that makes attribute references for GtkObjects more
Pythonic:

    widget.set_text(widget.get_text() + "foo")

becomes:

    widget.text = widget.text + "foo"

The LibGladeApplication now automatically wraps GtkObjects in a new 
GtkAttributesFacade instance when returning them.  I'll include the code in the
next two posts.


Comment 4 Shannon -jj Behrens 2003-05-28 10:55:28 EDT
"""This module contains the GtkAttributesFacade class."""


class GtkAttributesFacade:

    """Wrap a GTK instance to simplify the way attributes are referenced.

    Given a GTK instance i, make it possible for any functions i.get_foo() and
    i.set_foo(value) to be accessed via i.foo like a normal attribute.

    The following attributes are used:

    instance - This is the GTK instance that is being wrapped.

    """

    def __init__(self, instance):
        """Accept the instance."""
        self.__dict__["instance"] = instance

    def __setattr__(self, name, value):
        """Simplify the way attributes are referenced.

        When trying to do self.foo = something, if there is a
        self.instance.set_foo() method, use it.  Otherwise, just set the
        attribute in self.

        Return value so that chaining is possible.
        
        """
        setter = "set_" + name
        if hasattr(self.instance, setter):
            apply(getattr(self.instance, setter), [value])
        else:
            self.__dict__[name] = value
        return value

    def __getattr__(self, name):
        """Simplify the way attributes are referenced.

        Remember that this method is called after a failed lookup in self.  Try
        looking for self.instance.foo.  Next, try looking for a
        self.instance.get_foo() method, and call it if it exists.  Otherwise,
        raise an exception.
        
        """
        if hasattr(self.instance, name):
            return getattr(self.instance, name)
        getter = "get_" + name
        if hasattr(self.instance, getter):
            return apply(getattr(self.instance, getter))
        raise AttributeError(
            "%s instance has no attribute '%s' " % 
                (self.instance.__class__.__name__, name))
Comment 5 Shannon -jj Behrens 2003-05-28 10:55:56 EDT
"""This module contains the LibGladeApplication class."""

import signal
import gtk
from gtk import glade

from GtkAttributesFacade import GtkAttributesFacade 


class LibGladeApplication:

    """This is the base class for applications that use Glade.
    
    The following attributes are used:

    xml - This is an instance of glade.XML which encapsulates the Glade GUI.

    """

    def __init__(self, gladeFile):
        """Setup the appropriate signal handlers and call setHandlers."""
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        self.xml = glade.XML(gladeFile)
        self.setHandlers()

    def setHandlers(self):
        """Automatically autoconnect all of the handlers.
        
        Any methods (even in subclasses) that start with "on_" will be treated
        as a handler that is automatically connected to by
        xml.signal_autoconnect.

        """
        handlers = {}
        for i in dir(self):
            if i.startswith("on_"):
                handlers[i] = getattr(self, i)
        self.xml.signal_autoconnect(handlers)

    def __getattr__(self, name):
        """If self doesn't have the attribute, check self.xml.

        If self.xml does have the attribute, wrap it in a GtkAttributesFacade 
        instance, cache it in self, and return it.
        
        """
        obj = self.xml.get_widget(name)
        if obj:
            obj = GtkAttributesFacade(obj) 
            setattr(self, name, obj)
            return obj
        raise AttributeError("%s instance has no attribute '%s'" % 
            (self.__class__.__name__, name))

    def on_quit_activate(self, *args):
        """Ignore args and call gtk.mainquit()."""
        gtk.mainquit()
Comment 6 Shannon -jj Behrens 2003-05-28 11:02:48 EDT
I apologize for "spamming" you guys with this code.  I was just trying to be
helpful.  If you guys are actually interested, drop me an email.  I'll go away
now. ;)
Comment 7 Daniel Walsh 2003-05-28 11:50:22 EDT
Haven't had a chance to look at/incorporate your stuff, but it looks
interesting. I would like to encourage you to continue your effort.  
That is what makes Open Source great.

Dan
Comment 8 Daniel Walsh 2003-05-28 11:59:04 EDT
Your LibGladeApplication Class will only work in the case where the
on_XXX match on on_XXX method call.  So some of redhat-config-services would
need to be changed.

Have you rewritten redhat-config-services to use your patch(s).  If so please
submit the changes and I will try it out.

Dan
Comment 9 Seth Vidal 2003-11-19 02:21:18 EST
Shannon, 
 Please join the fedora-config-list@redhat.com mailing list. There are
folks there working on the redhat-config-* tools and your
contributions would be encouraged.

http://www.redhat.com/mailman/listinfo/fedora-config-list

Thanks!
Comment 10 Shannon -jj Behrens 2003-11-19 17:26:01 EST
Hmm, I'm trying like crazy to join that mailing list :-/  I've
subscribed and confirmed twice, but I never get the final confirmation
notice :-/  Maybe I should get a non-Yahoo address.

Note You need to log in before you can comment on or make changes to this bug.