【问题标题】:PyQt5 QGroupBox with QCheckBox - dismiss auto disablePyQt5 QGroupBox 与 QCheckBox - 关闭自动禁用
【发布时间】:2021-11-16 09:25:04
【问题描述】:

我想使用一个可以选中的 QGroupBox,但是当 QGroupBox 复选框未选中时,我不会禁用 QGroupBox 的内容小部件。

From reference:

checked : bool 该属性保存分组框是否被选中

如果组框是可选中的,它会显示一个复选框。如果 复选框被选中,组框的孩子被启用; 否则,孩子将被禁用并且用户无法访问。

默认情况下,选中的组框也会被选中。

我想在 QGroupBox 标题栏中有一个复选框,但我不希望应用上述功能。

复选框逻辑将是全选、全选,因此当未选中复选框时,用户可以修改内部 QGroupBox 复选框元素。

我想保留如下界面:

也许我必须使用带有 QPaintEvent 或 QSS 样式表的 QFrame,但我不是这方面的专家。

编辑:如果可能的话,我还希望 triState 用于 QGroupBox 复选框。

编辑 2: 我试试这段代码:

self.main_self.ui_visible_player_list_fields_window.groupBox.changeEvent(QtCore.QEvent.EnabledChange).connect(lambda event:event.ignore())

但它有错误。

 self.main_self.ui_visible_player_list_fields_window.groupBox.changeEvent(QtCore.QEvent.EnabledChange).connect(lambda event:event.ignore())
TypeError: changeEvent(self, QEvent): argument 1 has unexpected type 'Type'

编辑:我认为下面的代码可以解决问题:

class Custom_QGroupBox(QtWidgets.QGroupBox):

    def __init__(self, parent=None):
        super(Custom_QGroupBox, self).__init__(parent)

    def changeEvent(self, event):
        if event.type() == QtCore.QEvent.EnabledChange:
            self.blockSignals(True)
            self.setEnabled(True)
            event.ignore()
            self.blockSignals(False)
        else:
            return super(Custom_QGroupBox, self).changeEvent(event)

但它没有:(

【问题讨论】:

    标签: python pyqt5 qcheckbox qgroupbox


    【解决方案1】:

    前提:可能不是个好主意

    UI 元素有已知的约定,用户习惯他们并期望对众所周知的元素类型执行“操作”会导致同样已知的结果。
    虽然组框更像是一个边缘案例,但 Qt 的默认行为遵循约定:切换复选框会导致切换其内容的启用状态。

    由于 UI 元素应始终尝试遵循约定并使用户操作的可能结果尽可能可预测,因此更好的解决方案是添加“顶级”按钮组,将所有框设置为选中或未选中(“全选”和“全选”)。

    为什么它不起作用?

    首先,changeEvent 不是信号,所以你不能尝试“连接”它(总是在 Qt 文档中查找函数类型,如果它是信号,它除了定义之外,还会有一个[signal] 符号)。
    它是一个事件处理程序,它需要一个事件 instance 作为参数,而不是类型。

    然后,切换组框的复选框会更改其子项的状态,而不是组框的状态,因此切换时您将永远不会收到EnabledChange 事件。
    如果您考虑一下,这很明显:如果 whole 组框在切换其复选框时被禁用,则您永远不能再次单击它来启用它,因为默认情况下禁用的小部件会忽略输入事件,并且标题 复选框也将显示为禁用。

    可能的解决方案

    有多种可能的解决方案,包括将 QFrame 子类化并绘制复选框和标题,但使其符合当前样式将非常(并且不必要)困难。
    最好的选择通常是less 改变默认行为的那个。

    在这种情况下,我的建议是做两件事:

    • 连接到组框的toggled 信号并覆盖默认行为(恢复启用状态,除非明确为该小部件设置);
    • 在实际绘制之前覆盖paintEvent 并更改用于样式和画家的QStyleOptionGroupBox state

    由于状态是在内部为组框定义的,我们需要覆盖它的值:initStyleOption() 在复选框(理论上)选中时添加选项的状态标志 State_On,或者在未选中时添加 State_Off (因此,无论子小部件是否启用),但我们必须根据复选框状态设置选项状态。为此,我们必须检查所有复选框并验证是否所有都被选中,任何个被选中,或者没有。

    from PyQt5 import QtCore, QtWidgets
    
    class Custom_QGroupBox(QtWidgets.QGroupBox):
        checkAllIfAny = True
        def __init__(self, *args, **kwargs):
            super(Custom_QGroupBox, self).__init__(*args, **kwargs)
            self.setCheckable(True)
            self.checkBoxes = []
            self.toggled.connect(self.toggleCheckBoxes)
    
        def addCheckBox(self, cb):
            self.checkBoxes.append(cb)
            cb.toggled.connect(self.update)
            cb.destroyed.connect(lambda: self.removeCheckBox(cb))
    
        def removeCheckBox(self, cb):
            try:
                self.checkBoxes.remove(cb)
                cb.toggled.disconnect(self.update)
            except:
                pass
    
        def allStates(self):
            return [cb.isChecked() for cb in self.checkBoxes]
    
        def toggleCheckBoxes(self):
            if self.checkAllIfAny:
                state = not all(self.allStates())
            else:
                state = not any(self.allStates())
    
            for widget in self.children():
                if not widget.isWidgetType():
                    continue
                if not widget.testAttribute(QtCore.Qt.WA_ForceDisabled):
                    # restore the enabled state in order to override the default
                    # behavior of setChecked(False); previous explicit calls for
                    # setEnabled(False) on the target widget will be ignored
                    widget.setEnabled(True)
                    if widget in self.checkBoxes:
                        widget.setChecked(state)
    
        def paintEvent(self, event):
            opt = QtWidgets.QStyleOptionGroupBox()
            self.initStyleOption(opt)
            states = self.allStates()
            if all(states):
                # force the "checked" state
                opt.state |= QtWidgets.QStyle.State_On
                opt.state &= ~QtWidgets.QStyle.State_Off
            else:
                # force the "not checked" state
                opt.state &= ~QtWidgets.QStyle.State_On
                if any(states):
                    # force the "not unchecked" state and set the tristate mode
                    opt.state &= ~QtWidgets.QStyle.State_Off
                    opt.state |= QtWidgets.QStyle.State_NoChange
                else:
                    # force the "unchecked" state
                    opt.state |= QtWidgets.QStyle.State_Off
            painter = QtWidgets.QStylePainter(self)
            painter.drawComplexControl(QtWidgets.QStyle.CC_GroupBox, opt)
    
    
    app = QtWidgets.QApplication([])
    groupBox = Custom_QGroupBox('Group options')
    
    layout = QtWidgets.QGridLayout(groupBox)
    o = 0
    for c in range(2):
        for r in range(4):
            o += 1
            cb = QtWidgets.QCheckBox('Option {}'.format(o))
            groupBox.addCheckBox(cb)
            layout.addWidget(cb, r, c)
    
    groupBox.show()
    app.exec_()
    

    【讨论】:

    • 不错的答案。谢谢!
    【解决方案2】:

    我添加了这段代码:

    self.main_self.ui_visible_player_list_fields_window.groupBox.blockSignals(True)
            self.main_self.ui_visible_player_list_fields_window.groupBox.setEnabled(True)
            
            for child_widget in self.main_self.ui_visible_player_list_fields_window.groupBox.children():
                try:
                    child_widget.setEnabled(True)
                except:
                    pass
            self.main_self.ui_visible_player_list_fields_window.groupBox.blockSignals(False)
    

    在切换方法中,是child_widget_checkbox.stateChanged.connect 方法,它可以工作。

    唯一要完全回答问题的是 QGroupBox 的三态。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-03
      • 1970-01-01
      • 1970-01-01
      • 2021-07-12
      • 2014-04-22
      • 2014-08-20
      • 1970-01-01
      相关资源
      最近更新 更多