在 Windows 中使用 tkinter 调用 mainloop 时保持控制台焦点

Keep console focus when calling mainloop with tkinter in Windows

我正在使用 cmd.exe 到 运行 打开 tkinter window 的程序。当调用 root.mainloop() 函数时,我想将注意力集中在控制台上。下面的示例代码。这是 运行ning 在 Window 系统上 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()

因此这段代码将在打开 tkinter window 后每 50 毫秒打印一些内容。执行时焦点转到 window。我希望焦点保持在控制台上。这可能吗?

Windows 仅(据我所知):
好吧,我找到了一种方法(部分地(指的是大部分功能)取自 Get HWND of each Window?(问题本身)):

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

基本上就是那个函数,然后使用 .after 尽快调用它,以便它在主 window 出现后立即执行(还有 "cmd.exe"在 window 的标题中(因此,如果您打开了两个 cmd,它可能会切换到另一个(尽管如果它们具有相同的标题,即使您提供了完整的标题,也会发生这种情况))和 .after 将其余参数作为传递给预定函数的参数)

另一种选择是获取当前处于焦点的 window 并在 tkinter 开始后将其置于顶部:

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