PyQt5系列教程(45):像QQ一樣選擇好友(QComboBox的使用,補充前一章節的知識點)
來自專欄 PyQt5圖形界面編程
上期我們進行了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
更多詳細的介紹,請參見官網:
QComboBox Class | Qt Widgets 5.10小例子
本期有兩個小例子,下面是簡單版的。
簡單版
這個例子中我們在下拉框中選擇內容時,第二行的內容會隨著我們選擇的不同而發生變化。當我們大聲說出:「我要裝逼了」,然後你就可以…
這個例子相對簡單,我們把主要代碼講解一下。
核心代碼講解
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了」,就會彈出相應的對話框。消息對話框這個知識點請參考:
學點編程吧:PyQt5系列教程(11):今天的消息挺全哦!這裡就不再重複了。
你們看看是不是很簡單,只要你沒有特別要求,使用起來就是這麼簡單。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 =
.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 =
.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: if event.type() == QEvent.MouseButtonPress: self.itemOpSignal.emit(self.qq) return QWidget.eventFilter(self, object, event)
為bt_close按鈕和ComboBoxItem自身安裝事件過濾器。當我們滑鼠移入下拉框列表項目中的時候,字體變成白色;滑鼠移走的時候,字體變成黑色。
而點擊bt_close按鈕的時候,發射itemOpSignal信號,這會帶上QQ號的哦。事件過濾器的知識點可以參考:
學點編程吧:PyQt5系列教程(12):構建我們自己的密碼輸入框現在我們再看下itemOpSignal信號連接到的槽函數。
def itemOp(self, qq): indexqq = self.storage_qq.index(qq) self.listw.takeItem(indexqq) del self.storage_qq[indexqq]
我們根據得到的QQ號得到相應的索引,然後根據索引刪除self.listw的項目以及storage_qq列表中存儲的數據完成最後的刪除操作。
總結
今天我們學習了以下幾個知識點:
- QComboBox小部件
- delegate(代理或者叫委託)的概念
- 複習了事件過濾器這個知識點
最後
ok,今天的介紹就到這裡吧。如果你喜歡本篇文章,請給我點贊
讚賞(推薦)
分享給你的好友們吧!
關注微信公眾號:學點編程吧,發送pyqt545,可以獲得本期的代碼!
加油!(? ??_??)? (*?????)
推薦閱讀: