有什么方法可以让 tkinter 窗口关闭 Win + D 命令吗?

问题描述 投票:0回答:1

我正在尝试使用 python tkinter 制作一些应用程序,它必须始终位于屏幕顶部。查了很多文档,终于知道如何设置 tkinter 窗口置顶,但是没找到即使用户按下 Win + D 快捷键也能置顶的方法。

有什么方法可以让 tkinter 窗口忽略 Win + D 命令吗?

python tkinter command window shortcut
1个回答
0
投票

您可以设置一个低级键盘挂钩来本质上“吸收”任何禁止的按键,尽管这可能有点矫枉过正。在此示例中,我为左右 Windows 键设置了过滤器。

这依赖于 pywin32 模块,但可能会被重写以专门使用

ctypes

对于它的价值,有一些警告:

  • 这是一种相当严厉的方法,并且只要它正在运行,就会阻止任何/所有与 Windows 键相关的快捷方式工作
  • 它将在整个操作系统范围内执行此过滤,不仅仅是在您的应用程序中

这段代码很大程度上改编自这个答案,原作者还提供了一个键盘模块,它抽象了一些技术功夫 - 它可能值得研究一下

完成所有这些后,这里的想法是,您设置

KeyFilter
类的实例以在其自己的线程中运行,并且您在
ForbiddenKeys
枚举中设置的键将被过滤掉,如下所示:只要该线程(或更可能是创建该线程的应用程序)正在运行。

if __name__ == '__main__'
块中有一个样板示例,因此您可以尝试直接运行它以查看它是如何工作的。

import atexit
import ctypes
import win32api
import win32con
import win32gui
from enum import Enum


class ForbiddenKeys(int, Enum):
    """Enumerator of keycodes that will be filtered out"""
    # NOTE: inheriting from int allows for testing of membership once the Enum
    # is cast to a set, e.g.: 'if <val> in set(ForbiddenKeys)'
    # (int class must come before Enum)
    KEY_LWIN = win32con.VK_LWIN  # 0x5B
    KEY_RWIN = win32con.VK_RWIN  # 0x5C
    ...
    # you can add more keys here as needed
    # e.g.: KEY_LCTRL = 0xA2


class KeyFilter:
    """
    Sets up a Windows low-level keyboard hook to filter out "forbidden" keys:
    (keycodes defined in ForbiddenKeys enum)
    """
    user32 = ctypes.WinDLL('user32', use_last_error=True)
    CMPFUNC = ctypes.CFUNCTYPE(
        ctypes.c_int,
        ctypes.c_int,
        ctypes.c_int,
        ctypes.POINTER(ctypes.c_void_p)
    )

    def _filter_handler(self, nCode, wParam, lParam) -> int:
        keycode = lParam[0] & 0xFFFF_FFFF  # upper bits of keycode are masked
        if keycode in set(ForbiddenKeys):
            return 1
        else:  # key allowed, move along to the next hook
            return self.user32.CallNextHookEx(
                self.hook_id,
                nCode,
                wParam,
                lParam
            )

    def _unhook(self) -> None:
        """Unhook the keyboard hook and stop the listener loop"""
        self.user32.UnhookWindowsHookEx(self.hook_id)

    def set_hook(self) -> None:
        """Listen for keyboard events until unhooked at exit"""
        handler_ptr = self.CMPFUNC(self._filter_handler)
        # NOTE: argtypes are required for 64-bit Python compatibility
        self.user32.SetWindowsHookExW.argtypes = (
            ctypes.c_int,
            ctypes.c_void_p,
            ctypes.c_void_p,
            ctypes.c_uint,
        )
        # set up a Windows low-level keyboard hook
        self.hook_id = self.user32.SetWindowsHookExW(
            win32con.WH_KEYBOARD_LL,  # hook type: low-level keyboard input
            handler_ptr,  # point to the handler method
            win32api.GetModuleHandle(None),  # handler is in this module
            0,
        )
        atexit.register(self._unhook)  # release hook at interpreter exit
        # start the message loop
        while (msg := win32gui.GetMessage(0, 0, 0)) > 0:
            win32gui.TranslateMessage(ctypes.byref(msg))
            win32gui.DispatchMessage(ctypes.byref(msg))


if __name__ == '__main__':
    # test keyfilter
    from threading import Thread

    keyfilter = KeyFilter()
    keyfilter_thread = Thread(
        target=keyfilter.set_hook,
        name='KeyFilter',
        daemon=True,
    )
    keyfilter_thread.start()
    # press ctrl-c or close the terminal to stop the keyfilter
    while True:
        try:
            pass
        except KeyboardInterrupt:
            break
© www.soinside.com 2019 - 2024. All rights reserved.