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一样选择好友,这里我们还是来简单的模拟一下。效果如下:
大家通过这个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: