分享免费的编程资源和教程

网站首页 > 技术教程 正文

实战PyQt5: 097-键盘事件 python检测键盘按键事件

goqiw 2024-10-10 08:17:10 技术教程 21 ℃ 0 评论

在一个GUI桌面应用中,用户和程序之间的交互一般通过键盘和鼠标来完成,处理有关键盘和鼠标的事件是一个GUI程序几乎不可或缺的部分,下面介绍在Qt中处理输入焦点和键盘事件的相关知识。

设置控件的输入焦点

在一个窗口中,一般会有多个控件在其中,如果一个控件要获得键盘输入信息,必须将输入焦点设置到该控件上,在窗口中,一个时刻只有一个(或者没有) 控件可以获得输入焦点。可以使用QWidget的下列方法来操作控件的输入焦点。

  • setFocus([reason]):- 如果控件在活动窗口中,调用此方法后,该控件成为输入焦点。reason参数可为Qt.FoucsReason中的以下枚举变量。
    • MouseFocusReason (0): 使用鼠标获得焦点;
    • TabFocusReason (1): 按下了Tab键;
    • BacktabFocusReason (2): 按下了组合键+Tab键,组合键包括Shift或Control, 例如Shift+Tab;
    • ActiveWindowFocusReason (3): 窗口变为活动和非活动;
    • PopupFocusReason (4): 打开或关闭弹出窗口;
    • ShortcutFocusReason (5): 按下快捷键;
    • MenuBarFocusReason (6): 通过菜单操作;
    • OtherFocusReaaon (7): 其他方式。
  • clearFocus(): 清除输入焦点;
  • hasFocus(): 如果控件是输入焦点,返回True;否则,返回False;
  • focusWidget(): 返回最后调用setFocus()方法的控件对象;
  • setFocusProxy(): 指定获得输入焦点的控件对象;
  • focusProxy(): 返回获得非当前控件的输入焦点的控件对象;
  • focusNextChild(): 找到下一个可获得输入焦点的控件对象,并设置为输入焦点。相当于按了Tab键。如果有此控件,返回True;否则,返回False;
  • focusPreviousChild(): 找到上一个可获得输入焦点的控件对象,并设置为输入焦点。相当于按了Shift+Tab键。如果有此控件,返回True;否则,返回False;
  • focusNextPrevChild(): 如果isnext参数为True,功能相当于focusNextChild();如果isnext参数为False,功能相当于focusPreviousChild();
  • setTabOrder(,) - 静态函数。用于指定按下键时,输入焦点的移动顺序。component2是当输入焦点在component1是按下键后的输入焦点所在的控件。如果有多个控件,则需要调用多次。例如,指定输入焦点在按下键的变化顺序为widget1->widget2->widget3->widget4,相应的代码为:
                  QWidget.setTabOrder(widget1,widget2)
                  QWidget.setTabOrder(widget2,widget3)
                  QWidget.setTabOrder(widget3,widget4)
  • setFocusPolicy ():指定控件如何设置输入焦点。method可以Qt.FocusPolicy中的以下枚举变量:
    • NoFocus: 不能设置输入焦点;
    • TabFocus: 用Tab键设置输入焦点;
    • ClickFocus: 用点击鼠标来设置输入焦点;
    • StrongFocus: 用Tab键和点击鼠标来设置焦点;
    • WheelFocus:用Tab键、点击鼠标和滚轮来设置焦点。
  • focusPolicy():返回当前获得输入焦点的方式;
  • grabKeyboard():限定键盘输入。在调用releaseKeyboard()之前,其他控件无法获得输入焦点;
  • releaseKeyboard():释放之前所作的键盘输入限定。

QApplication类的静态方法focusWidget()返回拥有键盘输入焦点的应用程序顶级窗口。如果没有有,则返回None。

QWidget类的下列方法可用来处理焦点事件:

  • focusInEvent(self, event) - 获得焦点时被调用;
  • focusOutEvent(self,even) - 失去焦点时被调用。

上述函数中的event参数为QFocusEvent类的实例,有以下方法获得焦点事件相关信息:

  • gotFocus(): 如果event的类型为QEvent.FocusIn,返回True,否则,返回False;
  • lostFocus(): 如果event的类型为QEvent.FocusOut,返回True,否则,返回False;
  • reason(): 返回设置输入焦点的原因。

焦点设置测试

在测试代码中,我们创建一个带有QPushButton按钮和两个QLineEdit单行编辑的窗口。对于单行编辑框,处理获得和失去输入焦点的事件。通过按下按钮,我们将输入焦点设置为第二个单行编辑框。 另外,通过Tab键顺向切换焦点,或者通过S hift+Tab反向切换焦点。完整代码如下:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, 
                             QLineEdit, QPushButton, QVBoxLayout, QFormLayout)
 
class MyLineEdit(QLineEdit):
    def __init__(self, id, parent=None):
        super(MyLineEdit, self).__init__(parent)
        self.id = id
        
    #焦点进入事件
    def focusInEvent(self, evt):
        print('输入焦点在:', self.id)
        QLineEdit.focusInEvent(self, evt)
        
    #焦点离开事件
    def focusOutEvent(self, evt):
        print(self.id, ':失去输入焦点')
        QLineEdit.focusOutEvent(self, evt)
 
class FocusSetDemo(QMainWindow):
    def __init__(self, parent = None):
        super(FocusSetDemo, self).__init__(parent)
        
        # 设置窗口标题
        self.setWindowTitle('实战PyQt5:焦点设置演示')
        # 设置窗口大小
        self.resize(300, 200)
        
        self.initUi()
        
    def initUi(self):
        mainWidget = QWidget()
        mainLayout = QVBoxLayout()
        mainLayout.setSpacing(10)
        
        btnChangeFocus = QPushButton('将焦点设置到编辑框2')
        btnChangeFocus.clicked.connect(self.onButtonChangeFocus)
        
        self.lineEdit1 = MyLineEdit(1)
        self.lineEdit2 = MyLineEdit(2)
        
        fLayout = QFormLayout()
        fLayout.addRow('编辑框1', self.lineEdit1)
        fLayout.addRow('编辑框2', self.lineEdit2)
        
        mainLayout.addWidget(btnChangeFocus)
        mainLayout.addLayout(fLayout)
 
        mainWidget.setLayout(mainLayout)
        self.setCentralWidget(mainWidget)
        
    def onButtonChangeFocus(self):
        self.lineEdit2.setFocus()
        
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = FocusSetDemo()
    window.show()
    sys.exit(app.exec())

运行结果如下图:

快捷键设置

快捷键(也称热键)是GUI程序交互操作的一种常见功能,在一个GUI应用的系统菜单里,我们常常可以见到菜单项有对应的快捷键操作。快捷键用来快速设置输入焦点,要设置快捷键,可以在相应的字母上加上“&”,程序运行后,会在相应的字母下加下划线以提示用户。按下Alt+带下划线的字母,对应的控件即可获得输入焦点,执行相应动作。

对于编辑框这类没有文本的控件,在设置快捷键时,可以创建一个QLabel对象,并通过调用setBuddy(component)与控件相关联。如果不创建QLabel对象,则可以使用QWidget的下列的方法来设置和管理快捷键:

  • grabShortcut (keys [,context]): 登记快捷键,返回一个标识符。参数keys为QtGui.QKeySequence对象。设置控制键+快捷键的Keys设置方式为:
    • QKeySequence.mnemonic("&e")
    • QKeySequence("Alt+e")
    • QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_E)

context参数可以是Qt.ShortcutContext中的枚举变量:WidgetShortcut、WidgetWithChildrenShortcut、WindowsShortcut(默认值 )和ApplicationShortcut。

  • releaseshortcut (id) - 删除标识符为id的组合键;
  • setShortcutEnabled(id [,flag]): flag为True时,标识符为id的组合键有效;否则无效。

按下快捷键时,产生QEvent.Shortcut事件,可在函数event(self,event)中处理。event参数是QShortcutEvent对象,有以下方法:

  • shortcutId(): 返回快捷键的标识符;
  • isAmbiguous(): 如果事件同时发送给几个控件,返回True;否则,返回Fasle;
  • key():返回代表所按下快捷键的QkeySequence对象。

快捷键设置测试

测试代码演示了直接为按钮设置快捷键,使用QLabel.setBuddy()方法为编辑框1设置快捷键和使用QWidget. grabShortcut为编辑框2设置快捷键,完整演示代码如下:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QLabel,
                             QLineEdit, QPushButton, QVBoxLayout, QFormLayout)
 
class MyLineEdit(QLineEdit):
    def __init__(self, parent = None):
        super(MyLineEdit, self).__init__(parent)
        self.id = None
        
    def event(self, evt):
        if evt.type() == QEvent.Shortcut:
            if self.id == evt.shortcutId():
                self.setFocus(Qt.ShortcutFocusReason)
                return True
        return QLineEdit.event(self, evt)
 
class FocusSetDemo(QMainWindow):
    def __init__(self, parent = None):
        super(FocusSetDemo, self).__init__(parent)
        
        # 设置窗口标题
        self.setWindowTitle('实战PyQt5: 快捷键设置演示')
        # 设置窗口大小
        self.resize(360, 240)
        
        self.initUi()
        
    def initUi(self):
        mainWidget = QWidget()
        mainLayout = QVBoxLayout()
        mainLayout.setSpacing(10)
        
        btnClearFocus = QPushButton('(&R)清除编辑框1的输入焦点')
        btnClearFocus.clicked.connect(self.onButtonClearFocus)
        
        label1 = QLabel('编辑框1(&E)')
        self.lineEdit1 = MyLineEdit()
        self.lineEdit1.setText('test1')
        label1.setBuddy(self.lineEdit1)
        
        self.lineEdit2 = MyLineEdit()
        self.lineEdit2.setText('test2')
        self.lineEdit2.id = self.lineEdit2.grabShortcut(QKeySequence.mnemonic('&D'))
        
        fLayout = QFormLayout()
        fLayout.addRow(label1, self.lineEdit1)
        fLayout.addRow('编辑框2', self.lineEdit2)
        
        mainLayout.addWidget(btnClearFocus)
        mainLayout.addLayout(fLayout)
 
        mainWidget.setLayout(mainLayout)
        self.setCentralWidget(mainWidget)
        
    def onButtonClearFocus(self):
        self.lineEdit1.clearFocus()
        
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = FocusSetDemo()
    window.show()
    sys.exit(app.exec())

运行结果如下图所示:

按键事件

按下并释放键盘按键时,以下方法将被调用:

  • keyPressEvent(self,event): 按下某一键时,该方法被调用直到键被释放为止;
  • keyReleaseEvent(self,event) - 释放之前按下的键时被调用。

上述方法中的event参数为QKeyEvent对象,其常用方法有:

  • key(): 返回按下键的值;
  • text(): 返回按下键的Unicode字符编码信息,当按键为Shift, Control, Alt等时,则该函数返回的字符为空值。
  • modifiers(): 判断按下了哪些修饰键(Shift,Ctrl,Alt,等等)。返回值为Qt. KeyboardModifier 中下列枚举变量的组合:
    • NoModifier - 没有修饰键;
    • ShiftModifier - Shift修饰键;
    • ControlModifier - Ctrl修饰键;
    • AltModifier - Alt修饰键;
    • MetaModifier - 组合修饰键;
    • KeypadModifier - 附加键盘上的任何按键;
    • GroupSwitchModifier - 按下键(仅限X11系统)。
  • isAutoRepeat(): 如果一直按着某键,返回True;否则,返回False;
  • match(QKeySequence.StandardKey key): 如果当前的键组合与key相同,返回True;否则,返回False。

比如,是否按下了复制快捷键的代码:

         if e.matches(QKeySequence.Copy):
                   print("组合键为",QKeySequence.Copy)

处理键盘按键时,需要注意以下几点:

  • 控件必须可以设置为输入焦点。有些控件,如QLabel是不能接受输入焦点的;
  • 捕获键盘事件要使用grabKeyboard( )方法,释放时,调用rekeaseKeyboard();
  • 可能拦截除Tab键和Shift+Tab键以外的任何键。因为默认Tab /Shift+Tab事件(切换焦点)被先捕获了, 要拦截这两个键,只能在event(self,event)中完成;
  • 如果要让父控件继续收到键盘事件,要调用事件的ignore()方法;否则,调用accept()。

测试按键事件

测试代码演示里使用keyPressEvent和event来捕获键盘按下事件,并使用paintEvent将其显示在窗口中央。完整代码如下:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtGui import QPainter, QPen, QFont
from PyQt5.QtWidgets import QApplication, QWidget
 
class DemoKeyboardEvent(QWidget):
    def __init__(self, parent=None):
        super(DemoKeyboardEvent, self).__init__(parent)
 
        # 设置窗口标题
        self.setWindowTitle('实战PyQt5: 键盘按键事件演示')
        # 设置窗口大小
        self.resize(400, 320)
        
        self.key=''
        
    #重绘窗口事件
    def paintEvent(self, event):           
        painter = QPainter(self)
        painter.setFont(QFont(self.font().family(), 36))
        painter.setPen(QPen(Qt.blue))
        painter.setRenderHint(QPainter.TextAntialiasing)
        
        #居中绘制文本信息
        painter.drawText(self.rect(), Qt.AlignCenter, self.key)  
        
        #键盘按键事件
    def keyPressEvent(self, event):
        self.key = ''           
        if event.key() == Qt.Key_Home:
            self.key = 'Home'
        elif event.key() == Qt.Key_End:
            self.key = 'End'
        elif event.key() == Qt.Key_PageUp:
            if event.modifiers() & Qt.ControlModifier:
                self.key = "Ctrl+PageUp"
            else:
                self.key = "PageUp"
        elif event.key() == Qt.Key_PageDown:
            if event.modifiers() & Qt.ControlModifier:
                self.key = "Ctrl+PageDown"
            else:
                self.key = "PageDown"
        elif Qt.Key_0 <= event.key() <= Qt.Key_9:
            self.key = event.text()
        elif Qt.Key_A <= event.key() <= Qt.Key_Z:
            if event.modifiers() & Qt.ShiftModifier:
                self.key = "Shift+"
            self.key += event.text()
        
        #如果key有字符,不为空,则绘制字符
        if self.key:
            self.update()
        #否则就继续监视这个事件
        else:
            QWidget.keyPressEvent(self, event)
    
    #Tab键由于涉及焦点切换,不会传递给keyPressEvent,因此,需要在这里重新定义。  
    def event(self, event):
        #如果有按键按下,并且按键是tab键
        if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab:
            self.key = "Tab"
            self.update()
            return True
        return QWidget.event(self, event)   
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = DemoKeyboardEvent()
    window.show()
    sys.exit(app.exec())

测试结果如下图:

本文知识点

  • 焦点事件;
  • 快捷键设置;
  • 键盘按下和释放事件;
  • 使用QPainter 绘制指定样式的文字信息。

喜欢本文内容就关注, 收藏,点赞,评论和转发。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表