It seems by default the Tab method for changing focus through widgets only stops once in a QButtonGroup
and one is expected to move inside the group with the arrow keys (which only works with radio buttons, anyway). I want, however, to create a button group with QCheckBox
inside, with the "exclusive" behaviour but allowing to uncheck all boxes, and where I can use the Tab key to move normally, as if they were not in a group.
The "clearable" part I could get it done with a subclass, but for the tabbing, it seems as soon as a button in the group gets the focus, it changes the focusPolicy
for all other buttons such that they don't accept Tab (from 11 to 10), while the button that got the focus changes to 11. How can I disable/override this? Who is messing with the focus policies? I've tried defining a focusInEvent
method for a QCheckBox
, and I see that it changes "this button's" focusPolicy
, but how can I know, from "this button" what the "other buttons" are (given that the final application may have many button groups)? Ideally, I would do something to the QButtonGroup
subclass, but I don't know if it has any method that can respond to a focus change in its buttons.
Here is a small example. The "Tab order" button prints the current tab order and focus policies:
#!/usr/bin/env python3
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QSize
class Custom(QWidget):
def __init__(self, text1, text2):
QWidget.__init__(self)
self.box = QCheckBox(text1)
self.button = QPushButton(text2)
layout = QHBoxLayout()
layout.addWidget(self.box)
layout.addWidget(self.button)
self.setLayout(layout)
self._text = f'{text1} {text2}'
self.setFocusPolicy(QtCore.Qt.ClickFocus)
def text(self):
return self._text
class ClearableButtonGroup(QButtonGroup):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.button = None
def addButton(self, button):
try:
super().addButton(button)
button.pressed.connect(self.button_pressed)
button.clicked.connect(self.button_clicked)
except TypeError:
pass
def removeButton(self, button):
try:
button.pressed.disconnect(self.button_pressed)
button.clicked.disconnect(self.button_clicked)
super().removeButton(button)
except AttributeError:
pass
def button_pressed(self):
if (self.sender() is self.checkedButton()):
self.button = self.sender()
else:
self.button = None
def button_clicked(self):
button = self.sender()
if (button is self.button):
exclusive = self.exclusive()
self.setExclusive(False)
button.setChecked(False)
self.setExclusive(exclusive)
class HelloWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
gridLayout = QGridLayout(centralWidget)
self.box1 = Custom('Box 1', 'Button 1')
self.box2 = Custom('Box 2', 'Button 2')
self.box3 = Custom('Box 3', 'Button 3')
self.box4 = Custom('Box 4', 'Button 4')
gridLayout.addWidget(self.box1, 0, 0)
gridLayout.addWidget(self.box2, 1, 0)
gridLayout.addWidget(self.box3, 2, 0)
gridLayout.addWidget(self.box4, 3, 0)
button1 = QPushButton('Tab order')
gridLayout.addWidget(button1, 4, 1)
button1.clicked.connect(self.tab)
group = ClearableButtonGroup(self)
group.setExclusive(True)
group.addButton(self.box1.box)
group.addButton(self.box2.box)
group.addButton(self.box3.box)
group.addButton(self.box4.box)
def tab(self):
print_tab_order(self)
def print_tab_order(widget):
w = widget
while True:
try:
print('Text: {}; FocusPolicy: {}'.format(w.text(), w.focusPolicy()))
except AttributeError:
pass
w = w.nextInFocusChain()
if w == widget:
break
print('----')
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWin = HelloWindow()
mainWin.show()
sys.exit( app.exec_() )