Linux 系统中按错时大写锁定键始终打开

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

由于键盘指示器小部件无法在我的kali系统上运行,因此我决定使用pyqt自己编写一个。我发现如果我把程序分离出来跑一下就正常了,但是用pyqt6就不行了。在Windows上运行正常,但是在Linux上却出现了一个很奇怪的问题。即使我反复按大写锁定,它仍然返回相同的错误值。

import subprocess
from time import sleep
while(True):
    print(subprocess.run("xset q | grep \"Caps Lock\" | awk -F': ' '{gsub(/[0-9]/,\"\",$3); print $3}'",
                                        stdout=subprocess.PIPE,
                                        shell=True,
                                        text=True).stdout.strip() == 'on')    
    sleep(0.3)
# pip install PyQt6 pynput
from platform import system
from sys import argv, exit

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QPalette, QColor, QFont
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel
from pynput import keyboard


class CapsLockDetector(QMainWindow):
    def __init__(self):
        super().__init__()

        self.status_label = None
        self.initUI()
        self.setupKeyboardHook()

    def initUI(self):
        self.setWindowTitle('Caps Lock Detector')
        self.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.FramelessWindowHint)
        self.setGeometry(0, 0, 400, 120)

        palette = self.palette()
        palette.setColor(QPalette.ColorRole.Window, QColor(10, 10, 10))
        self.setPalette(palette)

        screen_geometry = QApplication.primaryScreen().geometry()
        self.move(screen_geometry.x(), screen_geometry.y())
        self.status_label = QLabel(self)
        self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.setCentralWidget(self.status_label)
        self.status_label.setStyleSheet("color: white;")
        font = QFont("Consolas", 40)  
        self.status_label.setFont(font)
        self.updateCapsLockStatus()

    def setupKeyboardHook(self):
        listener = keyboard.Listener(on_press=self.on_key_press)
        listener.start()

    def on_key_press(self, key):
        if key == keyboard.Key.caps_lock:
            self.updateCapsLockStatus()

    def updateCapsLockStatus(self):
        new_status: bool = None
        if system() == "Windows":
            import ctypes
            hllDll = ctypes.WinDLL("User32.dll")
            VK_CAPITAL = 0x14
            new_status = hllDll.GetKeyState(VK_CAPITAL) not in [0, 65408]
        elif system() == "Linux":
            import subprocess
            new_status = subprocess.run("xset q | grep \"Caps Lock\" | awk -F': ' '{gsub(/[0-9]/,\"\",$3); print $3}'",
                                        stdout=subprocess.PIPE,
                                        shell=True,
                                        text=True).stdout.strip() == 'on'
            
            print(new_status)
        self.show()
        self.status_label.setText("OFF" if not new_status else "ON")

    def mousePressEvent(self, event):
        self.hide()


if __name__ == '__main__':
    app = QApplication(argv)
    window = CapsLockDetector()
    window.show()
    exit(app.exec())

我希望我的程序返回正确的值

linux pyqt pyqt6 pynput capslock
1个回答
0
投票

在 Linux(使用 X11/Xorg)上,仅当按键释放时,大写锁定才会关闭。事实上,有一个相关报告也包含补丁,但不幸的是,即使在十年年后它也从未被合并!

因此,最安全的解决方案是仅依赖密钥释放:

    def setupKeyboardHook(self):
        listener = keyboard.Listener(on_release=self.on_key_release)
        listener.start()

    def on_key_release(self, key):
        if key == keyboard.Key.caps_lock:
            self.updateCapsLockStatus()

现在,由于这可能是针对长时间运行的程序,并且您可能仍然希望尽快看到更新的当前状态,因此您可以保留内部状态并仅在必要时更新它。

但是,在此之前,您需要记住一个极其重要的方面,即使对于您最初的实现也是如此:

pynput
通过使用单独的线程工作,而小部件是不是线程安全的。

请注意,当某些东西“不是线程安全的”时,并不一定意味着它不起作用,但它是“不安全”的。尝试从单独的线程访问 UI 元素在某些情况下可能会正常工作,但通常会导致意外结果、图形问题、行为不一致甚至致命崩溃。这就是为什么它总是不鼓励,而具有适当信号的 QThread 是使线程与 UI 通信的唯一安全方法。 使用这样的键盘监听器的正确方法是将其移动到自己的线程。 class Listener(QThread): caps = pyqtSignal(bool) def __init__(self): super().__init__() self.listener = keyboard.Listener( on_press=self.press, on_release=self.release) self.started.connect(self.listener.start) def press(self, key): if key == keyboard.Key.caps_lock: self.caps.emit(True) def release(self, key): if key == keyboard.Key.caps_lock: self.caps.emit(False) class CapsLockDetector(QMainWindow): capsStatus = False def __init__(self): ... self.listener = Listener() self.listener.caps.connect(self.handleCaps) self.listener.start() def handleCaps(self, pressed): if not pressed or not self.capsStatus: self.updateCapsLockStatus() def updateCapsLockStatus(self): ... self.capsStatus = new_status

请注意,为此使用 QMainWindow 没有多大意义,您可以直接子类化 QLabel。

© www.soinside.com 2019 - 2024. All rights reserved.