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()