在PyQt中使用带复选框的下拉框

8 投票
3 回答
14078 浏览
提问于 2025-04-16 13:12

我想做一个下拉列表,里面有复选框,类似于把ComboBox里的选项变成复选框。但是QComboBox不支持把QCheckBox作为它的成员,我找不到其他的解决办法。我在Qt Wiki上找到了一种用C++实现的方法,但我不知道怎么把它转换成Python代码。

3 个回答

2

我之前在这个链接上回答过一个类似的问题:如何在组合框中创建一个带复选框的树视图 - PyQt。不过为了完整起见,我在这里再给你贴一下:

你需要创建一个模型,这个模型要支持 Qt.CheckStateRole 这个功能,在数据和设置数据的方法中都要用到,同时在标志方法中要使用 Qt.ItemIsUserCheckable 这个标志。

我在这里给你贴一个我在项目中使用的例子,这是一个 QSortFilterProxyModel 的通用实现,可以在任何模型中使用。不过你也可以把这些思路用在你自己的模型实现中。显然,我在这个子类中使用了一些内部结构,这些结构在 PyQt 中并没有直接提供,而是和我的内部实现(self.booleanSet 和 self.readOnlySet)相关联。

def flags(self, index):
    if not index.isValid():
        return Qt.ItemIsEnabled

    if index.column() in self.booleanSet:
        return Qt.ItemIsUserCheckable | Qt.ItemIsSelectable | Qt.ItemIsEnabled
    elif index.column() in self.readOnlySet:
        return Qt.ItemIsSelectable | Qt.ItemIsEnabled
    else:
        return QSortFilterProxyModel.flags(self, index)

def data(self, index, role):
    if not index.isValid():
        return QVariant()

    if index.column() in self.booleanSet and role in (Qt.CheckStateRole, Qt.DisplayRole):
        if role == Qt.CheckStateRole:
            value = QVariant(Qt.Checked) if index.data(Qt.EditRole).toBool() else QVariant(Qt.Unchecked)
            return value
        else: #if role == Qt.DisplayRole:
            return QVariant()
    else:
        return QSortFilterProxyModel.data(self, index, role)

def setData(self, index, data, role):
    if not index.isValid():
        return False

    if index.column() in self.booleanSet and role == Qt.CheckStateRole:
        value = QVariant(True) if data.toInt()[0] == Qt.Checked else QVariant(False)
        return QSortFilterProxyModel.setData(self, index, value, Qt.EditRole)
    else:
        return QSortFilterProxyModel.setData(self, index, data, role)
8

使用组合框的项目模型,因为这些项目支持复选框。你只需要标记哪些项目可以被用户勾选,并设置一个初始的勾选状态,这样复选框就会显示出来(只有在有有效状态时,复选框才会出现)。

item.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
item.setCheckState(QtCore.Qt.Unchecked)  # causes checkBox to show

下面是一个简单的子类示例:

from PyQt5 import QtGui, QtCore, QtWidgets
import sys, os

# subclass
class CheckableComboBox(QtWidgets.QComboBox):
    # once there is a checkState set, it is rendered
    # here we assume default Unchecked
    def addItem(self, item):
        super(CheckableComboBox, self).addItem(item)
        item = self.model().item(self.count()-1,0)
        item.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
        item.setCheckState(QtCore.Qt.Unchecked)

    def itemChecked(self, index):
        item = self.model().item(i,0)
        return item.checkState() == QtCore.Qt.Checked

# the basic main()
app = QtWidgets.QApplication(sys.argv)
dialog = QtWidgets.QMainWindow()
mainWidget = QtWidgets.QWidget()
dialog.setCentralWidget(mainWidget)
ComboBox = CheckableComboBox(mainWidget)
for i in range(6):
    ComboBox.addItem("Combobox Item " + str(i))

dialog.show()
sys.exit(app.exec_())
9

当我需要这个的时候,我想到了一个更简单的解决办法(至少不需要去继承QCombobox)。这个方法对我有效。

具体做法是创建一个带有可勾选选项的菜单,然后把这个菜单设置到一个按钮上。接着,可以把菜单或者选项连接到一个槽函数上。

在Qt中的代码大概是这样的(我还没用过PyQt,抱歉,希望你能把这个转换过去,对我来说似乎更简单):

QMenu *menu = new QMenu;
QAction *Act1 = new QAction("Action 1", menu);
Act1->setCheckable(true);
QAction *Act2 = new QAction("Action 2", menu);
Act2->setCheckable(true);
menu->addAction(Act1);
menu->addAction(Act2);

QPushButton *btn = new QPushButton("Btn");    
btn->setMenu(menu);

希望这对你有帮助

撰写回答