23-窗口、窗口控件、对话框以及相关功能类-快捷键设置和键位序列

快捷键设置和键位序列

Qt 中专门为快捷键设定了 QKeySequence 类,它封装了快捷键使用的按键序列。

介绍3种快捷键方式:

  • 第1种是基于 QAction的快捷键,这是设计菜单栏和工具栏常用的方式;
  • 第2种是基于 QShortcut,可以对快捷键直接绑定相关的槽函数;
  • 第3种是可视化的快捷键绑定,Qt中提供了可视化快捷键绑定的类 QKeySequenceEdit,用户可以设置自己的快捷键。

这3种方式绑定快捷键都要使用 QKeySequence,从而为不同的场景绑定快捷键提供便利。

from PySide6.QtWidgets import QKeySequenceEdit


QKeySequenceEdit(keySequence: Union[PySide6.QtGui.QKeySequence,PySide6.QtCore.QKeyCombination,PySide6.QtGui.QKeySequence.StandardKey,str,int],parent: Union[PySide6.QtWidgets.QWidget,NoneType] = None)-> None
QKeySequenceEdit(parent: Union[PySide6.QtWidgets.QWidget,NoneType] = None)-> None

键位序列QKeySequence

QKeySequence类封装了快捷键所使用的键序列

QKeySequence说明
from PySide6.QtGui import QKeySequence

QKeySequence(self)-> None
QKeySequence(k1: int,k2: int=0,k3: int=0,k4: int=0)-> None
QKeySequence(k1: Union[PySide6.QtCore.QKeyCombination,PySide6.QtCore.Qt.KeyboardModifier,PySide6.QtCore.Qt.Key],k2: Union[PySide6.QtCore.QKeyCombination,PySide6.QtCore.Qt.KeyboardModifier,PySide6.QtCore.Qt.Key]=Instance(QKeyCombination.fromCombined(0)),k3: Union[PySide6.QtCore.QKeyCombination,PySide6.QtCore.Qt.KeyboardModifier,PySide6.QtCore.Qt.Key]=Instance(QKeyCombination.fromCombined(0)),k4: Union[PySide6.QtCore.QKeyCombination,PySide6.QtCore.Qt.KeyboardModifier,PySide6.QtCore.Qt.Key]=Instance(QKeyCombination.fromCombined(0)))-> None
QKeySequence(key: PySide6.QtGui.QKeySequence.StandardKey)-> None
QKeySequence(key: str,format: PySide6.QtGui.QKeySequence.SequenceFormat=Instance(PySide6.QtGui.QKeySequence.SequenceFormat.NativeText))-> None
QKeySequence(ks: Union[PySide6.QtGui.QKeySequence,PySide6.QtCore.QKeyCombination,PySide6.QtGui.QKeySequence.StandardKey,str,int])-> None

在最常见的形式中,键序列描述了必须一起使用才能执行某些操作的键的组合。键序列与QAction对象一起使用,以指定可以使用哪些键盘快捷键来触发动作。
可以通过三种不同的方式构造键序列以用作键盘快捷键:

  • 对于标准快捷方式,可以使用标准密钥来请求与每个快捷方式相关联的平台特定的密钥序列。
  • 对于自定义快捷方式,可以使用诸如 “Ctrl X” 之类的人类可读字符串,并且可以将其转换为适用于不同语言用户的适当快捷方式。翻译是在 “QShortcut” 上下文中进行的。
  • 对于硬编码的快捷方式,可以使用由键和KeyboardModifier枚举值定义的值的组合来指定整数键码。每个键代码由单个键值和零个或多个修饰符组成,例如shiftmodifidifier,ControlModifier,AltModifier和元修饰符。

例如,Ctrl P可能是用作打印文档的快捷方式的序列,可以通过以下任何一种方式指定:

QKeySequence(QKeySequence.Print)
QKeySequence(tr("Ctrl+P"))
QKeySequence(tr("Ctrl+p"))
QKeySequence(Qt.CTRL | Qt.Key_P)
QKeySequence(Qt.CTRL + Qt.Key_P) # deprecated

请注意,对于字母,规范字符串中使用的大小写无关紧要。在上述示例中,用户不需要按住Shift键来激活用 “Ctrl P” 指定的快捷方式。但是,对于其他键,将Shift用作未指定的额外修饰键可能会导致应用程序的用户的混淆,该应用程序的键盘的布局与开发人员使用的布局不同。有关更多详细信息,请参见下面的键盘布局问题部分。

最好在可能的情况下使用标准快捷方式。在为非标准快捷方式创建关键序列时,应优先使用人类可读的字符串而不是硬编码的整数值。

可以将QKeySequence对象强制转换为QString,以获得该序列的人类可读的翻译版本。同样,toString() 函数产生人类可读的字符串,用于菜单。在macOS上,适当的符号用于描述使用Macintosh键盘上的特殊键的键盘快捷键。

指定硬编码密钥代码的另一种方法是使用字符的Unicode代码点; 例如,‘A’ 给出与Key_A相同的密钥序列。

在macOS上,对 “Ctrl” 、CTRL、Key_Control和ControlModifier的引用对应于Macintosh键盘上的命令键,对 “Meta” 、META、Key_Meta和MetaModifier的引用对应于控制键。macOS上的开发人员可以在所有平台上使用相同的快捷方式描述,并且他们的应用程序将自动在macOS上按预期工作。

标准的快捷键

QKeySequence定义了许多标准的键盘快捷键,以减少在典型应用程序中设置操作时所需的工作量。

下表显示了四个广泛使用的平台上的应用程序经常用于这些标准快捷键的一些常见键序列。注意,在macOS上,Ctrl值对应于Macintosh键盘上的命令键,而Meta值对应于控制键。

请注意,由于用于标准快捷键的键序列在不同平台之间是不同的,因此您仍然需要在每个平台上测试您的快捷键,以确保您不会无意中将相同的键序列分配给许多操作。

请注意,键绑定取决于平台。当前绑定的快捷方式可以使用 keyBindings()进行查询。

PySide6.QtGui.QKeySequence.StandardKey枚举值:

StandardKeyWindowsmacOSKDE PlasmaGNOME
HelpContentsF1Ctrl+?F1F1
WhatsThisShift+F1Shift+F1Shift+F1Shift+F1
OpenCtrl+OCtrl+OCtrl+OCtrl+O
CloseCtrl+F4,Ctrl+WCtrl+W,Ctrl+F4Ctrl+WCtrl+W
SaveCtrl+SCtrl+SCtrl+SCtrl+S
QuitCtrl+QCtrl+QCtrl+Q
SaveAsCtrl+Shift+SCtrl+Shift+S
NewCtrl+NCtrl+NCtrl+NCtrl+N
DeleteDelDel,Meta+DDel,Ctrl+DDel,Ctrl+D
CutCtrl+X,Shift+DelCtrl+X,Meta+KCtrl+X,F20,Shift+DelCtrl+X,F20,Shift+Del
CopyCtrl+C,Ctrl+InsCtrl+CCtrl+C,F16,Ctrl+InsCtrl+C,F16,Ctrl+Ins
PasteCtrl+V,Shift+InsCtrl+V,Meta+YCtrl+V,F18,Shift+InsCtrl+V,F18,Shift+Ins
PreferencesCtrl+,
UndoCtrl+Z,Alt+BackspaceCtrl+ZCtrl+Z,F14Ctrl+Z,F14
RedoCtrl+Y,Shift+Ctrl+Z,Alt+Shift+BackspaceCtrl+Shift+ZCtrl+Shift+ZCtrl+Shift+Z
BackAlt+Left,BackspaceCtrl+[Alt+LeftAlt+Left
ForwardAlt+Right,Shift+BackspaceCtrl+]Alt+RightAlt+Right
RefreshF5F5F5Ctrl+R,F5
ZoomInCtrl+PlusCtrl+PlusCtrl+PlusCtrl+Plus
ZoomOutCtrl+MinusCtrl+MinusCtrl+MinusCtrl+Minus
FullScreenF11,Alt+EnterCtrl+Meta+FF11,Ctrl+Shift+FCtrl+F11
PrintCtrl+PCtrl+PCtrl+PCtrl+P
AddTabCtrl+TCtrl+TCtrl+Shift+N,Ctrl+TCtrl+T
NextChildCtrl+Tab,Forward,Ctrl+F6Ctrl+},Forward,Ctrl+TabCtrl+Tab,Forward,Ctrl+CommaCtrl+Tab,Forward
PreviousChildCtrl+Shift+Tab,Back,Ctrl+Shift+F6Ctrl+{,Back,Ctrl+Shift+TabCtrl+Shift+Tab,Back,Ctrl+PeriodCtrl+Shift+Tab,Back
FindCtrl+FCtrl+FCtrl+FCtrl+F
FindNextF3,Ctrl+GCtrl+GF3Ctrl+G,F3
FindPreviousShift+F3,Ctrl+Shift+GCtrl+Shift+GShift+F3Ctrl+Shift+G,Shift+F3
ReplaceCtrl+H(none)Ctrl+RCtrl+H
SelectAllCtrl+ACtrl+ACtrl+ACtrl+A
DeselectCtrl+Shift+ACtrl+Shift+A
BoldCtrl+BCtrl+BCtrl+BCtrl+B
ItalicCtrl+ICtrl+ICtrl+ICtrl+I
UnderlineCtrl+UCtrl+UCtrl+UCtrl+U
MoveToNextCharRightRight,Meta+FRightRight
MoveToPreviousCharLeftLeft,Meta+BLeftLeft
MoveToNextWordCtrl+RightAlt+RightCtrl+RightCtrl+Right
MoveToPreviousWordCtrl+LeftAlt+LeftCtrl+LeftCtrl+Left
MoveToNextLineDownDown,Meta+NDownDown
MoveToPreviousLineUpUp,Meta+PUpUp
MoveToNextPagePgDownPgDown,Alt+PgDown,Meta+Down,Meta+PgDown,Meta+VPgDownPgDown
MoveToPreviousPagePgUpPgUp,Alt+PgUp,Meta+Up,Meta+PgUpPgUpPgUp
MoveToStartOfLineHomeCtrl+Left,Meta+LeftHomeHome
MoveToEndOfLineEndCtrl+Right,Meta+RightEnd,Ctrl+EEnd,Ctrl+E
MoveToStartOfBlock(none)Alt+Up,Meta+A(none)(none)
MoveToEndOfBlock(none)Alt+Down,Meta+E(none)(none)
MoveToStartOfDocumentCtrl+HomeCtrl+Up,HomeCtrl+HomeCtrl+Home
MoveToEndOfDocumentCtrl+EndCtrl+Down,EndCtrl+EndCtrl+End
SelectNextCharShift+RightShift+RightShift+RightShift+Right
SelectPreviousCharShift+LeftShift+LeftShift+LeftShift+Left
SelectNextWordCtrl+Shift+RightAlt+Shift+RightCtrl+Shift+RightCtrl+Shift+Right
SelectPreviousWordCtrl+Shift+LeftAlt+Shift+LeftCtrl+Shift+LeftCtrl+Shift+Left
SelectNextLineShift+DownShift+DownShift+DownShift+Down
SelectPreviousLineShift+UpShift+UpShift+UpShift+Up
SelectNextPageShift+PgDownShift+PgDownShift+PgDownShift+PgDown
SelectPreviousPageShift+PgUpShift+PgUpShift+PgUpShift+PgUp
SelectStartOfLineShift+HomeCtrl+Shift+LeftShift+HomeShift+Home
SelectEndOfLineShift+EndCtrl+Shift+RightShift+EndShift+End
SelectStartOfBlock(none)Alt+Shift+Up,Meta+Shift+A(none)(none)
SelectEndOfBlock(none)Alt+Shift+Down,Meta+Shift+E(none)(none)
SelectStartOfDocumentCtrl+Shift+HomeCtrl+Shift+Up,Shift+HomeCtrl+Shift+HomeCtrl+Shift+Home
SelectEndOfDocumentCtrl+Shift+EndCtrl+Shift+Down,Shift+EndCtrl+Shift+EndCtrl+Shift+End
DeleteStartOfWordCtrl+BackspaceAlt+BackspaceCtrl+BackspaceCtrl+Backspace
DeleteEndOfWordCtrl+Del(none)Ctrl+DelCtrl+Del
DeleteEndOfLine(none)(none)Ctrl+KCtrl+K
DeleteCompleteLine(none)(none)Ctrl+UCtrl+U
InsertParagraphSeparatorEnterEnterEnterEnter
InsertLineSeparatorShift+EnterMeta+Enter,Meta+OShift+EnterShift+Enter
Backspace(none)Meta+H(none)(none)
CancelEscapeEscape,Ctrl+.EscapeEscape

请注意,由于用于标准快捷方式的密钥序列在不同平台之间有所不同,因此您仍然需要在每个平台上测试您的快捷方式,以确保您不会无意中将相同的密钥序列分配给许多操作。

如果不了解表中的内容,但想在代码中知道QKeySequence.XXX对应的按则可以使用如下方式:

from PySide6.QtGui import QKeySequence

print(QKeySequence.keyBindings(QKeySequence.Copy))


# [QKeySequence(Ctrl+C),OKeySequence(Ctrl+Ins)]

许多按键序列规格是由开发人员根据某些类型的键盘布局来选择的,而不是选择代表动作名称首字母的按键,如Ctrl S(“Ctrl+S”)或Ctrl C(“Ctrl+C”)。此外,由于某些符号只能在某些键盘布局上的修饰键的帮助下输入,因此用于一种键盘布局的键序列可能会映射到不同的键,甚至映射到根本没有键,或者需要在不同的键盘布局上使用额外的修饰键。
例如,快捷键Ctrl+和Ctrl -在图形应用程序中经常被用作缩放操作的快捷键,它们可以分别指定为"Ctrl++“和"Ctrl±”。但是,这些快捷键的指定和解释方式取决于键盘布局。挪威键盘的用户会注意到,键盘上的+和-键并不相邻,但仍然可以激活两个快捷键而不需要按Shift键。然而,使用英式键盘的用户需要按住Shift键才能输入+符号,使快捷键实际上与"Ctrl+Shift+="相同。
尽管有些开发人员可能会完全指定他们在键盘上使用的所有修饰符来激活快捷键,但这也会导致使用不同键盘布局的用户出现意想不到的行为。
例如,使用英式键盘的开发人员可能决定指定"Ctrl+Shift+="作为键序列,以便创建一个与Ctrl+相同的快捷键。然而,=键需要使用挪威键盘上的Shift键来访问,使得所需的快捷键有效地Ctrl Shift Shift =(一个不可能的键组合)。
因此,在指定可用于各种不同键盘布局的键序列时,人类可读的字符串和硬编码的键代码都可能存在问题。只有使用标准的快捷方式才能保证用户能够使用开发人员希望使用的快捷方式。
尽管如此,我们可以通过确保使用人类可读的字符串来解决这个问题,从而可以为不同语言的用户翻译键序列。这种方法对于那些键盘具有最典型的语言布局的用户是成功的。

键盘布局问题

开发人员根据某些类型的键盘的布局来选择许多键序列规范,而不是选择表示动作名称的第一个字母的键,例如Ctrl S (“Ctrl S”) 或Ctrl C (“Ctrl C”)。另外,由于某些符号只能在某些键盘布局上的修饰键的帮助下输入,因此旨在与一个键盘布局一起使用的键序列可能映射到不同的键,根本不映射到任何键,或者需要在不同的键盘布局上使用附加的修饰键。
例如,快捷键 (Ctrl + 和Ctrl减) 通常用作图形应用程序中缩放操作的快捷键,并且它们可以分别指定为 “Ctrl” 和 “Ctrl -”。但是,这些快捷方式的指定和解释方式取决于键盘布局。挪威键盘的用户会注意到,和键在键盘上不相邻,但仍然能够激活这两个快捷方式,而无需按下Shift键。但是,使用英国键盘的用户将需要按住Shift键才能输入符号,从而使快捷方式实际上与 “Ctrl Shift =” 相同。
尽管一些开发人员可能会完全指定他们在键盘上使用的所有修饰符来激活快捷方式,但这也将导致不同键盘布局的用户出现意外行为。
例如,使用英国键盘的开发人员可以决定将 “Ctrl Shift =” 指定为键序列,以便创建与Ctrl plus一致的快捷方式。但是,需要使用挪威键盘上的Shift键访问 = 键,从而有效地使所需的快捷方式Ctrl Shift Shift = (不可能的组合键)。
结果,当指定可在各种不同键盘布局上使用的键序列时,人类可读的字符串和硬编码的键代码在使用上都可能存在问题。只有使用标准快捷方式才能保证用户能够使用开发人员想要的快捷方式。
尽管如此,我们可以通过确保使用人类可读的字符串来解决此问题,从而可以为不同语言的用户进行密钥序列的翻译。对于键盘具有最典型的语言布局的用户,这种方法将是成功的。

GNU Emacs风格密钥序列

与GNU Emacs中使用的密钥序列相似,最多允许四个密钥代码,可以通过使用多参数构造函数或通过传递逗号分隔的密钥序列的人类可读字符串来创建。

例如,可以使用以下任一方式指定键序列Ctrl X后跟Ctrl C:

QKeySequence(tr("Ctrl+X, Ctrl+C"))
QKeySequence(Qt.CTRL | Qt.Key_X, Qt.CTRL | Qt.Key_C)
QKeySequence(Qt.CTRL + Qt.Key_X, Qt.CTRL + Qt.Key_C) # deprecated

在创建QKeySequence之前,必须已经构建了QApplication实例; 否则,您的应用程序可能会崩溃。

键位序列QKeySequence方法
方法描述
count()–>int返回键序列中的键数。最大值为4。
isEmpty()–>bool如果键序列为空则返回true;否则返回false。
matches(seq:PySide6.QtGui.QKeySequence)–>PySide6.QtGui.QKeySequence.SequenceMatch用seq匹配序列,看是否和实例化的按键序列相同。
如果成功返回ExactMatch(枚举值见下方)
如果seq匹配不完全返回PartialMatch
如果序列没有任何共同之处则返回NoMatch。
如果seq较短,则返回NoMatch。
swap(other:PySide6.QtGui.QKeySequence)将其他密钥序列与此密钥序列交换。这个操作非常快,从来不会失败。
toString([format=QKeySequence.SequenceFormat.PortableText])–>str根据格式SequenceFormat(枚举值见下方)返回键序列的字符串表示形式。例如值CTRL + Key_O的结果是"CTRL +O"。
如果键序列有多个键代码,则在返回的字符串中以逗号分隔,例如"Alt+X,Ctrl+Y,Z"。字符串,“Ctrl”,"Shift"等在"QShortcut"上下文中使用tr()进行转换。
如果键序列没有键,则返回空字符串。
在macOS上,如果格式为NativeText,返回的字符串类似于菜单栏中显示的序列;否则,字符串使用"便携式"格式,适合写入文件。
[Static]fromString(str[,format=QKeySequence.SequenceFormat.PortableText])–>PySide6.QtGui.QKeySequence根据格式从字符串str返回一个QKeySequence。
[Static]keyBindings(key:PySide6.QtGui.QKeySequence.StandardKey)–>PySide6.QtGui.QKeySequence.StandardKey | List[PySide6.QtGui.QKeySequence.StandardKey返回给定键的键绑定列表。调用此函数的结果将根据目标平台而有所不同。
列表的第一个元素表示给定平台的主要快捷方式。
如果结果包含多个结果,则可以将这些结果视为给定键在同一平台上的可选快捷方式。
[Static]listFromString(str[,format=QKeySequence.SequenceFormat.PortableText])–>List[PySide6.QtGui.QKeySequence]根据格式从字符串str返回一个QKeySequence列表。
[Static]listToString(list[,format=QKeySequence.SequenceFormat.PortableText])–>str根据格式返回list的字符串表示形式。
[Static]mnemonic(text: str)–>PySide6.QtGui.QKeySequence返回文本中助记符的快捷键序列,如果没有找到助记符,则返回空键序列。
例如:
mnemonic(“E&xit”)返回ALT+Key_X
mnemonic(“&Quit”)返回ALT+Key_Q
mnemonic(“Quit”)返回空的QKeySequence。
__le__(other:PySide6.QtGui.QKeySequence)–>bool如果这个键序列小于或等于另一个键序列,则返回true;否则返回false。
__ne__(other:PySide6.QtGui.QKeySequence)–>bool如果这个键序列不等于另一个键序列,则返回true;否则返回false。
__lt__(ks:PySide6.QtGui.QKeySequence)提供此键序列与其他键序列的任意比较。唯一能保证的是,如果两个键序列相等,运算符返回false;如果两个键序列不相等,则(ks1 < ks2)== !(ks2 < ks1)。
这个函数在某些情况下很有用,例如如果您想使用QKeySequence对象作为QMap中的键。
__eq__(other:PySide6.QtGui.QKeySequence)–>bool如果这个键序列等于另一个键序列,则返回true;否则返回false。
__gt__(other:PySide6.QtGui.QKeySequence)–>bool如果这个键序列比另一个键序列大,则返回true;否则返回false。
__ge__(other:PySide6.QtGui.QKeySequence)–>bool如果这个键序列大于或等于另一个键序列,则返回true;否则返回false。

PySide6.QtGui.QKeySequence.SequenceMatch枚举值:

枚举值描述
QKeySequence.NoMatch按键序列不同;甚至不是部分匹配。
QKeySequence.PartialMatch键序列部分匹配,但不相同。
QKeySequence.ExactMatch按键序列是相同的。

PySide6.QtGui.QKeySequence.SequenceFormat枚举值:

枚举值描述
QKeySequence.NativeText作为特定于平台的字符串的键序列。这意味着它将被翻译出来,在Mac上,它将类似于菜单栏中的一个键序列。当您希望将字符串显示给用户时,最好使用此枚举。
QKeySequence.PortableText键序列以"可移植"格式给出,适合于读取和写入文件。在许多情况下,它看起来类似于Windows和X11上的本机文本。

基于QAction的快捷键

from PySide6.QtGui import QAction

QAction(icon: Union[PySide6.QtGui.QIcon,PySide6.QtGui.QPixmap],text: str,parent: Union[PySide6.QtCore.QObject,NoneType]=None)-> None
QAction(parent: Union[PySide6.QtCore.QObject,NoneType]=None)-> None
QAction(text: str,parent: Union[PySide6.QtCore.QObject,NoneType]=None)-> None

在Qt 中,QKeySequence 一般与 QAction 对象一起使用,以指定使用哪些快捷键来触发操作。在 Qt 中,支持3 种绑定快捷键的方式,下面以 Ctrl+N 为例展开介绍(如下3种方式的效果是一样的)。

  • 标准快捷键:

    newAct = QAction(QIcon("./images/new.png"),"&New")
    newAct.setShortcuts(QKeySequence.New)
    
  • 自定义快捷键:

    newAct = QAction(QIcon("./images/new.png"),"&New")
    newAct.setShortcuts("Ctrl+N")
    
  • 来自 Qt 的快捷键:

    newAct = QAction(QIcon("./images/new.png"),"&New")
    newAct.setShortcuts(QKeySequence(Qt.CTRL | Qt.Key_N))
    

这里使用的是setShortcuts()函数,如果这个函数不工作,则可以尝试使用setShortcut(PySide6.QtGui.QKeySequence.StandardKey枚举值)函数,前者传递一个快捷键列表,后者传递一个快捷键,这样使用也没有问题。

基于QShortcut的快捷键

from PySide6.QtGui import QShortcut

QShortcut(arg__1: PySide6.QtGui.QKeySequence.StandardKey,arg__2: PySide6.QtCore.QObject,arg__3: Callable,arg__4: PySide6.QtCore.Qt.ShortcutContext = Instance(Qt.WindowShortcut))-> None
QShortcut(arg__1: Union[PySide6.QtGui.QKeySequence,PySide6.QtCore.QKeyCombination,PySide6.QtGui.QKeySequence.StandardKey,str,int],arg__2: PySide6.QtCore.QObject,arg__3: Callable,arg__4: PySide6.QtCore.Qt.ShortcutContext = Instance(Qt.WindowShortcut))-> None
QShortcut(key: PySide6.QtGui.QKeySequence.StandardKey,parent: PySide6.QtCore.QObject,member: Union[bytes,NoneType] = None,ambiguousMember: Union[bytes,NoneType] = None,context: PySide6.QtCore.Qt.ShortcutContext = Instance(Qt.WindowShortcut))-> None
QShortcut(key: Union[PySide6.QtGui.QKeySequence,PySide6.QtCore.QKeyCombination,PySide6.QtGui.QKeySequence.StandardKey,str,int],parent: PySide6.QtCore.QObject,member: Union[bytes,NoneType] = None,ambiguousMember: Union[bytes,NoneType] = None,context: PySide6.QtCore.Qt.ShortcutContext = Instance(Qt.WindowShortcut))-> None
QShortcut(parent: PySide6.QtCore.QObject)-> None

QShortcut类用于创建键盘快捷键。

如果想通过菜单栏、工具栏之外的方式设置快捷键,就可以使用 QShortcut 来实现,代码如下(实现了对 Ctrl+E 自定义快捷键的绑定):

# 自定义快捷键
from PySide6.QtGui import QShortcut,QKeySequence

custom_shortcut = QShortcut(QKeySequence("Ctrl+E"),self)
custom_shortcut.activated.connect(lambda: self.customShortcut(custom_shortcut))


def customShortcut(self,key):
    self.label.setText(f"触发自定义快捷键:{key.keys()}")
QShortcut属性
属性描述
autoRepeat : bool此属性用于确定快捷方式是否可以自动重复。
如果为true,当按下键盘快捷键组合时,该快捷键将自动重复,前提是系统上启用了键盘自动重复。缺省值为true。
context :PySide6.QtGui.QShortcut.context此属性保存快捷方式有效的上下文。
快捷方式的上下文决定了在什么情况下允许触发快捷方式。正常的上下文是WindowShortcut,它允许在父窗口(包含快捷方式的窗口小部件)是活动顶级窗口的子窗口小部件时触发快捷方式。
默认情况下,此属性被设置为WindowShortcut。
获取方法:
context()
setContext(context)
enabled :bool此属性保存是否启用快捷方式。
当QShortcutEvent事件与快捷方式的key()序列匹配时,启用的快捷方式会发出activated()或activatedambiguous()信号。
如果应用程序处于WhatsThis模式,快捷方式将不会发出信号,但将显示"这是什么?"用短信代替。
默认情况下,此属性为true。
key: PySide6.QtGui.QKeySequence此属性保存快捷键的主键序列。
这是一个具有Shift,Ctrl和Alt可选组合的键序列。键序列可以通过多种方式提供:
setKey(0)# 没有信号发出
setKey(QKeySequence())# 没有信号发出
setKey(0x3b1)# 希腊字母α
setKey(Qt.Key_D)# 删除例子
setKey(‘q’)# 字母Q
setKey(Qt.CTRL # Ctrl+P
setKey(“Ctrl+P”)# Ctrl+P
默认情况下,此属性包含一个空键序列。
QShortcut方法
方法描述
autoRepeat() -->bool属性autoRepeat的值
context() -->PySide6.QtCore.PySide6.QtCore.Qt.ShortcutContext属性context的值
id() -->int返回主键绑定的ID。
isEnabled() -->bool属性enabled的值
key() -->PySide6.QtGui.QKeySequence属性key的值
keys() -->List[PySide6.QtGui.QKeySequence]返回触发此快捷方式的键序列列表
setAutoRepeat(on:bool)设置属性autoRepeat
setContext(context:PySide6.QtCore.PySide6.QtCore.Qt.ShortcutContext)设置属性context
setEnabled(enable:bool)设置属性enabled
setKey(key:PySide6.QtGui.QKeySequence)设置属性key
setKeys(key:PySide6.QtGui.QKeySequence.StandardKey)将快捷键触发设置为标准键快捷键StandardKey
即设置为PySide6.QtGui.QKeySequence.StandardKey
setKeys(keys: List[PySide6.QtGui.QKeySequence])设置多个快捷键
setWhatsThis(text:str)设置快捷方式"这是什么?"帮助文本。
当小部件应用程序在"What’s This?"模式,用户输入快捷键()序列。
设置"这是什么?"菜单项上的帮助(有或没有快捷键),设置该项操作上的帮助。
默认情况下,帮助文本是一个空字符串。
此函数在不使用小部件的应用程序中不起作用。
whatsThis()–>str返回快捷方式"What’s This?"帮助文本。
QShortcut信号
信号描述
activated()当用户键入快捷键序列时发出此信号。
activatedAmbiguously()当在键盘上键入一个键序列时,只要它与多个快捷键的开头匹配,就被认为是模糊的。
当快捷键的键序列完成时,如果键序列仍然是不明确的(即,它是一个或多个其他快捷键的开始),则会触发activatedambiguous()。
在这种情况下,激活()信号不会被触发。
QShortcut例子

下案例用于演示QShortcut、QKeySequence等的使用方法,代码如下:

# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/29 19:03
# File_name: 01-QShortcut和QKeySequence案例.py


import sys
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *


class QShortcutDemo(QMainWindow):
    def __init__(self,parent=None):
        super(QShortcutDemo,self).__init__(parent)
        widget = QWidget(self)
        self.setCentralWidget(widget)
        layout = QVBoxLayout()
        widget.setLayout(layout)
        _label = QLabel('既可以触发菜单快捷键,也可以通过Ctrl+E触发自定义快捷键')
        self.label = QLabel('显示信息')
        layout.addWidget(_label)
        layout.addWidget(self.label)

        bar = self.menuBar()
        file = bar.addMenu("File")
        file.addAction("New")

        #  快捷键1
        save = QAction("Save",self)
        save.setShortcut("Ctrl+S")
        file.addAction(save)

        # 快捷键2
        copy = QAction('Copy',self)
        copy.setShortcuts(QKeySequence.Copy)
        file.addAction(copy)

        # 快捷键3
        paste = QAction('Paste',self)
        # paste.setShortcuts(Qt.CTRL|Qt.Key_P)
        paste.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_P))
        # paste.setShortcuts(QKeySequence(Qt.CTRL|Qt.Key_P))
        file.addAction(paste)

        quit = QAction("Quit",self)
        file.addAction(quit)
        file.triggered[QAction].connect(self.action_trigger)

        # 自定义快捷键
        custom_shortcut = QShortcut(QKeySequence("Ctrl+E"),self)
        custom_shortcut.activated.connect(lambda: self.customShortcut(custom_shortcut))

        self.setWindowTitle("QShortcut 例子")
        self.resize(450,200)

    def customShortcut(self,key):
        self.label.setText('触发自定义快捷键:%s'% key.keys())

    def action_trigger(self,q):
        self.label.setText('触发菜单:%s;快捷键:%s'%(q.text(),q.shortcuts()))


if __name__ =='__main__':
    app = QApplication(sys.argv)
    demo = QShortcutDemo()
    demo.show()
    sys.exit(app.exec())

基于keySequenceEdit的快捷键

有时用户有自己设置快捷键的需求,如实现 QQ等软件自定义快捷键功能,这就涉及QKeySequenceEdit

当小部件获得焦点时开始录制用户输入的快捷键,并在用户释放按键1秒后结束。

from PySide6.QtWidgets import QKeySequenceEdit

QKeySequenceEdit(keySequence: Union[PySide6.QtGui.QKeySequence,PySide6.QtCore.QKeyCombination,PySide6.QtGui.QKeySequence.StandardKey,str,int],parent: Union[PySide6.QtWidgets.QWidget,NoneType] = None)-> None
QKeySequenceEdit(parent: Union[PySide6.QtWidgets.QWidget,NoneType] = None)-> None

上面已经介绍了 QKeySequence 及QAction 的快捷键,下面直接通过代码讲解。

该案例实现了通过 QKeySequenceEdit 对 save 菜单和 copy 菜单绑定用户自定义快捷键的功能。

运行代码,执行一些操作,结果如下。这部分内容显示笔者对 save 菜单绑定了Ctrl+S 快捷键,对 copy 菜单绑定了 Ctrl+C 快捷键,如果使用 Ctrl+C 快捷键就会触发 copy 菜单

主要代码如下。当用户输入完快捷键1秒后,会触发 keySequenceChanged 信号,并把用户输入的快捷键QKeySequence 作为参数发送出去。这里把用户输入的快捷键与对应菜单进行绑定,并发送给 show_key()函数,更新当前状态的信息

show_kev()函数在状态栏和 text_show(QTextBrowser)中更新了用户输入快捷键的信息

更新成功后,在text_show()函数中触发绑定的快捷键会触发相应的菜单

# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/29 19:08
# File_name: 02-QKeySequenceEdit的用法.py


import sys
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *


class KeySequenceEdit(QMainWindow):
    def __init__(self,parent=None):
        super(KeySequenceEdit,self).__init__(parent)

        # 基本框架
        label1 = QLabel('菜单save快捷键绑定:')
        self.keyEdit1 = QKeySequenceEdit(self)
        label2 = QLabel('菜单copy快捷键绑定:')
        self.keyEdit2 = QKeySequenceEdit(self)
        layout1 = QHBoxLayout()
        layout1.addWidget(label1)
        layout1.addWidget(self.keyEdit1)
        layout2 = QHBoxLayout()
        layout2.addWidget(label2)
        layout2.addWidget(self.keyEdit2)
        self.label_show = QLabel('显示按键信息')
        self.text_show = QTextBrowser()
        self.text_show.setMaximumHeight(60)

        # 信号与槽绑定
        # self.keyEdit1.editingFinished.connect(lambda :print('输入完毕1'))
        # self.keyEdit2.editingFinished.connect(lambda :print('输入完毕2'))
        self.keyEdit1.keySequenceChanged.connect(lambda key: self.save.setShortcut(key))
        self.keyEdit2.keySequenceChanged.connect(lambda key: self.copy.setShortcut(key))
        self.keyEdit1.keySequenceChanged.connect(self.show_key)
        self.keyEdit2.keySequenceChanged.connect(self.show_key)

        # 菜单栏
        bar = self.menuBar()
        file = bar.addMenu("File")
        file.addAction("New")
        self.save = QAction("Save",self)
        file.addAction(self.save)
        self.copy = QAction('Copy',self)
        file.addAction(self.copy)
        file.triggered[QAction].connect(lambda q: self.statusBar().showMessage('触发菜单:%s;快捷键:%s'%(q.text(),q.shortcuts()),3000))

        # 布局管理
        layout = QVBoxLayout()
        layout.addLayout(layout1)
        layout.addLayout(layout2)
        layout.addWidget(self.label_show)
        layout.addWidget(self.text_show)
        widget = QWidget(self)
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.resize(300,200)

    def show_key(self,key: QKeySequence):
        self.statusBar().showMessage('更新快捷键'+ str(key),2000)
        key1 = self.keyEdit1.keySequence()
        key2 = self.keyEdit2.keySequence()
        _str = f'菜单栏快捷键更新成功;\nsave绑定:{key1}\ncopy绑定:{key2}'
        # self.label_show.setText(_str)
        self.text_show.setText(_str)


if __name__ =='__main__':
    app = QApplication(sys.argv)
    demo = KeySequenceEdit()
    demo.show()
    sys.exit(app.exec())

全局热键

程序处于后台也能触发热键即全局热键。pyside6似乎没有直接设置全局热键的方法,不过可以曲线救国利用pynput实现同样功能的全局热键。使用前需要先安装pynput模块

以下分别从键盘监控和全局热键两个角度尝试全局热键。

键盘显示器的一个常见用例是对全局热键做出反应。由于 监听器不维护任何状态,涉及多个键的热键必须 将此状态存储在某处。

Pynput为此提供了类。它 包含两种更新状态的方法pynput.keyboard.HotKeypynput.keyboard.GlobalHotKeys,旨在轻松互操作 带键盘监听器:可作为监听器直接传递 回调。

监听键盘

主要有两种方法,类似于鼠标。一种是对Listener的封装,用于快捷键

pynput.keyboard.Listener
  • 当两个函数中任意一个返回False还有就是释放Exception或继承自Exception的异常时,就会结束进程。
  • 键盘事件监听器是一个线程,所有的回调函数都会在独立的线程中运行。 调用pynput.keyboard.Listener.stop,发起StopException异常,或者回调函数中返回False都会停止事件的监听。
  • 可以用listener.start()listener.stop()代替with语句。键盘侦听器有回调都将是 从线程调用。threading.Thread.
from pynput import keyboard



def keyboard_listener():
	"""键盘监听"""

	def on_press(key):
		"""
		:param key: 按下的按键。pynput.keyboard.Key
		:return: 按下按键时调用该方法
		"""
		print(f"{'-'* 20}type:{type(key)}--{key}按下{'-'* 20}")

	def on_release(key):
		"""
		:param key: 按下的按键。pynput.keyboard.Key
		:return: 松开按键时回调该方法
		"""
		print(f"{'-'* 20}type:{type(key)}--{key}释放{'-'* 20}")
		if not all([self.play_status,self.record_status]):
			return False

	# 注册系统全局热键监控键盘,监控按下F10
	with pynput.keyboard.Listener(on_press=on_press,on_release=on_release)as listener:
		listener.join()

官方示例:

from pynput import keyboard

def on_press(key):
    try:
        print('alphanumeric key {0} pressed'.format(
            key.char))
    except AttributeError:
        print('special key {0} pressed'.format(
            key))

def on_release(key):
    print('{0} released'.format(
        key))
    if key == keyboard.Key.esc:
        # Stop listener
        return False

# Collect events until released
with keyboard.Listener(
        on_press=on_press,
        on_release=on_release)as listener:
    listener.join()

# ...or,in a non-blocking fashion:
listener = keyboard.Listener(
    on_press=on_press,
    on_release=on_release)
listener.start()

键盘侦听器是 a,所有回调都将是 从线程调用。threading.Thread

从任何地方调用,从回调返回以停止侦听器。pynput.keyboard.Listener.stopStopExceptionFalse

传递给回调的参数是 a,对于 特殊键,a用于普通字母数字键,或 只是未知的钥匙。keypynput.keyboard.Keypynput.keyboard.KeyCodeNone

使用上面的非阻塞版本时,当前线程将继续 执行。这在与其他 GUI 框架集成时可能是必需的 包含主循环,但是当从脚本运行时,这将导致 程序立即终止。

如果回调处理程序引发异常,侦听器将被停止。因为 回调在专用线程中运行,异常不会自动 重新提出。

要收到有关回调错误的通知,请调用侦听器 实例:Thread.join

from pynput import keyboard

class MyException(Exception): pass

def on_press(key):
    if key == keyboard.Key.esc:
        raise MyException(key)

# Collect events until released
with keyboard.Listener(
        on_press=on_press)as listener:
    try:
        listener.join()
    except MyException as e:
        print('{0} was pressed'.format(e.args[0]))
pynput.keyboard.Events
import pynput

with pynput.keyboard.Events()as event:
    for i in event:  # 迭代用法。
        key_event = i
        break

    key_event = event.get()
    # get用法可以提供一个实数作为最长等待时间单位秒,超过这个时间没有事件,就会报错。错误类型是queue模块的Empty,而非TimeoutError。

if isinstance(key_event,pynput.keyboard.Events.Press):  # 判断事件情况:
    print('按下按键',end='')
elif isinstance(key_event,pynput.keyboard.Events.Release):
    print('松开按键',end='')

# 判断按键:这个事件的`key`属性 对应才是Listener方法获得的按键`'key'
try:
    print(key_event.key.name)
except AttributeError:  # 说明这个是普通按键。
    print(key_event.key.char)
else:  # 两种判断方式,第一种是我自创的,第二种是官网上的。
    if(key_event.key.name).startswith('ctrl'):  # 通过名称判断。
        print('发生了ctrl键事件。')
    elif key_event.key is pynput.keyboard.Key.esc:
        print('发生了esc键事件。')

若要读取单个事件,请使用以下代码:

from pynput import keyboard

# The event listener will be running in this block
with keyboard.Events()as events:
    # Block at most one second
    event = events.get(1.0)
    if event is None:
        print('You did not press a key within one second')
    else:
        print('Received event {}'.format(event))

若要循环访问键盘事件,请使用以下代码

from pynput import keyboard

# The event listener will be running in this block
with keyboard.Events()as events:
    for event in events:
        if event.key == keyboard.Key.esc:
            break
        else:
            print('Received event {}'.format(event))

单个热键pynput.keyboard.HotKey

from pynput import keyboard


def on_activate():
    print('Global hotkey activated!')


def for_canonical(f):
    return lambda k: f(hot_Listener.canonical(k))


hotkey = keyboard.HotKey(keyboard.HotKey.parse('<ctrl>+<alt>+h'),on_activate)

with keyboard.Listener(on_press=for_canonical(hotkey.press),
                       on_release=for_canonical(hotkey.release))as hot_Listener:
    hot_Listener.join()

这将创建一个热键,然后使用侦听器更新其状态。

一次 所有指定的键同时按下,将 调用on_activate

请注意,密钥是在传递之前 被传递给实例。这是为了删除任何修饰符状态 从关键事件,并使用多个物理对修饰符进行规范化 按钮。pynput.keyboard.Listener.canonicalHotKey

该方法是一个方便的功能 将快捷方式字符串转换为键集合。请参阅其文档 更多信息。pynput.keyboard.HotKey.parse

还有pynput.keyboard.HotKey可以实现热键功能但是并不建议使用

HotKey用于单个热键监控

from pynput import keyboard


def on_activate():
    print('Global hotkey activated!')


def for_canonical(f):
    return lambda k: f(hot_Listener.canonical(k))


hot_keys = keyboard.HotKey.parse('<ctrl>+<alt>+h')# 把热键转化成其他形式用于监控
hotkey = keyboard.HotKey(hot_keys,on_activate)

# 当按下按键时hotkey.press更新按下的按键hotkey.release把释放按键从内置列表变量删除
with keyboard.Listener(on_press=for_canonical(hotkey.press),
                       on_release=for_canonical(hotkey.release))as hot_Listener:
    hot_Listener.join()

注册多个全局热键GlobalHotKeys

注册多个全局热键,请使用便利类:pynput.keyboard.GlobalHotKeys

多个热键监控需要手动执行GlobalHotKeys的stop停止

如下所有可以用上下文也可直接赋值进行热键监控。 需要注意start join 和stop使用时机

# -*- coding: UTF-8 -*-
# File date: Hi_2023/4/3 21:49
# File_name: 08- 注册多个全局热键-GlobalHotKeys.py


from pynput import keyboard


class keyboard_GlobalHotKeys1:

    def on_activate_h(self):
        print('<ctrl>+<alt>+h pressed')

    def on_activate_i(self):
        print('<ctrl>+<alt>+i pressed')

    def on_activate_x(self):
        print('<ctrl>+<alt>+x pressed')

    def stop(self):
		""""结束监控"""
        print('<esc>+<shift>+q pressed')
        self.h.stop()

    def listening_method(self):
        with keyboard.GlobalHotKeys({'<ctrl>+<alt>+h': self.on_activate_h,
                                  '<ctrl>+<alt>+i': self.on_activate_i,
                                  '<ctrl>+<alt>+x': self.on_activate_x,
                                  '<ctrl>+<alt>+q': self.stop})as self.h:
            self.h.join()


class keyboard_GlobalHotKeys2:
    def on_activate_h(self):
        print('<ctrl>+<alt>+h pressed')

    def on_activate_i(self):
        print('<ctrl>+<alt>+i pressed')

    def on_activate_x(self):
        print('<ctrl>+<alt>+x pressed')

    def esc_shift(self):
        print('<esc>+<shift>+q pressed')
        self.listen.stop()

    def listening_method(self):
        self.listen = keyboard.GlobalHotKeys({'<ctrl>+<alt>+h': self.on_activate_h,
                                           '<ctrl>+<alt>+i': self.on_activate_i,
                                           '<ctrl>+<alt>+x': self.on_activate_x,
                                           '<ctrl>+<alt>+q': self.esc_shift})

        self.listen.start()
        self.listen.join()


if __name__ =='__main__':
    keyboard_GlobalHotKeys1().listening_method()# 上下文写法
    keyboard_GlobalHotKeys2().listening_method()# 手动stop和start

热键思路扩展

定义多个bool变量标识按键,按键列表标识按下顺序。按下则为Ture并在列表记录,反之释放为False,在列表移除。当三个都为Ture时触发热键回调函数

同时兼顾热键和热键按下顺序

使用打包程序时,得到匿名启动ImportError

当使用打包程序如PyInstaller打包应用,打开时报ImportError

错误的原因是打包程序尝试构建依赖项 检查语句使用importlib模块。Pynput*在运行时使用查找依赖于平台的后端模块。

要解决此问题,请参阅工具的文档以查找 如何显式添加模块。

要添加哪些模块取决于您的分发平台。后端模块是以下划线’_'开头的模块在pynput.keyboard and pynput.mouse包中的。此外,还需要pynput中具有相应名称的_util模块。

# 查看发现分别调用这两个,下面例子可能需要调整没试过
from pynput._util import backend,Events # keyboard的模块
from pynput._util import backend,Events # mouse的模块

需将此模块作为隐藏导入包含在PyInstaller程序中

python -m PyInstaller your_program.py -F
hidden-import=pynput.keyboard._xorg

您还将鼠标与pynput一起使用,那么模块pynput.mouse._xorg也会出现相同的错误:

python -m PyInstaller your_program.py  -F  
hidden-import=pynput.keyboard._xorg
hidden-import=pynput.mouse._xorg

警告!如果你是为Windows或Mac打包的,你可能会得到一个它找不到的不同模块。这就是Linux的功能。如果您希望您的程序是跨平台的,那么您必须将该程序打包例如,针对Windows并对其进行测试,以查看找不到哪个模块,并将其作为隐藏导入

如果希望程序在Linux和Windows上运行,请使用以下命令:

python -m PyInstaller your_program.py  onefile  
hidden-import=pynput.keyboard._xorg  
hidden-import=pynput.mouse._xorg  
hidden-import=pynput.keyboard._win32  
hidden-import=pynput.mouse._win32

如果您有很多隐藏的模块,那么您可以编辑.spec文件并将模块添加到hiddenimports列表中,如下所示在PyInstaller 4.1上:

hiddenimports=['pynput.keyboard._xorg','pynput.mouse._xorg'],
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

士别三日,当挖目相待

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值