In-place substitution of PyGTK widgets
Question: The code below creates a column of three labels. I would like to take the
middle label, and replace it with another widget using the text from the label
_after the initial creation of the UI_.

My actual use case is to take a GTKBuilder populated UI, and replace any
particular named label with a [dynamically wrapped
label](http://stackoverflow.com/questions/1893748/pygtk-dynamic-label-
wrapping) at run time. (I used a button here because it's simple but
distinct.) Then I can still use Glade to set up the UI, including the labels,
and not pepper my Python code with errant labels and strings if I later want
to make a label wrap.

The code as it stands does not work - the new button gets added to the end of
the column, and I want it to remain in the middle, where `label2` was to begin
with. What can I do, preferably in `wrap_in_button`, to make sure it ends up
in the correct place? I'd rather keep it general, since the parent may be a
`Box` or a `Table` or any general `Container`.

    
    
    import pygtk
    import gtk
    
    def destroy(widget, data=None):
        gtk.main_quit()
    
    def wrap_in_button(label):
        text = label.get_text()
        button = gtk.Button(text)
    
        parent = label.get_parent()
    
        if parent:
            parent.remove(label)
            parent.add(button)
    
    def Main():
        # Pretend that this chunk is actually replaced by GTKBuilder work
        # From here...
        window = gtk.Window()
        window.connect('destroy', destroy)
    
        box = gtk.VBox()
        window.add(box)
    
        label1 = gtk.Label("Label 1")
        label2 = gtk.Label("Label 2")
        label3 = gtk.Label("Label 3")
    
        box.pack_start(label1)
        box.pack_start(label2)
        box.pack_start(label3)
    
        # ...up to here
    
        # Comment this to see the original layout
        wrap_in_button(label2)
    
        window.show_all()
    
        gtk.main()
    
    if __name__ == "__main__":
        Main()
    


Answer: I found the solution in the code of the _gazpacho_ interface designer.

You can use this function:

    
    
    def replace_widget(current, new):
        """
        Replace one widget with another.
        'current' has to be inside a container (e.g. gtk.VBox).
        """
        container = current.parent
        assert container # is "current" inside a container widget?
    
        # stolen from gazpacho code (widgets/base/base.py):
        props = {}
        for pspec in gtk.container_class_list_child_properties(container):
            props[pspec.name] = container.child_get_property(current, pspec.name)
    
        gtk.Container.remove(container, current)
        container.add(new)
    
        for name, value in props.items():
            container.child_set_property(new, name, value)
    

The key seems to be to transfer the child properties from the old widget to
the new one after running `gtk.Container.add()`.

Applied to your example, this would be:

    
    
    import pygtk
    import gtk
    
    def replace_widget(current, new):
        """
        Replace one widget with another.
        'current' has to be inside a container (e.g. gtk.VBox).
        """
        container = current.parent
        assert container # is "current" inside a container widget?
    
        # stolen from gazpacho code (widgets/base/base.py):
        props = {}
        for pspec in gtk.container_class_list_child_properties(container):
            props[pspec.name] = container.child_get_property(current, pspec.name)
    
        gtk.Container.remove(container, current)
        container.add(new)
    
        for name, value in props.items():
            container.child_set_property(new, name, value)
    
    def destroy(widget, data=None):
        gtk.main_quit()
    
    def wrap_in_button(label):
        text = label.get_text()
        button = gtk.Button(text)
    
        replace_widget(label, button)
    
    def Main():
        # Pretend that this chunk is actually replaced by GTKBuilder work
        # From here...
        window = gtk.Window()
        window.connect('destroy', destroy)
    
        box = gtk.VBox()
        window.add(box)
    
        label1 = gtk.Label("Label 1")
        label2 = gtk.Label("Label 2")
        label3 = gtk.Label("Label 3")
    
        box.pack_start(label1)
        box.pack_start(label2)
        box.pack_start(label3)
    
        # ...up to here
    
        wrap_in_button(label2)
    
        window.show_all()
        gtk.main()
    
    if __name__ == "__main__":
        Main()
    

This works for me using Python 2.6.6 and PyGTK 2.17.

As a solution to your original problem, I used `label_set_autowrap()` [from
here](http://stackoverflow.com/questions/1893748/pygtk-dynamic-label-
wrapping/2016849#2016849) and it worked most of the time. However, it's not a
perfect solution as I wasn't able to correctly right-align auto-wrapped text.

