This is a QPushButton
. When clicked it looks like this:
As you can see, around the button there's this light grey dotted/checkered highlight/border. How do I get rid of it? Can't seem to find an answer anywhere.
I've even tried setting the border to None
in the stylesheet.
That "border" is actually the focus rectangle, which is shown whenever a widget has keyboard focus. In case of push buttons, it means that it can be pressed using the spacebar on the keyboard.
There are two ways to "remove" the focus rectangle: by using setFocusPolicy()
with QtCore.Qt.NoFocus
to disable any focus on the widget, or by preventing its painting.
Consider that either possibilities will make keyboard navigation (via tab and arrow keys) and interaction (with the space bar) impossible or, at least, less usable.
To disable the focus policy, just use this:
self.someButton.setFocusPolicy(QtCore.Qt.NoFocus)
Consider that disabling the focus in most styles it also disables the automatic mnemonic underscore accelerator shortcuts (for Alt+<letter> shortcuts); while on MacOS those are automatically disabled, this might be a problem on other platforms, as in this case all mnemonics need to be set manually using &
before the letter for the shortcut.
In that case, the painting approach might be useful, with the catch that if the user inadvertently presses the spacebar at any moment, the currently focused button will be pressed, which is certainly not a good thing from the UX perspective.
There are two main ways to prevent the painting: via stylesheet or via custom painting.
Using stylesheets gives complete control on the painting, but has the downsides of requiring to provide the stylesheet at least for the normal and pressed states (but possibly for the checked and hovered states also) and will not respect the platform default size hints and margins for buttons. Also, for some specific platform styles (especially on some Linux distros) there is no guarantee that the focus won't be painted if the style also mixes stylesheets internally; in those cases it is possible that the focus rectangle might be disabled by setting outline: none;
.
button.setStyleSheet('''
QPushButton {
border: 1px outset green;
border-radius: 2px;
background: lightgreen;
}
QPushButton:pressed {
border: 1px inset green;
}
''')
Custom painting can be done by subclassing QPushButton and reimplementing the paintEvent()
; by using QStylePainter() and QStyleOptionButton() we can ensure that the button will always be painted according to the current style while controlling some of its aspect before actually painting it:
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)
An alternative is to use a QProxyStyle that can be installed on the button or on the whole application (unfortunately, setting a style on a parent widget won't propagate the style on its children).
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)
Note that when setting stylesheets on widgets the style is partially bypassed in some cases (depending on the style, the widget, the current default style and the properties set in the stylesheet).