23

I have the following code

window = Tk()
window.lift()
window.attributes("-topmost", True)

This code works in that it displays my Tkinter window above all other windows, but it still only solves half of the problem. While the window is in fact displayed above all other windows, the window does not have focus. Is there a way not only to make the window the frontmost window in Tkinter, but also put focus on it?

Python Spoils You
  • 374
  • 1
  • 2
  • 12
  • What makes you think it doesn't have focus? Have you tried calling `focus_force` on the window? – Bryan Oakley Mar 30 '14 at 23:43
  • 3
    I think it does not have focus because the window is grayed out, and I have to click in for it to recognize keystrokes, also `focus_force` does not resolve the problem – Python Spoils You Mar 30 '14 at 23:51
  • @marczellm, can you specify the issue? I ran the code on Python 3.6 and Windows 10 and it works – Tarun Lalwani Nov 04 '17 at 19:18
  • @TarunLalwani To be specific, I would like my Tkinter window to steal global focus from all applications and set it to an Entry field. The window does appear on top of all windows with `-topmost` but none of the methods puts the focus on the Entry field - the previously active applications remains the active/focused window. – marczellm Nov 04 '17 at 21:17
  • Please provide a sample test app that you use. also python version and how you launch the script? @marczellm – Tarun Lalwani Nov 05 '17 at 05:04
  • @marczellm, please check the code I posted. That works great for me – Tarun Lalwani Nov 05 '17 at 08:26

5 Answers5

18

If focus_force() is not working you can try doing:

window.after(1, lambda: window.focus_force())

It's essentially the same thing, just written differently. I just tested it on python 2.7.

root.focus_force() wouldn't work but the above method did.

Python Spoils You
  • 374
  • 1
  • 2
  • 12
Brian Fuller
  • 386
  • 1
  • 3
  • 12
  • What OS did you use? (I'm on OSX) – Python Spoils You Mar 31 '14 at 01:25
  • @PythonSpoilsYou Sorry for the late reply but I am on Windows. I just retested this and it seems to have worked on only one code but another did not. – Brian Fuller Mar 31 '14 at 19:03
  • Do you have any idea as to why? – Python Spoils You Mar 31 '14 at 22:24
  • 1
    @PythonSpoilsYou No, not a clue. It baffles me. And since you're on OSX, it makes me clueless even more. You could try to do some hard googling such as, 'make tkinter window focus OSX'. Best of luck to you. – Brian Fuller Mar 31 '14 at 22:51
  • @BrianFuller I am having a slightly different problem. I have used the following 3 ways to bring window to focus - `window.focus_force()` , `window.wm_attributes("-topmost" , -1)` and the answer given by you `window.after(1, lambda : window.focus_force()) `. In each of the three ways, the window does come to focus as soon as I run my program, but itself minimizes (I neither click anywhere , nor minimize it) 1 second later. Why? I am on Windows 8 – Nancy Sep 23 '15 at 08:08
  • Great! Thanks. The no-focus-on-TK-window issue has tortured me! – Apostolos Nov 03 '22 at 22:58
14

Solution for Windows is a littlebit tricky - you can't just steal the focus from another window, you should move your application to foreground somehow. But first, I suggest you to consume a littlebit of windows theory, so we're able to confirm, that this is what we want to achieve.

As I mentioned in comment - it's a good opportunity to use the SetForegroundWindow function (check restrictions too!). But consider such stuff as a cheap hack, since user "owns" foreground, and Windows would try to stop you at all cost:

An application cannot force a window to the foreground while the user is working with another window. Instead, Windows flashes the taskbar button of the window to notify the user.

Also, check a remark on this page:

The system automatically enables calls to SetForegroundWindow if the user presses the ALT key or takes some action that causes the system itself to change the foreground window (for example, clicking a background window).

Here's goes a simplest solution, since we're able to emulate Alt press:

import tkinter as tk
import ctypes

#   store some stuff for win api interaction
set_to_foreground = ctypes.windll.user32.SetForegroundWindow
keybd_event = ctypes.windll.user32.keybd_event

alt_key = 0x12
extended_key = 0x0001
key_up = 0x0002


def steal_focus():
    keybd_event(alt_key, 0, extended_key | 0, 0)
    set_to_foreground(window.winfo_id())
    keybd_event(alt_key, 0, extended_key | key_up, 0)

    entry.focus_set()


window = tk.Tk()

entry = tk.Entry(window)
entry.pack()

#   after 2 seconds focus will be stolen
window.after(2000, steal_focus)

window.mainloop()

Some links and examples:

CommonSense
  • 4,232
  • 2
  • 14
  • 38
  • **Warning**: After running this code my pc started acting strangely (eg. keyboard didn't work) even after exiting the program. Had to restart to make everything normal again. – Sebastian Nielsen Jan 23 '20 at 22:51
  • @Sebastian Nielsen, if the only concern is the keyboard, then this isn't strange, but it's possible if you interrupt `steal_focus` midway. Anyway, [`keybd_event`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-keybd_event) has been superseded a while ago, so maybe you should build your solution around the up-to-date [`SendInput`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput) counterpart, but I unnable to reproduce behaviour that you described. – CommonSense Jan 24 '20 at 10:04
  • This solution is the only one that works, but how do you use SendInput in place of keybd_event? – Elijah Jan 20 '21 at 19:23
  • @Eli, try this: https://stackoverflow.com/a/62205935/6634373 – CommonSense Jan 28 '21 at 14:20
  • Wonderful! I was able to make it work by sending "alt" keypress. I used "keyboard" library and then used Keyboard.press_and_release('alt') before doing a "focus_force()" on the window – Sandeep S D Nov 25 '21 at 13:06
5

Not sure what issue you face. But below is sample code which worked great for me.

from tkinter import *

window = Tk()

def handle_focus(event):
    if event.widget == window:
        window.focus_set()
        input1.focus_set()


label1 = Label(window,text = "Enter Text 2")
input1 = Entry(window, bd=5)

label2 = Label(window,text = "Enter Text 2")
input2 = Entry(window, bd=5)

submit = Button(window, text="Submit")

label1.pack()
input1.pack()
label2.pack()
input2.pack()
submit.pack(side=BOTTOM)

window.lift()
window.attributes("-topmost", True)

window.bind("<FocusIn>", handle_focus)

hwnd = window.winfo_id()

window.mainloop()

This was tested using Latest Python 3.6 on Windows 10.

Test Results

The result was that after running the program, I could just start typing and and he input would go to the first text box

Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
  • what I would like to do is that my application (in response to a global keyboard event or a timed event) steals focus from other applications such as Firefox. – marczellm Nov 06 '17 at 23:29
  • @marczellm, unfortunately, your demands isn't about stealing the focus from another application (at least on win), but about replacing one [*foreground*](https://msdn.microsoft.com/en-gb/library/windows/desktop/ms633539(v=vs.85).aspx) window (e.g. firefox) with another (your tk application). And it's not that trivial task as it seems to be, so here's a [similar problem](https://stackoverflow.com/questions/26323767/intercepting-a-windows-attempt-to-steal-global-focus-on-windows). – CommonSense Nov 07 '17 at 09:35
  • @Tarun Lawani thanks for this. This approach helped me solve a focus capture problem for a script running on MacOS when nothing else was working. – Cam U May 07 '18 at 02:06
4

Note: this is Windows specific. This focuses the whole main window. Essentially forcing an alt-tab to reach the window.


None of these answers worked for me, the tk window would appear on top of everything with the -topmost flag, my input box would have focus, but the window itself wouldn't, which meant that typing wouldn't appear in the tk window. It looks like like this:

enter image description here

What helped was adding a call to the Windows API to steal focus:

import win32gui    
root = tk.Tk()

# Application specific setup ...

win32gui.SetForegroundWindow(root.winfo_id())

Disclaimer: Stealing focus is bad behaviour, so only use it if you must and if it makes sense, e.g. input box the user will instantly type into.

dtasev
  • 540
  • 6
  • 12
0

For Linux (Ubuntu 16.04.6 LTS) I tried many suggestions including .grab which broke things. In the end I used:

if self.top2_is_active is True: # Are we already playing songs?
    self.top2.focus_force()     # Get focus
    self.top2.lift()            # Raise in stacking order
    root.update()
    return                      # Don't want to start playing again

It's not really "stealing" focus because I have a button on the main window defined as:

''' ▶  Play Button '''
self.play_text="▶  Play"                # play songs window is opened.
self.listbox_btn2 = tk.Button(frame3, text=self.play_text, \
                    width=BTN_WID, command=self.play_items)
self.listbox_btn2.grid(row=0, column=1, padx=2)

After clicking this button the text is changed to:

self.listbox_btn2 ["text"] = "  Show playing"     # Play button
WinEunuuchs2Unix
  • 1,801
  • 1
  • 17
  • 34