[默认情况下,用于通过窗口小部件更改焦点的Tab方法似乎仅在QButtonGroup
中停止一次,并且期望通过箭头键在该组内移动(无论如何仅与单选按钮一起使用)。但是,我想创建一个按钮组,其中包含QCheckBox
,具有“独占”行为,但可以取消选中所有框,并且我可以使用Tab键正常移动的位置,就好像它们不在组。
我可以通过子类完成“可清除”部分,但是对于制表,似乎只要该组中的某个按钮获得焦点,它就会更改所有其他按钮的focusPolicy
,这样它们就不会t接受Tab(从11到10),而获得焦点的按钮变为11。如何禁用/覆盖此选项?谁在破坏重点政策?我尝试为focusInEvent
定义QCheckBox
方法,但我发现它会更改“此按钮的” focusPolicy
,但如何从“此按钮”中知道“其他按钮”是什么(给定的最终的应用程序可能有很多按钮组)?理想情况下,我会对QButtonGroup
子类做一些事情,但我不知道它是否有任何方法可以响应其按钮中的焦点更改。
这里是一个小例子。 “标签顺序”按钮可打印当前的标签顺序和焦点策略:
#!/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_() )
似乎缺少此行为,如果修改了QAbstractButton source code,则会实现:
// https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/widgets/qabstractbutton.cpp?h=5.14#n1088
void QAbstractButton::keyPressEvent(QKeyEvent *e)
{
// ...
d->moveFocus(e->key());
// ...
// https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/widgets/qabstractbutton.cpp?h=5.14#n247
void QAbstractButtonPrivate::moveFocus(int key)
{
// ...
if (candidate) {
if (key == Qt::Key_Up || key == Qt::Key_Left)
candidate->setFocus(Qt::BacktabFocusReason);
else
candidate->setFocus(Qt::TabFocusReason);
}
}
据观察,所有按钮都被视为一个单元,无法禁用它,因此一种解决方法是实现具有相同功能的类,并在previous post中实现了它,情况是:
class ButtonManager(QtCore.QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._buttons = []
@property
def buttons(self):
return self._buttons
def add_button(self, button):
if isinstance(button, QtWidgets.QAbstractButton):
button.toggled.connect(self.on_toggled)
self.buttons.append(button)
@QtCore.pyqtSlot(bool)
def on_toggled(self, state):
button = self.sender()
if state:
for b in self.buttons:
if b != button and b.isChecked():
b.blockSignals(True)
b.setChecked(False)
b.blockSignals(False)
else:
button.blockSignals(True)
button.setChecked(False)
button.blockSignals(False)
# ...
button1.clicked.connect(self.tab)
button = ButtonManager(self)
button.add_button(self.box1.box)
button.add_button(self.box2.box)
button.add_button(self.box3.box)
button.add_button(self.box4.box)
# ...