5

I am making a Tkinter application. In the application, I want to pop up a context menu, which I do using Tk.Menu.post().

I don't know how to make this menu disappear when the application loses focus. I need to do this because the menu stays on top, even when switching to another window, leaving a menu 'artifact'.

I put a <FocusOut> event on the menu, which is triggered if the menu has focus and the user moves focus to another application. This works well.

What do I do if the main application window has focus? I could put a <FocusOut> event on the application window, which closes the menu; however, this ends up being called when I give focus to the menu, which closes the menu. The menu is created with parent as the main application, so I am not sure why <FocusOut> on the main app is even triggered when the menu gets focus.

How do I distinguish between the main application window losing focus to a different application vs losing focus to my menu?

I don't want to use tk_popup() because I want the user to continue to provide input to the main window. (use of the menu is optional).

Thanks to @Brad Lanam I came up with a SOLUTION, which I have included:

from Tkinter import *

class App(Tk):
    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)

        self.entry = Entry(self)
        self.entry.grid(padx=30, pady=30)
        self.entry.focus_set()
        self.entry.bind("<Tab>", self.put_menu)
        self.entry.bind("<Down>", self.set_focus_on_menu)

        self.menu = Menu(self, tearoff=False)
        self.menu.add_command(label="test")
        self.menu.bind("<FocusOut>", self.destroy_menu)


        self.bind("<FocusIn>", self.app_got_focus)
        self.bind("<FocusOut>", self.app_lost_focus)
        self.bind("<3>", self.put_menu)


    def app_got_focus(self, event):
        self.config(background="red")

    def app_lost_focus(self, event):
        self.config(background="grey")

        ######## SOLUTION BEGIN #########
        if self.focus_get() != self.menu:
            self.destroy_menu(event)
        ######## SOLUTION END ###########

    def set_focus_on_menu(self, event):
        self.menu.focus_set()

    def menu_got_focus(self, event):
        self.menu.activate(0)

    def put_menu(self, event):
        self.menu.post(self.winfo_x() + self.entry.winfo_x(), self.winfo_y() + self.entry.winfo_y()+20)

    def destroy_menu(self, event):
        self.menu.destroy()

app = App()

app.mainloop()
jgoeders
  • 1,886
  • 19
  • 25
  • What platform are you on? – Bryan Oakley Aug 06 '13 at 22:13
  • 1
    To answer one of your questions, the window has focus, not the application. [Removed possible solution that won't work]. – Brad Lanam Aug 07 '13 at 21:02
  • How about this: when the main window loses focus, after X seconds, if the menu does not have focus, lower the menu window. – Brad Lanam Aug 07 '13 at 21:08
  • Kindly share the answer that you figured out,& accept it so that there are less items in the **unanswered questions** list. Thanks :) – Alok Apr 26 '14 at 04:28
  • @jgoeders - Just for the sake of keeping things organized, please add your answer as an answer to this question and accept that answer (it's perfectly acceptable for you to accept your own answer to your own question - SO is about having the best programming Q&A in the world, whether the answer comes from the same person that asked the question is not a concern.) – ArtOfWarfare Sep 19 '14 at 20:40

1 Answers1

3

self.focus_get() will return the object that has focus, which can be used to distinguish between the menu receiving focus, vs some other application.

For example, to remove the menu when the focus moves to another application:

def app_lost_focus(self, event):
    if self.focus_get() != self.menu:
        self.destroy_menu(event)
    
martineau
  • 119,623
  • 25
  • 170
  • 301
jgoeders
  • 1,886
  • 19
  • 25