PyGObject, кнопки зацикливания

Завязываю, чтобы создать петлю из кнопок, и выполнить команду при их нажатии.

k=0
for row in list:

        delete_list.append("button_delete"+str(k))
        delete_list[k] = Gtk.Button(label="Delete")
        grid.attach(delete_list[k], columns+1, k, 1, 1)
        delete_list[k].connect("clicked",globals()["on_button"+str(k)+"_clicked"])

        k+=1

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

delete_list[k].connect("clicked",globals()["on_button"+str(k)+"_clicked"])
KeyError: 'on_button0_clicked'

Сначала я подумал, что ошибка возникла из-за отсутствия метода on_button0_clicked, но я создаю его и все еще получаю ту же ошибку.

Кроме того, если есть хороший способ / совет для динамического создания методов для ответа кнопок, это было бы здорово. На самом деле мне нужно создать метод для каждой кнопки, использующий счетчик «k».


person Community    schedule 28.06.2014    source источник
comment
Сначала я подумал, что ошибка возникла из-за отсутствия метода on_button0_clicked, но я создаю его и все еще получаю ту же ошибку. - это метод какого-то класса вместо глобальной области видимости?   -  person Simon Feltman    schedule 29.06.2014
comment
Да, метод был в том же классе, и размещение его в глобальной области исправляет ошибку :)   -  person    schedule 29.06.2014


Ответы (1)


Чтобы динамически создать функцию, которая связывает переменные цикла как локальные, вам нужна фабричная функция для их генерации:

def callback_factory(num):
    def new_callback(widget):
        print(widget, num)
    return new_callback

for k, row in enumerate(somelist):
    button = Gtk.Button(label=str(k))
    button.connect('clicked', callback_factory(k))

Это позволяет избежать распространенной ошибки, заключающейся в создании метода или лямбда-выражения в теле цикла, в результате чего при каждом нажатии кнопки печатается одно и то же. Это происходит из-за среды, в которой создается функция, привязанная к сгенерированной функции, в которой переменная k изменяется и используется всеми обратными вызовами. Ловушку можно наблюдать с помощью следующего кода, который работает не так, как вы могли ожидать:

for k, row in enumerate(somelist):
    def callback(widget):
        print(k)
    button = Gtk.Button(label=str(k))
    button.connect('clicked', callback)

PyGObject также поддерживает уникальные пользовательские данные для каждого вызова соединения, которые передаются в обратный вызов:

def callback(widget, num):
    print(widget, num)

for k, row in enumerate(somelist):
    button = Gtk.Button(label=str(k))
    button.connect('clicked', callback, k))

Это позволяет использовать один и тот же обратный вызов с различными аргументами, что в некоторых случаях может быть чище.

Боковое примечание: вероятно, не рекомендуется маскировать встроенный класс «список», назначая его своему собственному экземпляру списка.

person Simon Feltman    schedule 29.06.2014
comment
Честно говоря, я немного потерялся с некоторыми вашими комментариями, мне все еще нужно читать о python и PyGobject. (я только начал изучать программирование). Первое решение отлично работает, вот что я использую. Второй всегда выводит 67, а последний мне кажется странным, потому что button.connect ('clicked', k)) никогда не вызывает функцию обратного вызова. В любом случае, первое решение отлично работает, и я постараюсь понять все, что вы сказали, это кажется довольно интересным. - person ; 29.06.2014
comment
Не беспокойтесь, средний пример должен был продемонстрировать проблемный случай, который, как вы могли ожидать, сработает, но не работает. В последнем примере отсутствовал аргумент обратного вызова, который я исправил. - person Simon Feltman; 29.06.2014