簡體   English   中英

帶有 QCheckBox 的 PyQt5 QGroupBox - 關閉自動禁用

[英]PyQt5 QGroupBox with QCheckBox - dismiss auto disable

我想使用一個可檢查的 QGroupBox,但是當 QGroupBox 復選框未選中時,我不會禁用 QGroupBox 的內容小部件。

來自參考:

checked : bool 該屬性保存是否選中組框

如果組框是可勾選的,它會顯示一個復選框。 如果選中該復選框,則啟用組框的子項; 否則,孩子將被禁用並且用戶無法訪問。

默認情況下,還會選中可檢查的組框。

我想在 QGroupBox 標題欄中有一個復選框,但我不希望應用上述功能。

復選框邏輯將是全選,全選,因此當復選框未選中時,用戶可以修改內部 QGroupBox 復選框元素。

我想保留如下所示的界面 ui:

也許我必須使用帶有 QPaintEvent 或 QSS 樣式表的 QFrame,但我不是這方面的專家。

在此處輸入圖片說明

編輯:我也想三態為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)

但它沒有:(

我添加這個代碼:

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 的三態。

前提:可能不是一個好主意

UI 元素有一些已知的約定,用戶已經習慣了它們並期望對眾所周知的元素類型執行“操作”會導致同樣已知的結果。
雖然分組框更像是一種邊緣情況,但 Qt 的默認行為遵循約定:切換復選框會導致切換其內容的啟用狀態。

由於 UI 元素應始終嘗試遵循約定並使用戶操作的可能結果盡可能可預測,因此更好的解決方案是添加一組“頂級”按鈕,將所有框設置為選中或未選中(“全部檢查”和“不檢查”)。

為什么不起作用?

首先, changeEvent不是信號,所以你不能嘗試“連接”它(總是在 Qt 文檔中尋找函數類型,如果它是一個信號,除了它的定義之外,它還會有一個[signal]符號)。
它是一個事件處理程序,它需要一個事件實例作為參數,而不是一個類型。

然后,切換組框的復選框會更改其子項的狀態,而不是組框的狀態,因此在切換時您永遠不會收到EnabledChange事件。
如果您考慮一下,這很明顯:如果在切換復選框時整個組框被禁用,您將永遠無法再次單擊以啟用它,因為默認情況下禁用的小部件的輸入事件將被忽略,並且標題復選框都將也顯示為禁用。

可能的解決方案

有多種可能的解決方案,包括子類化 QFrame 並繪制復選框和標題,但使其符合當前樣式將非常(並且不必要地)困難。
最佳選擇通常是對默認行為進行較少更改的選擇。

在這種情況下,我的建議是做兩件事:

  • 連接到分組框的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_()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM