小部件中的键盘焦点

键盘焦点管理和处理。

Qt的小部件处理键盘焦点的方式已经成为GUI中的惯例。

基本问题是用户的按键可以指向屏幕上的多个窗口中的任何一个,以及目标窗口中的多个小部件中的任何一个。当用户按下按键时,他们期望按键能到达正确的位置,软件必须尽力满足这一期望。系统必须确定按键是针对哪个应用程序的,该应用程序中的哪个窗口,以及该窗口中的哪个小部件。

焦点运动

将键盘焦点引导到特定小部件的习俗如下:

  1. 用户按下Tab键(或Shift+Tab)。

  2. 用户点击一个小部件。

  3. 用户按下键盘快捷键。

  4. 用户使用鼠标滚轮。

  5. 用户将焦点移动到一个窗口,应用程序必须确定窗口内的哪个小部件应该获得焦点。

这些运动机制各有不同,不同类型的部件只在其中一些机制中获得焦点。我们将依次介绍每一种机制。

Tab 或 Shift+Tab

按下Tab键是迄今为止使用键盘移动焦点的最常见方式。(有时在数据输入应用程序中,Enter键的作用与Tab键相同;在Qt中,通过实现事件过滤器可以轻松实现这一点。)

在当今所有常用的窗口系统中,按下Tab键会将键盘焦点移动到每个窗口列表中的下一个小部件。Tab键沿着循环列表向一个方向移动焦点,Shift+Tab则向相反方向移动。按下Tab键从小部件移动到小部件的顺序称为Tab顺序。

您可以使用setTabOrder()自定义标签顺序。(如果不这样做,Tab通常按照小部件构建的顺序移动焦点。)Qt Widgets Designer提供了一种可视化更改标签顺序的方法。

由于按下Tab键非常常见,大多数可以获得焦点的部件应该支持Tab键焦点。主要的例外是那些很少使用的部件,以及存在某些键盘快捷键或错误处理程序可以移动焦点的情况。

例如,在一个数据输入对话框中,可能有一个字段在所有情况下只有百分之一的概率是必要的。在这样的对话框中,Tab键可以跳过这个字段,并且对话框可以使用以下机制之一:

  1. 如果程序能够确定是否需要该字段,它可以在用户完成输入并按下“确定”时,或者在用户完成其他字段后按下“Enter”时,将焦点移动到该字段。或者,将该字段包含在标签顺序中但禁用它。如果根据用户在其他字段中的设置,该字段变得合适,则启用它。

  2. 字段的标签可以包括一个键盘快捷键,用于将焦点移动到此字段。

Tab键支持的另一个例外是必须支持插入Tab键的文本输入小部件;几乎所有文本编辑器都属于这一类。Qt将Ctrl+Tab视为Tab,将Ctrl+Shift+Tab视为Shift+Tab,这些小部件可以重新实现event()并在调用event()之前处理Tab键,以获得所有其他键的正常处理。然而,由于某些系统将Ctrl+Tab用于其他目的,而且许多用户无论如何都不知道Ctrl+Tab,这并不是一个完整的解决方案。

用户点击一个小部件

这在使用鼠标或其他指向设备的计算机上可能比按Tab键更常见。

点击移动焦点比使用Tab键稍微强大一些。它不仅将焦点移动到一个小部件上,对于编辑器小部件,它还将文本光标(小部件的内部焦点)移动到鼠标点击的位置。

由于它非常常见且人们已经习惯了,因此为大多数小部件支持它是一个好主意。然而,也有一个重要的理由要避免它:你可能不希望从小部件中移除焦点。

例如,在文字处理软件中,当用户点击“B”(加粗)工具按钮时,键盘焦点应该发生什么变化?它应该保持在原来的位置,几乎肯定是在编辑小部件中,还是应该移动到“B”按钮?

我们建议支持文本输入的小部件支持点击聚焦,而对于大多数鼠标点击有不同效果的小部件则避免使用。(对于按钮,我们还建议添加键盘快捷键:QAbstractButton 及其子类使这一点非常容易实现。)

在Qt中,只有setFocusPolicy()函数影响点击聚焦。

用户按下键盘快捷键

键盘快捷键移动焦点并不罕见。这可以通过打开模态对话框隐式发生,也可以通过使用焦点加速器显式发生,例如由setBuddy()QGroupBoxQTabBar提供的那些。

您的应用程序可以支持用户可能想要跳转到的所有小部件的快捷焦点。例如,一个标签对话框可以为其每个页面设置键盘快捷键,这样用户就可以按下例如Alt+P键跳转到打印页面。请记住,这样做很容易过度,因为只有几个键,而且为命令提供键盘快捷键也很重要。请参考您目标平台的设计指南,例如微软的键盘用户界面设计指南或苹果的焦点和选择指南。

用户旋转鼠标滚轮

在Microsoft Windows上,鼠标滚轮的使用总是由具有键盘焦点的控件处理。在macOS和X11上,它由接收其他鼠标事件的控件处理。

Qt处理这种平台差异的方式是让部件在使用滚轮时移动键盘焦点。通过为每个部件设置正确的焦点策略,应用程序可以在Windows、macOS和X11上正确地以惯用方式工作。

用户将焦点移动到此窗口

在这种情况下,应用程序必须确定窗口中的哪个小部件应该接收焦点。

这可以很简单:如果焦点之前在这个窗口中,那么最后一个拥有焦点的部件应该重新获得它。Qt 会自动执行此操作。

如果焦点从未在此窗口中出现过,并且您知道焦点应该从哪里开始,请在调用show()之前,在应该接收焦点的小部件上调用setFocus()。如果不这样做,Qt将选择一个合适的小部件。