Bug 91423

Summary: Python class to make this code simpler
Product: [Retired] Red Hat Linux Reporter: Shannon -jj Behrens <jjinux>
Component: redhat-config-servicesAssignee: Daniel Walsh <dwalsh>
Status: CLOSED NOTABUG QA Contact:
Severity: medium Docs Contact:
Priority: low    
Version: 9CC: behdad, mitr
Target Milestone: ---Keywords: FutureFeature
Target Release: ---   
Hardware: All   
OS: Linux   
URL: http://ironorchid.com/jjinux/articles/pytemp-0.1.tar.gz
Whiteboard:
Fixed In Version: Doc Type: Enhancement
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2004-09-13 15:11:14 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Shannon -jj Behrens 2003-05-22 14:53:20 UTC
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 14:54:41 UTC
"""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 14:56:28 UTC
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 14:54:10 UTC
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 14:55:28 UTC
"""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 14:55:56 UTC
"""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 15:02:48 UTC
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 15:50:22 UTC
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 15:59:04 UTC
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 07:21:18 UTC
Shannon, 
 Please join the fedora-config-list 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 22:26:01 UTC
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.