我們在前面分別介紹了兩種輸入控件:純鍵盤文本輸入和步長調節器,下面我們來學習下組合框(下拉選擇輸入)。
一.簡介
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_())