En Tkinter, ¿es posible recuperar la lista de funciones de devolución de llamada vinculadas a un widget?

Resuelto no-one asked hace 7 meses • 2 respuestas

Dado el siguiente código en el que se asocian varias devoluciones de llamada con un solo botón:

import tkinter as tk

def hi(event):
    print('hello')
    return hi

def bye():
    print('bye')
    return bye

def salutations(event):
    print('... and salutations...')
    return bye

def and_so_forth(event):
    print('... and so forth...')
    return bye

root = tk.Tk()
button = tk.Button(root, text='test', command = bye)
button.bind("<Button-1>", hi)
button.bind("<Button-1>", salutations, "+")
button.bind("<Button-1>", and_so_forth, "+")

button.pack()
root.mainloop()

¿Existe algún método al que pueda llamar que enumere todas las devoluciones de llamada vinculadas button?

Los resultados que me gustaría obtener son algo parecido a:

command_list = ('hi', 'salutations', 'and_so_forth', 'bye')

Lo que he probado:

  • Busqué en la lista de propiedades, pero no vi nada prometedor.
  • button.command.get() (se ignora)
  • button.command.cget() (también ignorado)
  • button.invoke() (solo informa sobre byela función)

¿Alguien sabe si esto es posible?

Gracias.

no-one avatar Feb 16 '24 21:02 no-one
Aceptado

Lo más parecido que he encontrado es el _tclCommandsatributo:

commands = button._tclCommands
print(commands)
# >>> ['1310081992192bye', '1310100638592hi', '1310100643904salutations', '1310100643712and_so_forth']

Dado el guión bajo inicial, no creo que debas hacer esto, pero es lo más parecido que pude llegar. Además, tenga en cuenta que esos números iniciales cambian cada vez que ejecuta su aplicación.

Es un poco engorroso, pero también puedes hacer una referencia cruzada de esta lista con las funciones enumeradas por dir()para obtener los nombres de las devoluciones de llamada para un widget determinado:

def get_callbacks(widget):
    callbacks = []
    for fn in dir():  # list all functions in this module
        for callback_name in widget._tclCommands:  # get callbacks
            if fn in callback_name:  # print matches
                callbacks.append[fn]
    return callbacks

# naturally, this could also be done with a list comprehension, but it's
# a bit easier to see what's going on this way
# callbacks = [fn for fn in dir() for cmd in widget._tclCommands if fn in cmd]

EDITAR para señalar que si tiene un enlace a a lambdaen su widget, entonces el _tclCommandsatributo incluirá algo como '2160369114752<lambda>'para cada lambdalímite. En este caso, las referencias cruzadas dir()no funcionarán, por lo que tendrás que recurrir a simplemente eliminar los números.

JRiggles avatar Feb 16 '2024 14:02 JRiggles

Si y no. Si estuvieras escribiendo este código en Tcl, la respuesta es "sí". bindsin darle una devolución de llamada (por ejemplo: .button bind <Button-1>) devuelve una lista de las devoluciones de llamada asociadas con el enlace. Sin embargo, tkinter tiene que agregar un contenedor alrededor de la devolución de llamada, lo que genera un resultado que es difícil de entender.

Por ejemplo, dado este código:

print(button.bind("<Button-1>"))

... en tu caso produce algo como esto:

if {"[4346304576hi %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y %D]" == "break"} break

if {"[4346304832salutations %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y %D]" == "break"} break

if {"[4346305024and_so_forth %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y %D]" == "break"} break

Podrías intentar analizar eso, pero no hay garantía de que el resultado sea el mismo en futuras versiones de tkinter, y el número exacto que tienes que eliminar probablemente será diferente cada vez que lo ejecutes.

Podría ser mejor crear su propia subclase Buttony realizar un seguimiento de sus enlaces usted mismo. De manera improvisada, podría verse así:

class CustomButton(tk.Button):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._custom_bindings = []

    def bind(self, sequence=None, func=None, add=None):
        if func is None and add is None:
            return self._custom_bindings
        elif func is not None and add is None:
            self._custom_bindings = [func]
        elif func is not None and add is not None:
            self._custom_bindings.append(func)

        return super().bind(sequence=sequence, func=func, add=add)

Con eso, puedes llamar button.bind("<Button-1>")para obtener una lista de las funciones.

Bryan Oakley avatar Feb 16 '2024 14:02 Bryan Oakley