How can I switch the focus between toplevels of a tkinter program?

Question:

I am using key-accelerators for menu entries in a multi window program. But when an accelerator-key is pressed, then always the same window reacts to the key.

As you can see in my example code, I tried to change the focus by binding the event "FocusIn" to the toplevel and to the canvas. At the event I tried focus_force() and focus_set(). But the accelerator key event is always recognized in the last opened window.
This is my example code:

import tkinter as tk
from   tkinter import ttk

class win:
    number = 0
    def __init__(self):
        win.number += 1
        self.number = win.number

        self.top = tk.Toplevel()
        self.top.protocol("WM_DELETE_WINDOW", lambda: self.close_root())
        self.top.bind_all("<Control-o>", lambda event : self.menu())

        self.file_menu_button = ttk.Menubutton(self.top, text="File menu of top" + str(self.number))
        self.file_menu_button.grid()
        self.file_menu = tk.Menu(self.file_menu_button)
        self.file_menu.add_command(label="Open", accelerator="Ctrl+o", command=self.menu)
        self.file_menu_button.configure(menu=self.file_menu)

        self.canvas = tk.Canvas(self.top, width=400, height=200)
        self.canvas.grid()

        self.top.bind("<FocusIn>", lambda event: self.top.focus_force())
        #self.top.bind("<FocusIn>", lambda event: self.top.focus_set())
        self.canvas.bind("<FocusIn>", lambda event: self.top.focus_force())
        #self.canvas.bind("<FocusIn>", lambda event: self.top.focus_set())

    def close_root(self) : root.quit()
    def menu(self): print("menu" + str(self.number) + " clicked")

root = tk.Tk()
root.withdraw()
win1 = win()
win2 = win()
root.mainloop()

Answers:

I found the problem is caused by this line from my example code above:

self.top.bind_all("<Control-o>", lambda event : self.menu())

This line always gives the last new created toplevel the binding. All previous created toplevel windows loose the binding in this moment. So this line must be removed. Instead the binding must be created new at any time when a toplevel window gets the focus. This can be done in this way:

self.top.bind("<FocusIn>",
               lambda event:
               self.top.bind_all("<Control-o>", lambda event : self.menu()))
Answered By: Matthias Schweikart
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.