Home > Software engineering >  Keep console focus when calling mainloop with tkinter in Windows
Keep console focus when calling mainloop with tkinter in Windows

Time:10-12

I am using cmd.exe to run a program that opens a tkinter window. I'd like to keep focus on the console when the root.mainloop() function is called. Example code below. This is running on a Window system with Python 3.9.

# gui thread
def guiTask():
  print("doing tasks here")
  root.after(50, guiTask)

# main GUI
root = tk.Tk()
root.geometry('%dx%d' % (800, 800))
root.after(50, guiTask)
root.mainloop()

So this bit of code will just print something every 50 ms after opening a tkinter window. The focus goes to this window upon execution. I'd like the focus to be kept on the console. Is this possible?

CodePudding user response:

Windows only (to my knowledge):
Well, I found a way (partially (by what is meant the majority of the function) taken from Get HWND of each Window? (the question itself)):

import tkinter as tk


def win_focus_set(win_name):
    import ctypes
    enum_windows = ctypes.windll.user32.EnumWindows
    enum_windows_proc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
    get_window_text = ctypes.windll.user32.GetWindowTextW
    get_window_text_length = ctypes.windll.user32.GetWindowTextLengthW
    is_window_visible = ctypes.windll.user32.IsWindowVisible
    windows = []

    def foreach_window(hwnd, _):
        if is_window_visible(hwnd):
            length = get_window_text_length(hwnd)
            buff = ctypes.create_unicode_buffer(length   1)
            get_window_text(hwnd, buff, length   1)
            windows.append((hwnd, buff.value))
        return True

    enum_windows(enum_windows_proc(foreach_window), 0)
    for hwnd, name in windows:
        if win_name in name:
            ctypes.windll.user32.BringWindowToTop(hwnd)


def gui_task():
    print("doing tasks here")
    root.after(50, gui_task)


# main GUI
root = tk.Tk()
root.geometry('%dx%d' % (800, 800))
root.after(50, gui_task)
root.after(1, win_focus_set, 'cmd.exe')
root.mainloop()

Basically it is just that function and then call it as soon as possible using .after so that it gets executed right after the main window shows up (also "cmd.exe" just has to be in the title of the window (so if you had two cmds open it may switch to the other (although that could happen even if you provided the full title if they both had the same title)) and .after takes the rest of the arguments as arguments to pass to the scheduled function)

The other option would be to get the window that is currently in focus and bring that to top after tkinter starts:

import tkinter as tk
import ctypes

hwnd = ctypes.windll.user32.GetForegroundWindow()


def gui_task():
    print("doing tasks here")
    root.after(50, gui_task)


# main GUI
root = tk.Tk()
root.geometry('%dx%d' % (800, 800))
root.after(50, gui_task)
root.after(1, ctypes.windll.user32.BringWindowToTop, hwnd)
root.mainloop()
  • Related