GUI学习之二十三——QComboBox学习总结
我们在前面分别介绍了两种输入控件:纯键盘文本输入和步长调节器,下面我们来学习下组合框(下拉选择输入)。
一.简介
1.下拉框是一个组合控件(包含一个文本显示控件和一个按钮)。它默认显示最小的控件给用户来操作,并且可以用下拉选择的界面提供给用户更多的预置选项。
2.它是直接继承自QWidget。
二.功能作用
1.构造函数。
可以直接实例化,不用传递参数。
2.数据操作
数据的操作主要分对数据项的增删改和一些其他的操作
a.增加项目
下拉框的内容有两种增加方式:追加和指定位置添加。
QComboBox.addItem(self, text: str, userData: typing.Any = ...) #追加内容 QComboBox.addItem(self, icon: QtGui.QIcon, text: str, userData: typing.Any = ...) #追加内容(带图标) QComboBox.insertItem(self, index: int, text: str, userData: typing.Any = ...) #指定位置添加内容 QComboBox.insertItem(self, indx: int, icon: QtGui.QIcon, text: str, userData: typing.Any = ...) #指定位置添加内容(带图标)
可以看到有个参数是userdata,其实就是可以附带的内容,在面板上不显示,但是可以包含的内容。比方我们选择区号,面板上只显示地名,选择好后后台接收的内容是地名对应的数字。
还有一种增加的方法:批量增加。用一个可迭代的数据就可以
QComboBox.addItems(self, texts: typing.Iterable[str])
QComboBox.insertItems(self, index: int, texts: typing.Iterable[str])
这里的可迭代对象可以是元组、列表,但内容必须是字符串类型的。但要注意的是虽然字符串也属于可迭代对象,但这里是不能用的。
b.删除项目
删除项目很简单,只要指定需要删除的项目的所索引值就可以了
QComboBox.removeItem(self, index: int) #删除项目
c.改指定项目
QComboBox.setItemIcon(self, index: int, icon: QtGui.QIcon) #改指定的项目图标 QComboBox.(self, index: int, text: str) #改指定项目的显示文本 QComboBox.setItemData(self, index: int, value: typing.Any, role: int = ...) #改指定项目的userdata
D.编辑当前显示文本
如果没有指定的索引值或文本内容,则显示状态不变。
QComboBox.setCurrentIndex(self, index: int) #按指定索引值显示 QComboBox.setCurrentText(self, text: str) #按指定文本显示
E.插入分割线
QComboBox.insertSeparator(self, index: int) #在指定索引位置插入分割线
F.被编辑状态
如果控件设置了可被编辑,就可以用键盘输入新的内容。在有新的文本被输入,控件失去焦点后,所输入的文本会自动添加在最后面,就像qq登陆时的记住账号一样,只要输入一遍就有这个选项了。
QComboBox.setEditable(self, editable: bool) #设置可被编辑
这个编辑状态还可以结合当前文本的显示
QComboBox.setEditText(self, text: str) #设置当前显示的文本
这里有个现象,就是如果在调用上面这条指令前文本框里显示的有图标,在用这条这里后图标时不会变化的,只是后面的文本改了。敲一下回车就好了。
G.插入模型(树形表)
这个用法以后再详细讲,现在就说一下是怎么用的
from PyQt5.Qt import * import sys class Window(QWidget): def __init__(self): super().__init__() self.UI_test() def UI_test(self): cb = QComboBox(self) model = QStandardItemModel() #创建标准树形视图模型 item1 = QStandardItem('item1') item2 = QStandardItem('item2') item2_1 = QStandardItem('item2_1') item2.appendRow(item2_1) #把item2_1列为item2的子列表 model.appendRow(item1) model.appendRow(item2) cb.setModel(model) cb.setView(QTreeView(cb)) #试图设置 pass if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec_())
H.数据的获取
QComboBox.count() #获取项目个数——>int QComboBox.itemIcon(index=) #获取指定项目的图标对象——>QIcon QComboBox.itemText(index=) #获取指定项目的文本——>str QComboBox.itemData(index=) #获取指定项目的userdat——>Any QComboBox.currentIndex() #获取当前项目的索引值——>int QComboBox.currentText() #获取当前项目文本——>str QComboBox.currentData() #获取当前项目的userdata——>Any
这里插播一个lambda的用法:有些信号是带有传递的参数的,但是如果我们不想用这个参数时,可以把这个参数屏蔽掉,比如我们添加一个按钮,点击按钮后获取最后一个项的文本
cb = QComboBox(self) cb.addItems(['a','b','c']) cb.move(100,200) btn = QPushButton('test',self) btn.move(100,150) btn.clicked.connect(lambda val-1 = cb.count():print(cb.itemText(val)))
点击按钮发现打印的时False,为什么呢?因为按钮再被clicked的时候是会发送一个布尔量作为参数的,那么val再有参数进来时时用实际传递的参数。这时候就需要把这个参数屏蔽掉
btn.clicked.connect(lambda _,val = cb.count()-1:print(cb.itemText(val)))
I.数据的限制
一般数据的限制是和可编辑的状态同时使用的,用了数据限制可以限制数据的条数。
QComboBox.setMaxCount() #设置可以存储的最大项数 QComboBox.maxCount() #获取可以存储的最大项数 QComboBox.setMaxVisibleItems() #设置展示的最大项数 QComboBox.maxVisibleItems() #获取展示的最大项数
存储个数达到上限了是不会有新的顶进来的,条目是不会变化的。而展示个数是限制了展示的个数,超出后会有个滚动条展示出来,可以用滚动条切换显示的内容
图中的最大展示数量就是6,而项目数应该有10个。
J.常规操作
QComboBox.setDuplicatesEnabled(self, enable: bool) #设置可重复性 QComboBox.duplicatesEnabled() #是否可以被重复 QComboBox.setFrame(self, a0: bool) #设置框架边框 QComboBox.hasFrame() #是否有框架边框 QComboBox.setIconSize(self, size: QtCore.QSize) #设置图标尺寸 QComboBox.iconSize() #获取图标尺寸
K.调整尺寸策略
QComboBox.setSizeAdjustPolicy(self, policy: 'QComboBox.SizeAdjustPolicy') type: 'QComboBox.SizeAdjustPolicy' AdjustToContents #始终根据内容调整 AdjustToContentsOnFirstShow #固定在提一次提示时的大小 AdjustToMinimumContentsLength #最小宽度 AdjustToMinimumContentsLengthWithIcon #包含图标的最小宽度
L.清除内容和弹出列表
QComboBox.clear() #清空空间里的所有项目 QComboBox.clearEditText() #清除当前显示内容 QComboBox.showPopup() #弹出项目列表
M.完成器和验证器
完成器的用法和QLineEdit的方法差不多,但是一般都是结合了下拉列表框里的文本内容生成一个可迭代对象给setCompleter()。验证器的用法也和前面讲的验证器差不多。包含了验证规则和修正方法。
QComboBox.setCompleter(self, c: 'QCompleter') #完成器具 QComboBox.setValidator(self, v: QtGui.QValidator) #验证器
三.信号
1.条目改变
QComboBox.activated(self, index: int) #参数为int QComboBox.activated(self, a0: str) #重载后的用法,参数为str
条目改变的信号必须是用户选中的,用代码操作时是不发送信号的。
2.当前条目改变
QComboBox.currentIndexChanged(self, index: int) #参数为int QComboBox.currentIndexChanged(self, a0: str) #重载后的用法,参数为str QComboBox.currentTextChanged(self, a0: str) #当前文本发生变化 QComboBox.editTextChanged(self, a0: str) #当前编辑文本变化
当前条目改变是有些情况下可以通过代码调用信号的。而当前文本发生变化,是只要显示的字符串发生变化就发送信号(被编辑时只要有变化就每变化一次发送一次),而索引变化时被编辑只要不确认就不会发送信号。当前文本发生变化和编辑文本发生变化的效果基本一致。
3.高亮发生变化
QComboBox.highlighted(self, index: int) #高亮发生变化,参数为int QComboBox.highlighted(self, a0: str) #高亮发生变化的重构,参数为str
高亮发生变化是只只要鼠标指向的条目发生变化就发送信号。
四.案例
如图,有这些要求
1.下拉控件1是省份,2是城市,还有一个显示控件显示城市对应的区号
2.城市随着省份的变化会变化
3.省份、城市和区号始终显示,不能改完城市才显示。
from PyQt5.Qt import * import sys class Window(QWidget): def __init__(self): super().__init__() self.UI_test() def UI_test(self): province = QComboBox(self) province.resize(150,30) province.move(200,200) self.province = province city = QComboBox(self) city.resize(150,30) city.move(400,200) le = QLineEdit(self) le.resize(150,30) le.move(200,250) self.le = le self.city = city self.city_dic = {'河南':{'郑州':'0371', '安阳':'0372', '洛阳':'0379'}, '陕西':{'西安':'029', '宝鸡':'0917', '渭南':'0913'}, '河北':{'石家庄':'0311', '保定':'0312', '邯郸':'0310'}} province.addItems(self.city_dic.keys()) province.currentIndexChanged[str].connect(self.change_pro) self.change_pro(province.currentText()) city.currentIndexChanged[str].connect(self.change_city) self.change_city(city.currentText()) pass def change_pro(self,pro): self.city.clear() citys = self.city_dic[pro] for k,val in citys.items(): self.city.addItem(k,val) #把区号作为userdata保存给控件 def change_city(self,cit): if len(self.city.currentText()) == 0: #在清除城市列表时会打印空行 pass else: self.le.setText(self.city.currentData()) if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec_())
注意的几点:
1.因为数据字典是两级的,如果获得区号需要遍历字典才可以,效率太低。可以把第二级字典作为userdata传给控件2
2.要求3里不能用代码调用信号,在程序里直接用代码调用了一遍函数作为初始化。
3.每次改省份时需要把city的内容clear一遍,但是city的内容在clear的时候会调用其对应的函数,每次都会多打印一个空白行,这里做了个if的判断
针对上面的其实还有可以优化的地方
1.对第2条来说,其实初始化时候用的currentIndexChanged()应该是可以调用信号的但为什么不行呢?因为在初始化的时候我们把给控件传递item的时候放在了连接信号槽的前面了,其实放在后面就不用特别手动调用函数了。
2.对第3条,在clear的时候直接断开槽,在clear以后重新连接槽就可以了。
from PyQt5.Qt import * import sys class Window(QWidget): def __init__(self): super().__init__() self.UI_test() def UI_test(self): province = QComboBox(self) province.resize(150,30) province.move(200,200) self.province = province city = QComboBox(self) city.resize(150,30) city.move(400,200) le = QLineEdit(self) le.resize(150,30) le.move(200,250) self.le = le self.city = city self.city_dic = {'河南':{'郑州':'0371', '安阳':'0372', '洛阳':'0379'}, '陕西':{'西安':'029', '宝鸡':'0917', '渭南':'0913'}, '河北':{'石家庄':'0311', '保定':'0312', '邯郸':'0310'}} province.currentIndexChanged[str].connect(self.change_pro) city.currentIndexChanged[str].connect(self.change_city) province.addItems(self.city_dic.keys()) pass def change_pro(self,pro): self.city.blockSignals(True) self.city.clear() self.city.blockSignals(False) self.city.currentIndexChanged[str].connect(self.change_city) citys = self.city_dic[pro] for k,val in citys.items(): self.city.addItem(k,val) #把区号作为userdata保存给控件 def change_city(self,cit): self.le.setText(self.city.currentData()) if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec_())