pygtk gtk.Builder.connect_signals en varios objetos?

I am updating some code from using libglade to GtkBuilder, which is supposed to be the way of the future.

With, you could call glade_xml.signal_autoconnect(...) repeatedly to connect signals onto objects of different classes corresponding to different windows in the program. However Builder.connect_signals seems to work only once, and (therefore) to give warnings about any handlers that aren't defined in the first class that's passed in.

I realize I can connect them manually but this seems a bit laborious. (Or for that matter I could use some getattr hackery to let it connect them through a proxy to all the objects...)

Is it a bug there's no function to hook up handlers across multiple objects? Or am I missing something?

Someone else has a similar problem which I assume means this can't be done.

preguntado el 09 de enero de 11 a las 05:01

4 Respuestas

Here's what I currently have. Feel free to use it, or to suggest something better:

class HandlerFinder(object):
    """Searches for handler implementations across multiple objects.
    # See <> for why this is
    # necessary.

    def __init__(self, backing_objects):
        self.backing_objects = backing_objects

    def __getattr__(self, name):
        for o in self.backing_objects:
            if hasattr(o, name):
                return getattr(o, name)
            raise AttributeError("%r not found on any of %r"
                % (name, self.backing_objects))

Respondido el 13 de enero de 11 a las 03:01

Thanks a lot, Simple and effective! :) - Mac

I have been looking for a solution to this for some time and found that it can be done by passing a dict of all the handlers to connect_signals.

The inspect module can extract methods using inspect.getmembers(instance, predicate=inspect.ismethod These can then be concatenated into a dictionary using d.update(d3), watching out for duplicate functions such as on_delete.

Código de ejemplo:

import inspect
handlers = {}
for c in [win2, win3, win4, self]:  # self is the main window
    methods = inspect.getmembers(c, predicate=inspect.ismethod)

This will not pick up alias method names declared using @alias. For an example of how to do that, see the code for, at def dict_from_callback_obj.

contestado el 27 de mayo de 15 a las 13:05

I'm only a novice but this is what I do, maybe it can inspire;-)

I instantiate the major components from a 'control' and pass the builder object so that the instantiated object can make use of any of the builder objects (mainwindow in example) or add to the builder (aboutDialog example). I also pass a dictionary (dic) where each component adds "signals" to it.
Then the 'connect_signals(dic)' is executed.
Of course I need to do some manual signal connecting when I need to pass user arguments to the callback method, but those are few.
class Control:

    def __init__(self):

        # Load the builder obj
        guibuilder = gtk.Builder()
        # Create a dictionnary to store signal from loaded components
        dic = {}

        # Instanciate the components...
        aboutdialog = modules.aboutdialog.AboutDialog(guibuilder, dic)           
        mainwin = modules.mainwindow.MainWindow(guibuilder, dic, self)

        del dic

class AboutDialog:

    def __init__(self, builder, dic):
        dic["on_OpenAboutWindow_activate"] = self.on_OpenAboutWindow_activate
        self.builder = builder

    def on_OpenAboutWindow_activate(self, menu_item):
        self.aboutdialog = self.builder.get_object("aboutdialog")


class MainWindow:

    def __init__(self, builder, dic, controller):

        self.control = controller

        # get gui xml and/or signals
        dic["on_file_new_activate"] = self.control.newFile
        dic["on_file_open_activate"] = self.control.openFile
        dic["on_file_save_activate"] = self.control.saveFile
        dic["on_file_close_activate"] = self.control.closeFile

        # get needed gui objects
        self.mainWindow = builder.get_object("mainWindow")

Edit: alternative to auto attach signals to callbacks:
Untested code

def start_element(name, attrs):
    if name == "signal":
        if attrs["handler"]:
            handler = attrs["handler"]
            #Insert code to verify if handler is part of the collection
            #we want.

def extractSignals(uiFile)
    import xml.parsers.expat
    p = xml.parsers.expat.ParserCreate()
    p.StartElementHandler = self.start_element

self.handlerList = []

for handler in handlerList:
    dic[handler] = eval(''. join(["self.", handler, "_cb"]))

Respondido el 10 de enero de 11 a las 20:01

Thanks for answering. I can see that will work, but it's repeating the handler names which is just what I wanted to avoid :) - billar

I'm learning by trying to answer. Something I will try... name my handlers with a convention that identifies which class would need it. Then parse the ui file to locate the handlers desired, or it could be all the handlers if the ui file is specific to the class. Code snipped added in my answer. Unsure if this helps you or not, but I'm sure learning fom it. :-) - David

   "on_window_destroy" : gtk.main_quit, 
   "on_buttonQuit_clicked" : gtk.main_quit 

Respondido el 05 de junio de 14 a las 18:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.