PyGTK控件的原地替换

4 投票
4 回答
5206 浏览
提问于 2025-04-16 00:22

下面的代码创建了一个包含三个标签的列。我想把中间的标签替换成另一个小部件,并且这个替换要在用户界面初次创建之后进行。

我的实际需求是使用GTKBuilder生成的用户界面,在运行时把任何特定名称的标签替换成一个动态换行的标签。(这里我用按钮是因为简单明了。)这样我就可以继续使用Glade来设置用户界面,包括标签,而不需要在我的Python代码中到处添加多余的标签和字符串,以后如果想让标签换行就方便多了。

目前的代码无法正常工作——新的按钮被添加到了列的末尾,而我希望它能保持在中间,也就是原来label2的位置。我该怎么做,最好是在wrap_in_button里,确保它能放在正确的位置?我希望保持通用性,因为父容器可能是BoxTable或者任何其他的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()

4 个回答

0

pygtk的文档里有一些关于add函数的有用信息:

这个方法通常用于简单的容器,比如gtk.Window、gtk.Frame或者gtk.Button,这些容器只放一个子组件。对于像gtk.Box或gtk.Table这样的布局容器,它们可以处理多个子组件,这个函数会选择一些默认的排列参数,但这些参数可能不太合适。

对于有多个子组件的容器,文档还提到它们有专门的函数来添加项目。在你的情况下,gtk.Box有三个选项:pack_startpack_endreorder_child

我不太确定在移除一个项目后,使用pack_start(或者pack_end)会怎样。如果不行的话,reorder_child听起来可能会有帮助。

如果这些都不行,你也可以选择先移除所有组件,然后按照正确的顺序用pack_start重新添加它们。

8

我在gazpacho界面设计器的代码中找到了这个解决办法。

你可以使用这个函数:

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)

关键是要在运行完gtk.Container.add()之后,把旧组件的子属性转移到新组件上。

应用到你的例子中,就是:

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()

我在使用Python 2.6.6和PyGTK 2.17时,这个方法对我有效。

作为你最初问题的解决方案,我使用了label_set_autowrap()可以在这里找到,大部分时候都能正常工作。不过,这并不是一个完美的解决办法,因为我没办法正确地让自动换行的文本右对齐。

3

与其把标签直接放在主容器里,不如把每个标签放到自己的小盒子里。

把这个:

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)

改成这个:

box1 = gtk.HBox()
label1 = gtk.Label("Label 1")
box1.pack_start(label1)

box2 = gtk.HBox()
label2 = gtk.Label("Label 2")
box2.pack_start(label2)

box3 = gtk.HBox()
label3 = gtk.Label("Label 3")
box3.pack_start(label3)

box.pack_start(box1)
box.pack_start(box2)
box.pack_start(box3)

其他的代码可以保持不变。你只需要确保每个小盒子里一次只能放一个子部件。

撰写回答