PyGTK小部件的就地替换

时间:2010-06-24 04:42:23

标签: python pygtk

下面的代码会创建一个包含三个标签的列。我想采用中间标签,并在初始创建UI 后使用标签中的文本将其替换为另一个小部件。

我的实际用例是使用GTKBuilder填充的UI,并在运行时用dynamically wrapped label替换任何特定的命名标签。 (我在这里使用了一个按钮,因为它很简单但很独特。)然后我仍然可以使用Glade设置UI,包括标签,如果我以后想要制作标签包装,不要用我的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 :(得分:5)

我在 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() from here,并且大部分时间都有效。但是,它不是一个完美的解决方案,因为我无法正确对齐自动换行的文本。

答案 1 :(得分: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)

其余代码可以保持不变。 您只需要确保一次只在这些框中有1个子窗口小部件。

答案 2 :(得分:0)

pygtk docs有关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始终以正确的顺序删除并重新添加所有小部件。

答案 3 :(得分:0)

类似于马蒂亚斯的回答,我曾在下面写过一段代码:

它不会保留所有属性,因此它只适用于某些容器,那些我当时都要测试的容器。

为python 2.4.x和相同年龄的pygtk制作,可能仍然适合你。

def replace_widget(cur, replace):
  """replace cur widget with another in a container keeping child properties"""
  con = cur.get_parent()
  pos = con.child_get_property(cur, "position")
  pak = con.query_child_packing(cur)
  con.remove(cur)
  if replace.get_parent(): replace.get_parent().remove(replace)
  con.add_with_properties(replace, "position", pos)
  con.set_child_packing(replace, *pak)