添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
PyQt5系列教程(45):QComboBox的使用

PyQt5系列教程(45):QComboBox的使用

上期我们进行了TIM的模拟,主要涉及到QTreeWidget方面的知识。


今天我们一起通过两个小例子来学习一下QComboBox类。


总体介绍

QComboBox小部件是一个组合的按钮和弹出列表。

QComboBox提供了一种向用户呈现选项列表的方式,其占用最小量的屏幕空间。

组合框是一个显示当前项目的选择小部件,可以弹出可选项目列表。组合框可以是可编辑的,允许用户修改列表中的每个项目。

组合框可以包含图像以及字符串; 当然insertItem()和setItemText()函数需要适当重载。对于可编辑组合框,提供了函数clearEditText(),以清除显示的字符串而不更改组合框的内容。

如果组合框的当前项目发生更改,则会发出两个信号currentIndexChanged()和activated()。无论以编程方式或通过用户交互完成更改,currentIndexChanged()总是被发射,而只有当更改是由用户交互引起时才activated() 。highlighted()信号在用户突出显示组合框弹出列表中的项目时发出。所有三个信号都有两个版本,一个带有str参数,另一个带有int参数。如果用户选择或突出显示一个图像,则只会发出int信号。每当可编辑组合框的文本发生改变时,editTextChanged()信号就会发出。

当用户在可编辑的组合框中输入一个新的字符串时,该小部件可能会插入它,也可能不会插入它,并且可以将它插入到多个位置。默认策略是InsertAtBottom,但您可以使用setInsertPolicy()更改它。

可以使用QValidator将输入约束为可编辑的组合框;请参阅setValidator()。默认情况下,接受任何输入。

例如,可以使用插入函数insertItem()和insertItems()来填充组合框。可以使用setItemText()更改项目。一个项目可以使用removeItem()来移除,所有项目都可以使用clear()来移除。当前项目的文本由currentText()返回,项目的文本编号使用text()返回。当前项目可以使用setCurrentIndex()来设置。 count()返回组合框中的项目数;可以用setMaxCount()设置项目的最大数量。您可以允许使用setEditable()进行编辑。对于可编辑组合框,您可以使用setCompleter()设置自动完成,并且用户是否可以添加重复项由setDuplicatesEnabled()进行设置。

QComboBox为其弹出列表使用模型/视图框架并存储其项目。默认情况下,QStandardItemModel存储项目,QListView子类显示弹出列表。您可以直接访问模型和视图(使用model()和view()),但QComboBox还提供了设置和获取项目数据的函数(例如,setItemData()和itemText())。您还可以设置新的模型和视图(使用setModel()和setView())。对于组合框标签中的文本和图标,将使用具有Qt.DisplayRole和Qt.DecorationRole的模型中的数据。请注意,您不能通过使用setSelectionMode()来更改view()的SelectionMode。

类归属

PyQt5->QtWidgets->QComboBox

继承关系

PyQt5->QObject and QPaintDevice->QWidget->QFontComboBox->QComboBox

更多详细的介绍,请参见官网:

小例子

本期有两个小例子,下面是简单版的。

简单版

这个例子中我们在下拉框中选择内容时,第二行的内容会随着我们选择的不同而发生变化。当我们大声说出: “我要装逼了” ,然后你就可以…

这个例子相对简单,我们把主要代码讲解一下。

核心代码讲解

class ExComboBox(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()
    def init_ui(self):
        label1 = QLabel("你可以选择", self)
        label2 = QLabel("大家静一静,", self)
        self.label3 = QLabel("        ", self)
        infomation = ["我想静静", "我要开始学习了", "我要开始睡觉了", "我要开始装B了"]
        combox = QComboBox(self)
        combox.addItems(infomation)
        self.label3.setText(combox.currentText())
        combox.activated[str].connect(self.zhuangB)
    def zhuangB(self, text):
        self.label3.setText(text)
        if text == "我要开始装B了":
            msgBox = QMessageBox(QMessageBox.NoIcon, '装B'




    
,"让你装B")
            msgBox.setIconPixmap(QPixmap("./res/zhuangB.png"))
            msgBox.setWindowIcon(QIcon("./res/latin_b.png"))
            msgBox.exec()

我们从上面代码中可以看出,QComboBox的基本使用还是挺简单的,创建对象放入数据就行了。我们具体来看下:

infomation = ["我想静静", "我要开始学习了", "我要开始睡觉了", "我要开始装B了"]
combox = QComboBox(self)
combox.addItems(infomation)

创建一个QCombobox对象,然后使用addItems()函数将列表数据放入,就可以使用啦。

当然除了addItems()函数,还有addItem()函数。例如:

combox.addItem(QIcon("./res/latin_b.png"),'这个选项有图标哦')

我们在下拉框的选项中增加一个图标,如下图:


self.label3.setText(combox.currentText())

将label3的文本设置为当前选项的值。

currentText()属性保存当前文本。如果下拉框是可编辑的,则当前文本是下拉框中显示的值。否则,如果下拉框为空或未设置当前项目,则为当前项目的值或空字符串。

   combox.activated[str].connect(self.zhuangB)
def zhuangB(self, text):
    self.label3.setText(text)
    if text == "我要开始装B了":
        msgBox = QMessageBox(QMessageBox.NoIcon, '装B',"让你装B")
        msgBox.setIconPixmap(QPixmap("./res/zhuangB.png"))
        msgBox.setWindowIcon(QIcon("./res/latin_b.png"))
        msgBox.exec()

我们选择下拉框下面的项目时发出activated信号,这个信号会把选中的值(字符串)传递给槽函数zhuangB()。

activated()信号在用户选择下拉框中的项目时发送。 请注意 ,即使选择没有改变,也会发送此信号。除了可以传字符串以外,还可以传项目的索引。如:

combox.activated[int].connect(self.zhuangB)

请酌情考虑使用。

def zhuangB(self, text):
    self.label3.setText(text)
    if text == "我要开始装B了":
        msgBox = QMessageBox(QMessageBox.NoIcon, '装B',"让你装B")
        msgBox.setIconPixmap(QPixmap("./res/zhuangB.png"))
        msgBox.setWindowIcon(QIcon("./res/latin_b.png"))
        msgBox.exec()

当我们选择 “我要开始装B了” ,就会弹出相应的对话框。消息对话框这个知识点请参考:

这里就不再重复了。

你们看看是不是很简单,只要你没有特别要求,使用起来就是这么简单。 so easy!

定制版

我们的标题就是像QQ一样选择好友,这里我们还是来简单的模拟一下。效果如下:

新增联系人到下拉框
选择联系人,下拉框显示QQ号
删除联系人

大家通过这个3个动画可以了解到原来下拉框还可以这样玩啊!嗯,就是这么吊!

好,我们先来看下具体的实现思路!

实现思路

新的概念

在上面的总体介绍中我们知道了:“QComboBox为其弹出列表使用模型/视图框架并存储其项目。默认情况下,QStandardItemModel存储项目,QListView子类显示弹出列表。您可以直接访问模型和视图(使用model()和view()),但QComboBox还提供了设置和获取项目数据的函数(例如,setItemData()和itemText())。您还可以设置新的模型和视图(使用setModel()和setView())”。

也就是说,使用模型和视图(model()和view())可以较为方便的自定义下拉框。

这个貌似理解起来还是有点难啊!

我们再增加一个概念吧!程序设计当中有一个设计模式叫做MVC(Model-View-Controller)设计模式,即模型-视图-控制。这是一种非常典型的设计模式,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

  • Model(模型) 是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
  • View(视图) 是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。
  • Controller(控制器) 是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

部分来源:百度百科

在PyQt中将视图与控制结合在一起,同时添加了代理delegate能够自定义数据条目item的显示与编辑方式。这仍然将数据存储的方式与呈现给用户的方式分开,但是基于相同的原则提供了更简单的框架。

这种分离使得可以在几个不同的视图中显示相同的数据,并实现新类型的视图,而无需更改基础数据结构。 为了灵活处理用户输入,我们引入了delegate(代理或者叫委托,下面统一称为委托)的概念。

在这个框架中拥有一个委托的好处是,它允许对数据项目的呈现和编辑进行自定义。整体体系结构如下:

该模型与数据源通信,为架构中的其他组件提供接口。 通信的性质取决于数据源的类型以及模型的实施方式。

该视图从模型中获得模型索引;这些是对数据项的引用。 通过向模型提供模型索引,视图可以从数据源中检索数据项。

在标准视图中,委托呈现数据项。 编辑项目时,委托使用模型索引直接与模型进行通信。

所以,我们在本次的设计中,我们使用setItemDelegate()函数为QCombobox弹出列表视图设置项目委托,设置属于我们自己的个性化下拉框列表。具体思路如下:

1、 新建一个QListWidget对象 。QListWidget设置为QComboBox的View,QListWidget的Model设置为QComboBox的Model。

2、自定义View类中的Item。继承QWidget,形成一个子类,这个子类就是我们希望出现的下拉框列表中的一项。

具体的实现,我们等会来看下代码。

其它

我们在了解了下拉框的实现方式之后,我们还用Qt设计师设计了一个新增用户的对话框(这个比较简单),以及当我们点击下拉框列表中 × 的时候,我们可以将其从中予以删除。这个我们是通过事件过滤器来实现的,这个知识点我们之前也介绍过的。

核心代码讲解

QCombobox部分

class ChooseUser(QDialog, Ui_Dialog):
    storage_qq = []
    def __init__(self, parent=None):
        super(ChooseUser, self).__init__(parent)
        self.setupUi(self)
        self.setmode()
    def setmode(self):
        with codecs.open("../res/qcombobox.qss", "r", "utf-8"




    
) as f:
            styleSheet = f.readlines()
        style = '\r\n'.join(styleSheet)
        self.comboBox.setStyleSheet(style)
        self.listw = QListWidget()
        self.comboBox.setModel(self.listw.model())
        self.comboBox.setView(self.listw)
    @pyqtSlot()
    def on_toolButton_clicked(self):
        user = DialogAddUser()
        r = user.exec_()
        if r > 0:
            qq, username, user_icon = user.get_userinfo()
            item =  ComboBoxItem(qq, username, user_icon)
            item.itemOpSignal.connect(self.itemOp)
            self.storage_qq.append(qq)
            self.listwitem = QListWidgetItem(self.listw)
            self.listw.setItemWidget(self.listwitem, item)
    def itemOp(self, qq):
        indexqq = self.storage_qq.index(qq)
        self.listw.takeItem(indexqq)
        del self.storage_qq[indexqq]
    @pyqtSlot(int)
    def on_comboBox_activated(self, p0):
        qq = self.storage_qq[p0]
        self.comboBox.setEditText(qq)

这个代码一看就是eric6生成的对话框代码,我们主要来看看具体的实现部分,其它部分大家可以自己下载源码查看。

storage_qq = []

这个列表我们是存储每个联系人QQ号的,如下图:


def setmode(self):
    with codecs.open("../res/qcombobox.qss", "r", "utf-8") as f:
        styleSheet = f.readlines()
    style = '\r\n'.join(styleSheet)
    self.comboBox.setStyleSheet(style)
    self.listw = QListWidget()
    self.comboBox.setModel(self.listw.model())
    self.comboBox.setView(self.listw)

这个表明我们首先要导入样式,给下拉框设置一些好看的样式,如鼠标移动的时候变蓝色、QQ号和姓名变成白色,这些都是通过样式来实现的。


知道不?骚年。(不过,样式部分,我还是不说,等专题,哈哈)

其中有个样式要注意下:

QComboBox QAbstractItemView::item{
    min-height: 60px;
    min-width: 60px;
    outline:0px;
}

这个是用来设置下拉框项目大小的,没有这个样式可能就是这样的,如下图:

很丑,是不是!

self.listw = QListWidget()
self.comboBox.setModel(self.listw.model())
self.comboBox.setView(self.listw)

这个就是我们上面说的:QListWidget设置为QComboBox的View,QListWidget的Model设置为QComboBox的Model

@pyqtSlot()
def on_toolButton_clicked(self):
    user = DialogAddUser()
    r = user.exec_()
    if r > 0:
        qq, username, user_icon = user.get_userinfo()
        item =  ComboBoxItem(qq, username, user_icon)
        item.itemOpSignal.connect(self.itemOp)
        self.storage_qq.append(qq)
        self.listwitem = QListWidgetItem(self.listw)
        self.listw.setItemWidget(self.listwitem, item)

这个函数是我们点击增加好友时响应的操作,如下图:


user = DialogAddUser()

DialogAddUser()是我们自定义的对话框,用于添加一些好友属性:姓名、图标。如下图:


这个对话框的代码很简单,这里不做讲解了,和之前QQ模拟、TIM模拟的差不多。

r = user.exec_()
if r > 0:
    qq, username, user_icon = user.get_userinfo()
    item =  ComboBoxItem(qq, username, user_icon)
    item.itemOpSignal.connect(self.itemOp)
    self.storage_qq.append(qq)
    self.listwitem = QListWidgetItem(self.listw)
    self.listw.setItemWidget(self.listwitem, item)
  • 当我们点击了对话框联系人的时候,我们从对话框中得到QQ号、姓名、图标,分别存入qq, username, user_icon三个变量中。
  • 然后我们创建一个ComboBoxItem对象,这个ComboBoxItem类是我们自定义的,也就是上面说的自定义View类中的Item(具体代码我们等会再说)。
  • 当ComboBoxItem对象发出itemOpSignal信号的时候,我们将其连接到槽函数itemOp()上,itemOpSignal信号也是我们自定义的(具体代码我们等会再说)。
  • 我们把得到的QQ号存储在storage_qq列表中。
self.listwitem = QListWidgetItem(self.listw)

用给定的父项(self.listw)构造指定类型的空列表项目。

self.listw.setItemWidget(self.listwitem, item)

将小部件设置为在给定项目中显示。也就是将我们自定义的Item显示在self.listwitem中。这样就能看到我们自己设定的好友信息啦!

@pyqtSlot(int)
def on_comboBox_activated(self, p0):
    qq = self.storage_qq[p0]
    self.comboBox.setEditText(qq)

当我们选择好友的时候,我们在下拉框的文本框中显示该该好友的QQ号。

ComboBoxItem部分

是时候来看看我们自定义下拉框列表中的项目Item了。

class ComboBoxItem(QWidget):
    itemOpSignal = pyqtSignal(str)
    def __init__(self, qq, username, user_icon):
        super().__init__()
        self.username = username
        self.qq = qq
        self.user_icon = user_icon
        self. initUi()
    def initUi(self):
        lb_username = QLabel(self.username, self)
        lb_qq = QLabel(self.qq, self)
        lb_icon = QLabel(self)
        lb_icon.setPixmap(QPixmap(self.user_icon))
        self.bt_close = QToolButton(self)
        self.bt_close.setIcon(QIcon("../res/close.png"))
        self.bt_close.setAutoRaise(True)
        #布局内容省略
        self.bt_close.installEventFilter(self)
        self.installEventFilter(self)
    def eventFilter(self, object, event):
        if object is self:
            if event.type() == QEvent.Enter:
                self.setStyleSheet("QWidget{color:white}")
            elif event.type() == QEvent.Leave:
                self.setStyleSheet("QWidget{color:black}")
        elif object is self.bt_close:
            if event.type() == QEvent.MouseButtonPress:
                self.itemOpSignal.emit(self.qq)
        return QWidget.eventFilter(self, object, event)

其实这类主要知识点就是在自定义信号和事件过滤器。

itemOpSignal = pyqtSignal(str)

PyQt5自动为所有Qt的内置信号定义信号。可以使用pyqtSignal()工厂将新信号定义为类属性。事实上,采用这种方式可以很方便的为我们通过信号传递参数,如:

#无参数的信号
signal_A = pyqtSignal()
#带一个int类型参数的信号
signal_B = pyqtSignal(int)
#带str和int类型参数的信号
signal_C = pyqtSignal(str, int)
#带一个列表类型参数的信号
signal_D = pyqtSignal(list)
#带int和dict类型参数的信号和带str类型参数的信号
signal_E = pyqtSignal([int, dict], [str])

这样就为我们传递各种参数带来了极大的便利性了。

   self.bt_close.installEventFilter(self)
    self.installEventFilter(self)
def eventFilter(self, object, event):
    if object is self:
        if event.type() == QEvent.Enter:
            self.setStyleSheet("QWidget{color:white}")
        elif event.type() == QEvent.Leave:
            self.setStyleSheet("QWidget{color:black}")
    elif object is self.bt_close: