¿Cómo capturar eventos en widgets secundarios tkinter?

En el siguiente bloque, al hacer clic en a_frame activa el controlador de eventos on_frame_click , pero al hacer clic en a_label que es un elemento secundario de a_frame no. ¿Hay alguna forma de forzar a a_frame a atrapar y manejar eventos que se originaron en sus hijos (preferiblemente sin tener que agregar controladores directamente a los niños)? Estoy usando Python 3.2.3.

 import tkinter def on_frame_click(e): print("frame clicked") tk = tkinter.Tk() a_frame = tkinter.Frame(tk, bg="red", padx=20, pady=20) a_label = tkinter.Label(a_frame, text="A Label") a_frame.pack() a_label.pack() tk.protocol("WM_DELETE_WINDOW", tk.destroy) a_frame.bind("

Sí, puedes hacer lo que quieras, pero requiere un poco de trabajo. No es que no sea compatible, solo que en realidad es bastante raro necesitar algo como esto, por lo que no es el comportamiento predeterminado.

TL; DR – investigación “tkinter bind tags”

El modelo de evento Tkinter incluye la noción de ” tags de enlace “. Esta es una lista de tags asociadas con cada widget. Cuando se recibe un evento en un widget, se comprueba cada etiqueta de enlace para ver si tiene un enlace para el evento. Si es así, se llama al manejador. Si no, continúa. Si un controlador devuelve “break”, la cadena se rompe y no se consideran más tags.

De forma predeterminada, las tags de enlace para un widget son el propio widget, la clase de widget, la etiqueta para la ventana de nivel superior en la que se encuentra el widget y, finalmente, la etiqueta especial “all”. Sin embargo, puede poner allí todas las tags que desee y cambiar el orden.

¿El resultado práctico de todo esto? Puede agregar su propia etiqueta única a cada widget, luego agregar un enlace único a esa etiqueta que será procesada por todos los widgets. Aquí hay un ejemplo, usando su código como punto de partida (agregué un widget de botón, para mostrar que esto no es algo especial solo para marcos y tags):

 import Tkinter as tkinter def on_frame_click(e): print("frame clicked") def retag(tag, *args): '''Add the given tag as the first bindtag for every widget passed in''' for widget in args: widget.bindtags((tag,) + widget.bindtags()) tk = tkinter.Tk() a_frame = tkinter.Frame(tk, bg="red", padx=20, pady=20) a_label = tkinter.Label(a_frame, text="A Label") a_button = tkinter.Button(a_frame, text="click me!") a_frame.pack() a_label.pack() a_button.pack() tk.protocol("WM_DELETE_WINDOW", tk.destroy) retag("special", a_frame, a_label, a_button) tk.bind_class("special", " 

Para obtener más información sobre las tags de enlace, es posible que esté interesado en mi respuesta a la pregunta ¿Cómo enlazar eventos propios en el widget de texto Tkinter después de que se vincule con el widget de texto? . La respuesta aborda una pregunta diferente a la de aquí, pero muestra otro ejemplo del uso de tags de enlace para resolver problemas del mundo real.

Parece que no puedo encontrar un método directo para vincular automáticamente a widgets secundarios (aunque existen métodos para vincular a una clase completa de widgets y a todos los widgets en una aplicación), pero algo como esto sería bastante fácil.

 def bind_tree(widget, event, callback, add=''): "Binds an event to a widget and all its descendants." widget.bind(event, callback, add) for child in widget.children.values(): bind_tree(child, event, callback, replace_callback) 

Solo pensé en esto, pero también podría poner un widget transparente del tamaño de a_frame encima de todo como hijo de a_frame y vincular el evento a eso, y luego podría referirse a a_frame como e.widget.master en la callback para hacerla reutilizable si es necesario. Eso probablemente haría lo que quieras.

Según lo que dice en la sección Niveles de vinculación de esta referencia en línea de Tkinter , parece que es posible porque puede vincular un controlador a tres niveles diferentes.
Para resumir:

  1. Nivel de instancia: vincular un evento a un widget específico.
  2. Nivel de clase: vincular un evento a todos los widgets de una clase específica.
  3. Nivel de aplicación: independiente del widget: ciertos eventos siempre invocan un controlador específico.

Para más detalles consulte el primer enlace.

Espero que esto ayude.