那个“边框”实际上是焦点矩形,只要小部件具有键盘焦点,就会显示它。如果是按钮,则表示可以使用键盘上的空格键按下。
有两种方法可以“移除”焦点矩形:使用setFocusPolicy() 和QtCore.Qt.NoFocus 来禁用任何小部件上的焦点,或者阻止其绘制。
考虑到任何一种可能性都会使键盘导航(通过 tab 和箭头键)和交互(使用空格键)变得不可能,或者至少不那么可用。
要禁用焦点策略,只需使用以下命令:
self.someButton.setFocusPolicy(QtCore.Qt.NoFocus)
考虑到在大多数样式中禁用焦点也会禁用自动助记符下划线加速器快捷键(对于 Alt+ 快捷键);虽然在 MacOS 上这些会自动禁用,但在其他平台上可能会出现问题,因为在这种情况下,所有助记符都需要在快捷方式的字母前使用 & 手动设置。
在这种情况下,绘画方法可能会很有用,如果用户在任何时候不小心按下了空格键,当前聚焦的按钮就会被按下,从用户体验的角度来看,这肯定不是一件好事。
有两种主要方法可以防止绘制:通过样式表或通过自定义绘制。
使用样式表可以完全控制绘画,但缺点是需要至少为正常和按下状态(但也可能为选中和悬停状态)提供样式表,并且会不尊重按钮的平台默认大小提示和边距。此外,对于某些特定平台样式(尤其是在某些 Linux 发行版上),如果样式还在内部混合样式表,则无法保证不会绘制焦点;在这些情况下,可能可以通过设置 outline: none; 来禁用焦点矩形。
button.setStyleSheet('''
QPushButton {
border: 1px outset green;
border-radius: 2px;
background: lightgreen;
}
QPushButton:pressed {
border: 1px inset green;
}
''')
自定义绘画可以通过继承QPushButton并重新实现paintEvent()来完成;通过使用QStylePainter() 和QStyleOptionButton(),我们可以确保按钮始终根据当前样式进行绘制,同时在实际绘制之前控制其某些方面:
class NoFocusRectButton(QtWidgets.QPushButton):
def paintEvent(self, event):
qp = QtWidgets.QStylePainter(self)
opt = QtWidgets.QStyleOptionButton()
# initialize the style option for the current button
self.initStyleOption(opt)
# remove the focus state flag if it exists
opt.state &= ~QtWidgets.QStyle.State_HasFocus
# paint the button
qp.drawControl(QtWidgets.QStyle.CE_PushButton, opt)
另一种方法是使用QProxyStyle,它可以安装在按钮上或整个应用程序上(不幸的是,在父窗口小部件上设置样式不会在其子窗口小部件上传播样式)。
class NoFocusProxyStyle(QtWidgets.QProxyStyle):
def drawControl(self, control, opt, painter, widget=None):
if control in (self.CE_PushButton, self.CE_PushButtonBevel):
opt.state &= ~self.State_HasFocus
super().drawControl(control, opt, painter, widget)
def drawPrimitive(self, element, opt, painter, widget=None):
if element == self.PE_FrameFocusRect and isinstance(widget, QtWidgets.QPushButton):
opt.state &= ~self.State_HasFocus
super().drawPrimitive(element, opt, painter, widget)
# ...
app = QtWidgets.QApplication(sys.argv)
proxyStyle = NoFocusProxyStyle()
app.setStyle(proxyStyle)
# or, alternatively
someButton.setStyle(proxyStyle)
请注意,在小部件上设置样式表时,在某些情况下会部分绕过样式(取决于样式、小部件、当前默认样式和样式表中设置的属性)。