在没有焦点的情况下监视 tkinter 中的按键
Monitor keypress in tkinter without focus
我正在使用 tkinter 在 Python 中编写一个小型计时模块。在这方面,我想全局监控何时按下转义键以停止计时。
不幸的是,tkinters“.bind”和“.bind_all”函数仅在 window 处于焦点时拾取击键。
我查看了其他几个用于记录击键的解决方案,包括包 "keyboard" 和 "pynput",但是这些包需要 运行 一个 while 循环,这会使 tkinter GUI 冻结并停止工作。
我找到了这个线程,但它对于具体说明如何完成它不是很有帮助:
我尝试了一些不同的选择
方案一:使用tkinter循环功能,但按键不注册
import keyboard
def _check_esc_pressed(self):
if self.run_active and keyboard.press('esc'):
self.Lap()
self.Stop()
self.after(50, self._check_esc_pressed())
选项 2:冻结 tkinter 客户端
import keyboard
def _check_esc_pressed(self):
while True:
if keyboard.is_pressed('esc'):
self.Lap()
self.Stop()
break
else:
pass
选项 3:冻结 tkinter 客户端
from pynput.keyboard import Key, Listener
def on_release(self, key):
if key == Key.esc:
self.Lap()
self.Stop()
# Stop listener
return False
def _check_esc_pressed(self):
def on_press(key):
pass
with Listener(
on_press=on_press,
on_release=on_release) as listener:
listener.join()
我希望按退出键会终止“_check_esc_pressed”功能,记录一圈并停止计时器。转义检查只应在 运行 处于活动状态时处理
我通过使用 python 的 system_hotkey 包找到了问题的解决方案。这个包允许你分配系统范围的热键,这些热键可以在不关注 tkinter 程序的情况下工作。
from system_hotkey import SystemHotkey
hk = SystemHotkey()
hk.register(['alt', 'q'], callback=lambda event: self.Start())
hk.register(['alt', 'w'], callback=lambda event: self.Stop())
请记住,像这样添加的任何热键都会使其他程序无法访问已注册的热键组合。
如果您仍然需要答案,或者其他人觉得这很有用...
答案可能在于我们看不到的代码 - 通过将应用程序的逻辑放在另一个线程中,它允许 tkinter 在不冻结的情况下做它的事情。
下面的代码遵循您的(稍作修改的)选项 2:
import queue
import keyboard
import threading
import time
import tkinter as tk
def app_main_loop(my_label):
# Create another thread that monitors the keyboard
input_queue = queue.Queue()
kb_input_thread = threading.Thread(target=_check_esc_pressed, args=(input_queue,))
kb_input_thread.daemon = True
kb_input_thread.start()
# Main logic loop
run_active = True
while True:
if not input_queue.empty():
if (run_active) and (input_queue.get() == "esc"):
run_active = False
Lap(my_label)
Stop()
time.sleep(0.1) # seconds
def _check_esc_pressed(input_queue):
while True:
if keyboard.is_pressed('esc'):
input_queue.put("esc")
time.sleep(0.1) # seconds
def Lap(my_label):
my_label.configure(text = "Lap")
def Stop():
print("Stopped")
if __name__ == "__main__":
# Create the ui
root = tk.Tk()
root.attributes("-fullscreen", True)
my_label = tk.Label(root, text="Hello World!")
my_label.pack()
# Run the app's main logic loop in a different thread
main_loop_thread = threading.Thread(target=app_main_loop, args=(my_label, ))
main_loop_thread.daemon = True
main_loop_thread.start()
# Run the UI's main loop
root.mainloop()
我正在使用 tkinter 在 Python 中编写一个小型计时模块。在这方面,我想全局监控何时按下转义键以停止计时。
不幸的是,tkinters“.bind”和“.bind_all”函数仅在 window 处于焦点时拾取击键。
我查看了其他几个用于记录击键的解决方案,包括包 "keyboard" 和 "pynput",但是这些包需要 运行 一个 while 循环,这会使 tkinter GUI 冻结并停止工作。
我找到了这个线程,但它对于具体说明如何完成它不是很有帮助:
我尝试了一些不同的选择
方案一:使用tkinter循环功能,但按键不注册
import keyboard
def _check_esc_pressed(self):
if self.run_active and keyboard.press('esc'):
self.Lap()
self.Stop()
self.after(50, self._check_esc_pressed())
选项 2:冻结 tkinter 客户端
import keyboard
def _check_esc_pressed(self):
while True:
if keyboard.is_pressed('esc'):
self.Lap()
self.Stop()
break
else:
pass
选项 3:冻结 tkinter 客户端
from pynput.keyboard import Key, Listener
def on_release(self, key):
if key == Key.esc:
self.Lap()
self.Stop()
# Stop listener
return False
def _check_esc_pressed(self):
def on_press(key):
pass
with Listener(
on_press=on_press,
on_release=on_release) as listener:
listener.join()
我希望按退出键会终止“_check_esc_pressed”功能,记录一圈并停止计时器。转义检查只应在 运行 处于活动状态时处理
我通过使用 python 的 system_hotkey 包找到了问题的解决方案。这个包允许你分配系统范围的热键,这些热键可以在不关注 tkinter 程序的情况下工作。
from system_hotkey import SystemHotkey
hk = SystemHotkey()
hk.register(['alt', 'q'], callback=lambda event: self.Start())
hk.register(['alt', 'w'], callback=lambda event: self.Stop())
请记住,像这样添加的任何热键都会使其他程序无法访问已注册的热键组合。
如果您仍然需要答案,或者其他人觉得这很有用...
答案可能在于我们看不到的代码 - 通过将应用程序的逻辑放在另一个线程中,它允许 tkinter 在不冻结的情况下做它的事情。
下面的代码遵循您的(稍作修改的)选项 2:
import queue
import keyboard
import threading
import time
import tkinter as tk
def app_main_loop(my_label):
# Create another thread that monitors the keyboard
input_queue = queue.Queue()
kb_input_thread = threading.Thread(target=_check_esc_pressed, args=(input_queue,))
kb_input_thread.daemon = True
kb_input_thread.start()
# Main logic loop
run_active = True
while True:
if not input_queue.empty():
if (run_active) and (input_queue.get() == "esc"):
run_active = False
Lap(my_label)
Stop()
time.sleep(0.1) # seconds
def _check_esc_pressed(input_queue):
while True:
if keyboard.is_pressed('esc'):
input_queue.put("esc")
time.sleep(0.1) # seconds
def Lap(my_label):
my_label.configure(text = "Lap")
def Stop():
print("Stopped")
if __name__ == "__main__":
# Create the ui
root = tk.Tk()
root.attributes("-fullscreen", True)
my_label = tk.Label(root, text="Hello World!")
my_label.pack()
# Run the app's main logic loop in a different thread
main_loop_thread = threading.Thread(target=app_main_loop, args=(my_label, ))
main_loop_thread.daemon = True
main_loop_thread.start()
# Run the UI's main loop
root.mainloop()