代码之家  ›  专栏  ›  技术社区  ›  Jellby

如何在选项卡组中的所有按钮之间进行制表?

  •  0
  • Jellby  · 技术社区  · 5 年前

    默认情况下,通过小部件改变焦点的Tab方法一次只能停止一次 QButtonGroup 其中一个应该用箭头键在组中移动(无论如何,它只对单选按钮起作用)。但是,我想创建一个按钮组 QCheckBox 内部,具有“独占”行为,但允许取消选中所有框,以及 在这里我可以使用Tab键正常移动,就像他们不在一个组中一样 .

    “clearable”部分我可以用一个子类来完成,但是对于tabing,似乎只要组中的一个按钮获得焦点,它就会改变 focusPolicy 对于所有其他按钮,例如它们不接受Tab(从11到10),而获得焦点的按钮更改为11。如何禁用/覆盖此选项?谁在搞焦点政策?我试着定义一个 focusInEvent Q检查盒 ,我看到它改变了“这个按钮的” 聚焦策略 ,但我如何从“this button”知道“other button”是什么(假设最终应用程序可能有许多按钮组)?理想的情况是,我会对 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_() )
    
    0 回复  |  直到 5 年前
        1
  •  0
  •   eyllanesc Yonghwan Shin    5 年前

    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)
    # ...