En Tkinter, ¿es posible recuperar la lista de funciones de devolución de llamada vinculadas a un widget?
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
bye
la función)
¿Alguien sabe si esto es posible?
Gracias.
Lo más parecido que he encontrado es el _tclCommands
atributo:
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 lambda
en su widget, entonces el _tclCommands
atributo incluirá algo como '2160369114752<lambda>'
para cada lambda
límite. En este caso, las referencias cruzadas dir()
no funcionarán, por lo que tendrás que recurrir a simplemente eliminar los números.
Si y no. Si estuvieras escribiendo este código en Tcl, la respuesta es "sí". bind
sin 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 Button
y 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.