lianback 2020-03-28
由于最近的项目需要具有复选功能,但过多的复选框会影响界面布局和美观,因而想到把 PyQt5 的下拉列表和复选框结合起来,但在 PyQt5 中并没有这样的组件供我们使用,所以想要自己实现一个下拉复选框,主要就是继承 QComboBox 类,然后将复选框 QCheckBox 加入其中,并实现相应的功能。
最终实现的下拉复选框效果如下:

在 PyQt5 中,有几个主要的方法需要了解一下,方法名称和对应的含义如下:
<span>,</span>组合框获取视图的所有权。实现下拉复选框的思路为用 setView() 方法将 QComboBox 下拉列表的视图改为 QListWidget 组件,然后将 QCheckBox 复选框用在 QListWiget 中,具体代码如下:
class ComboCheckBox(QComboBox):
    def __init__(self, items: list):
        """
        initial function
        :param items: the items of the list
        """
        super(ComboCheckBox, self).__init__()
        self.items = items  # items list
        self.box_list = []  # selected items
        self.text = QLineEdit()  # use to selected items
        self.text.setReadOnly(True)
        q = QListWidget()
        for i in range(len(self.items)):
            self.box_list.append(QCheckBox())
            self.box_list[i].setText(self.items[i])
            item = QListWidgetItem(q)
            q.setItemWidget(item, self.box_list[i])
            self.box_list[i].stateChanged.connect(self.show_selected)
        self.setLineEdit(self.text)
        self.setModel(q.model())
        self.setView(q)
    def get_selected(self) -> list:
        """
        get selected items
        :return:
        """
        ret = []
        for i in range(len(self.items)):
            if self.box_list[i].isChecked():
                ret.append(self.box_list[i].text())
        return ret
    def show_selected(self):
        """
        show selected items
        :return:
        """
        self.text.clear()
        ret = ‘; ‘.join(self.get_selected())
        self.text.setText(ret)其中 show_selected() 用于显示被选中的内容,get_selected() 则用于获取所有被选中的内容并返回。
要增加全选功能,首先是要在最前面加一个全选的选择框,然后为这个全选的选择框绑定相应的方法,用于实现全选功能和取消全选功能,具体代码如下:
def all_selected(self):
    """
    decide whether to check all
    :return:
    """
    # change state
    if self.state == 0:
        self.state = 1
        for i in range(1, len(self.items)):
            self.box_list[i].setChecked(True)
    else:
        self.state = 0
        for i in range(1, len(self.items)):
            self.box_list[i].setChecked(False)
    self.show_selected()由于默认的样式并不美观,所以我们可以对控件的样式进行自定义,例如字体大小、字体粗细等等,例如:
q.setStyleSheet("font-size: 20px; font-weight: bold; height: 40px; margin-left: 5px")
self.setStyleSheet("width: 300px; height: 50px; font-size: 21px; font-weight: bold")
完善后的下拉复选框的运行程序代码如下:
from PyQt5.QtWidgets import QComboBox, QLineEdit, QListWidgetItem, QListWidget, QCheckBox,  2     QApplication, QVBoxLayout, QWidget
import sys
class ComboCheckBox(QComboBox):
    def __init__(self, items: list):
        """
        initial function
        :param items: the items of the list
        """
        super(ComboCheckBox, self).__init__()
        self.items = ["全选"] + items  # items list
        self.box_list = []  # selected items
        self.text = QLineEdit()  # use to selected items
        self.state = 0  # use to record state
        q = QListWidget()
        for i in range(len(self.items)):
            self.box_list.append(QCheckBox())
            self.box_list[i].setText(self.items[i])
            item = QListWidgetItem(q)
            q.setItemWidget(item, self.box_list[i])
            if i == 0:
                self.box_list[i].stateChanged.connect(self.all_selected)
            else:
                self.box_list[i].stateChanged.connect(self.show_selected)
        q.setStyleSheet("font-size: 20px; font-weight: bold; height: 40px; margin-left: 5px")
        self.setStyleSheet("width: 300px; height: 50px; font-size: 21px; font-weight: bold")
        self.text.setReadOnly(True)
        self.setLineEdit(self.text)
        self.setModel(q.model())
        self.setView(q)
    def all_selected(self):
        """
        decide whether to check all
        :return:
        """
        # change state
        if self.state == 0:
            self.state = 1
            for i in range(1, len(self.items)):
                self.box_list[i].setChecked(True)
        else:
            self.state = 0
            for i in range(1, len(self.items)):
                self.box_list[i].setChecked(False)
        self.show_selected()
    def get_selected(self) -> list:
        """
        get selected items
        :return:
        """
        ret = []
        for i in range(1, len(self.items)):
            if self.box_list[i].isChecked():
                ret.append(self.box_list[i].text())
        return ret
    def show_selected(self):
        """
        show selected items
        :return:
        """
        self.text.clear()
        ret = ‘; ‘.join(self.get_selected())
        self.text.setText(ret)
class UiMainWindow(QWidget):
    def __init__(self):
        super(UiMainWindow, self).__init__()
        self.setWindowTitle(‘Test‘)
        self.resize(600, 400)
        combo = ComboCheckBox(["Python", "Java", "Go", "C++", "JavaScript", "PHP"])
        layout = QVBoxLayout()
        layout.addWidget(combo)
        self.setLayout(layout)
if __name__ == "__main__":
    app = QApplication(sys.argv)
    ui = UiMainWindow()
    ui.show()
    sys.exit(app.exec_())