Замена виджетов 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()

person detly    schedule 24.06.2010    source источник
comment
Если вам нужно только заменить ТЕКСТ в метке (вместо замены самой метки), вы можете использовать метод label.set_text() вашего экземпляра метки.   -  person heltonbiker    schedule 13.05.2011
comment
@heltonbiker - Нет, я бы заменил его виджетом с динамически переносимой меткой (см. ссылку).   -  person detly    schedule 13.05.2011


Ответы (4)


Вместо того, чтобы помещать этикетки непосредственно в основной контейнер, вы можете поместить каждую в отдельную коробку.

Измените это:

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 дочерний виджет в этих полях за раз.

person John    schedule 29.06.2010
comment
В итоге я сделал что-то подобное — используя gtk.Alignments вместо HBoxes. - person detly; 29.06.2010

Я нашел решение в коде дизайнера интерфейса 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() здесь, и это сработало большую часть времени. Однако это не идеальное решение, так как я не смог правильно выровнять автоматически перенос текста по правому краю.

person Matthias    schedule 07.11.2010

В документации 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.

person Jon    schedule 24.06.2010
comment
Однако я не могу использовать pack_start и друзей на Table. А удаление и повторное добавление всех виджетов предполагает, что в Glade не было специальных настроек, связанных с упаковкой, заполнением и т. д. - person detly; 24.06.2010
comment
Таблица имеет функцию attach. Это поможет? В любом случае, при каких обстоятельствах вы хотите динамически изменять элемент управления? не смутит ли это пользователя? - person Jon; 24.06.2010
comment
Ярлык может находиться внутри любого gtk.Container. И он не меняется динамически, он меняется при создании интерфейса. Я просто хочу сохранить свои метки и их текст в файлах gtkbuilder, а код — в файлах Python. - person detly; 24.06.2010
comment
Означает ли это, что вы используете метки на поляне только как заполнители? Если у вас нет реальной необходимости менять виджеты во время выполнения, я бы не стал этого делать. Просто уберите этикетки из поляны и добавьте правильные виджеты в правильный контейнер. - person Jon; 24.06.2010
comment
Вот этого я и надеялся избежать. Этикетки есть в Glade, так что я могу примерно видеть, что где. Это также означает, что мне не нужно возиться с переводом в Python. В противном случае у меня есть пользовательский интерфейс, который выглядит как швейцарский сыр, пока я пытаюсь его выложить, и много дублирования кода. Золотая середина для этого состоит в том, чтобы обернуть любую метку в именованный gtk.Alignment, а затем выполнить обмен на дочернем элементе этого. - person detly; 24.06.2010
comment
На мой взгляд, вы пишете более сложный код для решения проблемы, которая не затрагивает пользователей. В приложении, которое я разрабатываю, у меня есть древовидное представление, в котором модель и столбцы создаются в коде из-за проблемы с поляной. Я думаю, что у вас будет меньше проблем, если просто создать графический интерфейс для этих ярлыков вручную, чем пытаться автоматизировать его только ради того, чтобы увидеть его в дизайнере. В любом случае, неужели так сложно писать? - person Jon; 24.06.2010
comment
Нет, но каким бы я был программистом, если бы не тратил в два раза больше времени на поиски более простого пути? - person detly; 24.06.2010

Подобно ответу Матиаса, я однажды написал код ниже:

однако он не сохраняет все свойства, поэтому он работает только с некоторыми контейнерами, которые я хотел проверить в то время.

Сделано для 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)
person Dima Tisnek    schedule 27.10.2012