简体   繁体   English

PyQt5 单选按钮制表符停止功能障碍

[英]PyQt5 Radio Button Tab Stop Dysfunction

I have some programs that use dialog boxes with groups of radio buttons.我有一些程序使用带有单选按钮组的对话框。 The expected behavior when initially designating a button with setChecked(True) is that it will be assigned the tab stop for its group.最初使用 setChecked(True) 指定按钮时的预期行为是为其组分配制表位。 That's how it works in the Windows API.这就是它在 Windows API 中的工作原理。 But it appears that the tab stop always goes to the first button in the group (even though it's not checked) until you manually re-select the intended button with a mouse click or arrow key.但似乎制表位总是转到组中的第一个按钮(即使它没有被选中),直到您通过鼠标单击或箭头键手动重新选择预期的按钮。 You can use setFocus(True) on a designated button, but this only can be used for one group of buttons.您可以在指定按钮上使用 setFocus(True),但这只能用于一组按钮。 This happens with Windows 10 and Ubuntu, using PyQt5 versions 5.12 and 5.15. Windows 10 和 Ubuntu 使用 PyQt5 版本 5.12 和 5.15 会发生这种情况。 I've tried the various radio button focus-related functions listed in the Qt documentation without success.我尝试了 Qt 文档中列出的各种单选按钮焦点相关功能,但均未成功。

This question was asked 2 years ago (53645767/radio-button-tab-sequencing), without an answer that explains definitively either how to set the tab stops, or that it's not an option.这个问题是 2 年前提出的(53645767/radio-button-tab-sequencing),没有明确解释如何设置制表位或它不是一个选项的答案。

I adapted this script from a Web tutorial.我从 Web 教程改编了这个脚本。 For both groups, I initialize the second button in the group with setChecked(True), and use setFocus(True) on the button in the first group.对于这两个组,我使用 setChecked(True) 初始化组中的第二个按钮,并在第一组中的按钮上使用 setFocus(True)。 Cycling with "Tab" shows the tab stop at the second button of the first group as intended, but it stays with the first (unchecked) button of the second group until you click the second button or use the arrow key to re-select it.使用“Tab”循环会按预期在第一组的第二个按钮处显示制表位,但它会保留在第二组的第一个(未选中)按钮上,直到您单击第二个按钮或使用箭头键重新选择它. Am I missing something here or it this an intentional "feature"?我在这里遗漏了什么还是故意的“功能”?

def init_ui(self):
    self.label = QLabel('What is your favorite color?')
    self.rbtn1 = QRadioButton('blue')
    self.rbtn2 = QRadioButton('red')
    self.label2 = QLabel("")
    self.label3 = QLabel('What is your favorite element?')
    self.rbtn3 = QRadioButton('bolognium')
    self.rbtn4 = QRadioButton('unobtainium')
    self.label4 = QLabel("")
    
    self.btngroup1 = QButtonGroup()
    self.btngroup2 = QButtonGroup()
    self.btngroup1.addButton(self.rbtn1)
    self.btngroup1.addButton(self.rbtn2)
    self.btngroup2.addButton(self.rbtn3)
    self.btngroup2.addButton(self.rbtn4)
    
    self.rbtn1.toggled.connect(self.onClickedColor)
    self.rbtn2.toggled.connect(self.onClickedColor)
    self.rbtn3.toggled.connect(self.onClickedElement)
    self.rbtn4.toggled.connect(self.onClickedElement)        
    
    layout = QVBoxLayout()
    layout.addWidget(self.label)
    layout.addWidget(self.rbtn1)
    layout.addWidget(self.rbtn2)
    layout.addWidget(self.label2)
    layout.addWidget(self.label3)
    layout.addWidget(self.rbtn3)
    layout.addWidget(self.rbtn4)
    layout.addWidget(self.label4)
    
    self.setGeometry(200, 200, 300, 300)
    self.setLayout(layout)

    self.rbtn2.setChecked(True)
    self.rbtn2.setFocus(True)
    self.rbtn4.setChecked(True)
    
    self.show()

def onClickedColor(self):
    radioBtn = self.sender()
    if radioBtn.isChecked():
        self.label2.setText("Your favorite color is " + radioBtn.text())
        
def onClickedElement(self):
    radioBtn = self.sender()
    if radioBtn.isChecked():
        self.label4.setText("Your favorite element is " + radioBtn.text())

That seems like a bug (btw, the question you linked only shows a similar behavior, but considering it's about HTML I believe it's unrelated).这似乎是一个错误(顺便说一句,你链接的问题只显示了类似的行为,但考虑到它是关于 HTML 我相信它是不相关的)。

Unfortunately, "auto-focus" management is pretty complex and is based on various aspects: order of creation, parenthood, focus policies, position of widgets and system behavior.不幸的是,“自动对焦”管理非常复杂,并且基于多个方面:创建顺序、父级、焦点策略、小部件的 position 和系统行为。

A possible (and "hacky") solution could be to override focusNextPrevChild() .一个可能的(和“hacky”)解决方案可能是覆盖focusNextPrevChild()

The trick is to get the current focused widget and cycle through its own nextFocusInChain and previousInFocusChain (by checking its focus policy and recursively until a valid policy is found), and then ensure that the widget is contained in the button group.诀窍是获取当前焦点小部件并循环通过其自己的nextFocusInChainpreviousInFocusChain (通过检查其焦点策略并递归直到找到有效策略),然后确保小部件包含在按钮组中。

Note that the following is a very raw implementation, and you'll probably need further tests in order to get it correctly working and ensure that no bug/recursion issue happens.请注意,以下是一个非常原始的实现,您可能需要进一步测试才能使其正常工作并确保不会发生错误/递归问题。

    def focusNextPrevChild(self, isNext):
        if isNext:
            func = 'nextInFocusChain'
            reason = Qt.TabFocusReason
        else:
            func = 'previousInFocusChain'
            reason = Qt.BacktabFocusReason
        current = self.focusWidget()
        other = getattr(current, func)()
        while True:
            while not other.focusPolicy():
                other = getattr(other, func)()
            if isinstance(other, QRadioButton):
                for group in self.btngroup1, self.btngroup2:
                    if other in group.buttons():
                        checked = group.checkedButton()
                        if checked == current and not isNext:
                            continue
                        if checked:
                            checked.setFocus(reason)
                        else:
                            other.setFocus(reason)
                        return True
            break
        return super().focusNextPrevChild(isNext)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM