2

I'm having trouble with the Tkinter Menu widget (no menu button), whereby the callback seems to run out of sequence. Here is a very minimal example:

# Python 3.6.5.  Windows 7 x64.
from tkinter import *
root = Tk()

popup = Menu(root, tearoff=0)
popup.add_command(label="test", command=lambda: print("clicked 'test'"))

print("Before post")
popup.post(200,200)  # Expecting print output from this (when clicked)
print("After post")

root.mainloop()
print("end of program")

Expected output:

Before post
clicked 'test'
After post
end of program

Actual output:

Before post
After post 
clicked 'test'   <--- Shouldn't this appear BEFORE previous line?
end of program

I've tried numerous things, without success, such as: popup.wait_window(), popup.update_idletasks(), popup.grab_release(), popup.unpost(), popup.destroy(), tk_popup (instead of Menu), etc.

Any advice would be appreciated.

Tom Parker
  • 31
  • 2

2 Answers2

4

clicked 'test' <--- Shouldn't this appear BEFORE previous line?

No, it shouldn't. The post only makes the menu appear, it will not wait for the user to select something from the menu. That's just not how tkinter menus are designed to work.

If you need your code to pause until the user makes a selection, you probably need to wait on a variable, and then make sure that all of the menu items set that variable.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • I changed `popup.post(200,200)` to `popup.tk_popup(200,200)` but got the same result. I must be doing something wrong. – Tom Parker Nov 06 '18 at 15:30
  • @TomParker: I think you're right, my apologies. It's been a long time since I thought about popups. Menus aren't designed to block until you select something. – Bryan Oakley Nov 06 '18 at 15:43
0

I don't get that result on Linux, although apparently the command is supposed to execute. From the docs:

If a -command option is specified for a cascade entry then it is evaluated as a Tcl command whenever the entry is invoked.

My advice is don't try to trigger an event using another trigger. Instead, point both the menu command and whatever you are trying to do programmatically to the same target.

from tkinter import *

def func():
    print("clicked 'test'")

root = Tk()

popup = Menu(root, tearoff=0)
popup.add_command(label="test", command=func)
root['menu'] = popup
print("Before post")
func()
print("After post")

root.mainloop()
print("end of program")
Novel
  • 13,406
  • 2
  • 25
  • 41
  • Thanks. However: 1) As mentioned in line 1 of the code, this is for Windows 7 x64, 2) I prefer using the .post method, because it allows specifying x, y coordinates for the menu, 3) I use a lambda function, because in my real-world case I need to pass the selection to the callback 4) the menu seems to persist even after I make a selection. Perhaps there are work-arounds to these items, but then it might be getting a bit messy. I'm hoping there might be a cleaner solution. – Tom Parker Nov 06 '18 at 05:52