0

I wrote a test bed to learn about button, form and menu bindings. Created a frame and put a button on it and created a menu. Bind'ed (bound?) clicks to the button, menu and form in separate routines.

(Windows 10 and Ubuntu/Pi3) When clicking the form, all is well. When clicking the button, the button routine runs, then the form routine runs, too. Don't really want this. Certainly did not expect it! I put in a global boolean to detect and stop but that seems silly.

How do I restrict the form from also answering a button click without "silly code". I can't imagine why the bind is passed through to the form anyway. What am I missing here?

#!/usr/bin/python3

from tkinter import *

def ButtonClickedOnce(event=None):
  global ButtonJustPressed
  #Have to nullify the event somehow so the form does not get it, too.
  ButtonJustPressed = True  # Comment this to see the problem.
  print("Button Single Clicked - Primary Button")
pass

def FormClickedOnce(event=None):
  global ButtonJustPressed
  if ButtonJustPressed: ButtonJustPressed = False
  else: print("Form Single Clicked - Primary Button") 
pass

def MasterStart(event=None):
  print("Let's Go")
pass

def PgmExit(event=None):                           
  print("Primary Double Click on Button or F3, Stopping.") 
  root.destroy() 
pass

ButtonJustPressed = False

root = Tk()
root.wm_title('Bind Test')

menubar = Menu(root)
filemenu = Menu(menubar, tearoff=0)
FM_Start = filemenu.add_command(label="Let's Go", accelerator = "F5",
                                command = lambda: MasterStart())  # Start 'er up.
FM_Exit  = filemenu.add_command(label="Let's Stop", accelerator = "F3",
                                command = lambda: PgmExit())  # Exit now.

menubar.add_cascade(label='File', menu=filemenu)
root.config(menu=menubar)

frame = Frame(root)
frame.pack()

MyButton = Button(frame, text='Mouse Clicks')
MyButton.pack()

#Mouse button bindings
MyButton.bind('<Button-1>', ButtonClickedOnce)
MyButton.bind('<Double-1>', PgmExit) 
root.bind('<Button-1>', FormClickedOnce)

#Key bindings
root.bind('<F3>',PgmExit)
root.bind('<F5>',MasterStart)

root.geometry('%dx%d+%d+%d' % (200, 200, 0, 0))
root.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685

1 Answers1

0

Short answer

When you bind an event to the root window, every widget in that window will react to that event. This is as it should be, because otherwise it would be impossible to make bindings that apply everywhere within a window.

The simple solution to your problem is that ButtonClickedOnce needs to return the string "break", which will prevent the click from being processed by the binding to the root window.

Long answer

When you bind to a widget, you aren't actually binding to the widget. Instead, you are associating a binding with a bind tag. By default every widget has a bind tag that is the same name as the widget. You can get or set the list of the bind tags for a widget with the bindtags method (eg: MyButton.bindtags()).

When an event fires, tkinter will work through the list of bind tags for the widget and call the callback for any events associated with each bind tag, until one of the functions returns "break" or there are no more bind tags.

When you create a widget, it initially gets four bind tags - one for the widget, one for the widget class (not the python class, but an internal class), one for the toplevel window that the widget is in, and one named "all". These bind tags are ordered, from most specific (the actual widget) to least specific ("all").

When an event is processed, these bind tags are traversed in order. For example, if you click on a button and the button has a binding, then the bound function will be called. If there is a binding on the class (which is the case with most widgets to provide their default behavior), that binding will then trigger. If there is a binding on the window, that binding will trigger. And finally, if there is a binding on "all", that binding will trigger.

At any point along this chain, if a bound function returns the string "break", the chain will be broken and no more bind tags will be processed.

For a slightly longer description of how events are processed, see this answer: https://stackoverflow.com/a/11542200/7432

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685