8

On my home Kubuntu machine, I am running a script to beep on every keypress, no matter which window or application has focus, adapted from this insightful page

#!/usr/bin/env python 

from Xlib.display import Display
import os
import sys

ZERO=[]
for i in range(0,32):
        ZERO.append(0)
ignorelist=[ZERO]

def main():    
        if os.getuid()==0:
                os.system("modprobe pcspkr")
                print("Speaker enabled, start as normal user")
                sys.exit()

        print("If no beep is heard, then run as root to enable pcspkr")

        disp = Display()
        while 1:
                keymap=disp.query_keymap()
                if keymap not in ignorelist:
                        os.system("beep")

if __name__ == '__main__':
        main()

The script works great, but it pegs both CPUs of my dual-core Intel machine at around 80% each, so I can do little else with the machine. How can I reduce the CPU requirements of this simple script without interfering with its operation? In other words, it should still beep at the moment of keypress, no matter what window or application has focus.

If this is not possible in Python, what other technologies should I look at? C? I would assume that there exists some kernel component which notifies applications of keypresses: how else does KDE handle global shortcuts? How can I get my application to receive these notices as well?

The goal is to make a beep at the moment each key is pressed, as I am training my fingers to type on a mechanical keyboard without bottoming out yet without missing keypresses. I just graduated from Cherry Browns to Cherry Blues and the lack of tactical feedback takes some time to get used to.

Note that any solution must emit a beep no matter which window has focus. This program is intended to be used as a daemon that will run in the background of all applications that I use.

Thanks.

4

5 回答 5

12

使用事件:

import Xlib
from Xlib.display import Display

display = Display()

screen = display.screen()
w = screen.root.create_window(0, 0, 100, 100, 1,
              screen.root_depth,
              event_mask = Xlib.X.KeyPressMask)
w.map()

while True:
    event = display.next_event()
    if event.type != Xlib.X.KeyPress:
        continue
    print "OHAI"

详细信息来自http://python-xlib.sourceforge.net/doc/html/python-xlib_10.html#SEC9

于 2012-08-16T15:31:41.477 回答
8

您的 while 循环正在消耗您所有的 CPU 周期,因为它执行得尽可能快。您可以添加一个小的 sleep() 延迟,但您可能会错过一些关键事件。或者,考虑使用pyhook模块来监控按键。

于 2012-08-16T15:32:33.883 回答
7

你可以使用 nice 来启动你的脚本。nice 命令会降低脚本的优先级,这样它只会在系统无事可做时运行。这样它仍然会占用 CPU 周期,但您将能够正常使用您的系统来执行其他任务。

有关详细信息,请参见手册页。

编辑:

为了减少 CPU 使用率,您可以使用 time.sleep(0.01) 添加一个小的延迟。这将减少 CPU 负载,但会略微增加按键和发出蜂鸣声之间的时间。

于 2012-09-12T09:37:27.520 回答
5

您的程序正在占用 CPU,因为它正在运行一个无限循环,这将使您的 CPU 忙于每毫秒左右检查一次键盘状态,即使没有按下任何键也是如此。由于您的计算机没有迹象表明它可以停止,它会尽可能频繁地继续检查,从而消耗资源。

正如 Iain 所建议的,这个问题最优雅的解决方案是使用Display.next_event(),它会告诉程序等到收到新事件(例如按键)。在这段等待时间内,您的程序不会消耗大量 CPU,并且您的负载应该会大幅下降。

于 2012-08-16T15:37:55.820 回答
4

这个答案到以前发布的问题,揭示了一个名为的 Python 模块pyxhook,它是一个名为 pykeylogger 的程序的一部分。

使用此pyxhook模块,您应该能够使用以下代码在每次按键时发出哔声:

import pyxhook
import time
import os
import sys

def do_beep(event):
    os.system('beep')

hm = pyxhook.HookManager()
hm.HookKeyboard()
hm.KeyDown = do_beep
hm.start()
while True:
    try:
        time.sleep(10)
    except: 
        break
hm.cancel()

我无法正确测试它,因为我没有beep命令。但是,它会报告sh: beep: command not found每个按键。

于 2012-09-19T00:30:32.970 回答