PyGTK控件的原地替换
下面的代码创建了一个包含三个标签的列。我想把中间的标签替换成另一个小部件,并且这个替换要在用户界面初次创建之后进行。
我的实际需求是使用GTKBuilder生成的用户界面,在运行时把任何特定名称的标签替换成一个动态换行的标签。(这里我用按钮是因为简单明了。)这样我就可以继续使用Glade来设置用户界面,包括标签,而不需要在我的Python代码中到处添加多余的标签和字符串,以后如果想让标签换行就方便多了。
目前的代码无法正常工作——新的按钮被添加到了列的末尾,而我希望它能保持在中间,也就是原来label2
的位置。我该怎么做,最好是在wrap_in_button
里,确保它能放在正确的位置?我希望保持通用性,因为父容器可能是Box
、Table
或者任何其他的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 个回答
pygtk的文档里有一些关于add
函数的有用信息:
这个方法通常用于简单的容器,比如gtk.Window、gtk.Frame或者gtk.Button,这些容器只放一个子组件。对于像gtk.Box或gtk.Table这样的布局容器,它们可以处理多个子组件,这个函数会选择一些默认的排列参数,但这些参数可能不太合适。
对于有多个子组件的容器,文档还提到它们有专门的函数来添加项目。在你的情况下,gtk.Box
有三个选项:pack_start
、pack_end
和reorder_child
。
我不太确定在移除一个项目后,使用pack_start(或者pack_end)会怎样。如果不行的话,reorder_child听起来可能会有帮助。
如果这些都不行,你也可以选择先移除所有组件,然后按照正确的顺序用pack_start重新添加它们。
我在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()
,可以在这里找到,大部分时候都能正常工作。不过,这并不是一个完美的解决办法,因为我没办法正确地让自动换行的文本右对齐。
与其把标签直接放在主容器里,不如把每个标签放到自己的小盒子里。
把这个:
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)
其他的代码可以保持不变。你只需要确保每个小盒子里一次只能放一个子部件。