Как вкладывать все кнопки в группе вкладок? - PullRequest
0 голосов
/ 28 апреля 2020

Кажется, по умолчанию метод Tab для изменения фокуса с помощью виджетов останавливается только один раз в QButtonGroup, и ожидается, что он перемещается внутри группы с помощью клавиш со стрелками (в любом случае, он работает только с переключателями). Однако я хочу создать группу кнопок с QCheckBox внутри, с «эксклюзивным» поведением, но позволяющим снять все флажки, и , где я могу использовать клавишу Tab для обычного перемещения, как если бы их не было в группа .

«очищаемая» часть, которую я мог бы сделать с помощью подкласса, но для табуляции, кажется, как только кнопка в группе получает фокус, она меняет focusPolicy для всех других кнопок, чтобы они не принимали Tab (от 11 до 10), в то время как кнопка, которая получила фокус, меняется на 11. Как я могу отключить / переопределить это? Кто возится с политикой фокуса? Я попытался определить метод focusInEvent для QCheckBox, и я вижу, что он меняет «1010 * этой кнопки», но как узнать из «этой кнопки», что такое «другие кнопки» ( что в финальном приложении может быть много групп кнопок)? В идеале я хотел бы что-то сделать для подкласса 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_() )

1 Ответ

0 голосов
/ 28 апреля 2020

Кажется, что это поведение отсутствует, если исходный код QAbstractButton пересмотрен, он реализован:

// 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);
    }
}

Как видно, все кнопки считаются как единое целое, и его невозможно отключить, поэтому обходной путь заключается в реализации класса с такой же функциональностью, и в предыдущем посте я реализовал его, который в вашем случае:

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)
# ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...