導讀
組合框是一個重要且應用廣泛的組件,一般由兩個子組件組成:文本下拉單部分和按鈕部分。在許多既需要用戶選擇、又需要用戶手動輸入的應用場景下,組合框能夠很好的滿足我們的需求。如我們經常使用的聊天軟件QQ登錄框,便是一個很好的應用例子:
顯然,用戶既可以自己手動輸入新的QQ號碼,也可以在列表框中選擇歷史輸入記錄。對於提高用戶體驗是一個不錯的手段。這篇博文重點講述如何用QSS對組合框進行定制。
基本自定義
組合框的使用非常簡單,為了加快敘述速度,我們直接在Qt Designer中拖一個QComboBox控件放到主窗口中。此時,我們什么都不用做就有了一個簡單的組合框,如下:
但很顯然,我們得添加一個文字,否則QComboBox不會顯示任何內容。這樣出現的組合框樣式很普通:一個文本加一個帶箭頭號的按鈕就完了。既然主題是用QSS來定制組合框,那么我們第一件事就是新建一個.qss文件並添加到資源文件中進行編譯。.qss文件的內容初步編寫如下:
QComboBox { border: 1px solid gray; border-radius: 3px; padding: 1px 2px 1px 2px; # 針對於組合框中的文本內容 min-width: 9em; # 組合框的最小寬度 }
我們給組合框3個像素的圓角,邊框1個像素寬並將顏色設置為灰色。看看效果:
文本框部分似乎還不錯,但是右邊的按鈕外觀實在是太丑了,和整體風格不搭。我們繼續美化一下按鈕。按鈕是QComboBox的一個子組件,用::drop-down指代。編寫如下QSS代碼:
QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 20px; border-left-width: 1px; border-left-color: darkgray; border-left-style: solid; /* just a single line */ border-top-right-radius: 3px; /* same radius as the QComboBox */ border-bottom-right-radius: 3px; } QComboBox::down-arrow { image: url(:/misc/down_arrow_2); }
可以看到,我們分別將按鈕右上角和右下角設置了3個像素的圓角,這是因為我們前面給組合框的整體邊框設置了圓角。如果不給按鈕設置圓角,那么按鈕的棱角將會遮擋住整體邊框的圓角效果。另外,我們改變了按鈕上的箭頭圖標。::down-arrow也是一個子組件,我們用image屬性替換了系統默認的圖標。對比一下:
嗯,整體風格上看起來協調些了。當然了,在::drop-down子組件的定制中,我們將subcontrol-position屬性設置成了top, right。這樣按鈕就位於最右邊了。如果希望將按鈕置於最左邊顯然也很簡單。只需要將subcontrol-position設置為top, left,然后改變一下QComboBox的padding值就可以達到目的了。我們再拉出下拉框看看:
有什么問題呢?顯然,下拉框中的選項高度太小了,看起來挺別扭的。那么如何對下拉框進行定制呢?我們有個很好的模仿對象:
360安全衛士的登錄框中的下拉框看起來就挺不錯,而且還有圖標出現在選項的右邊。下面我們就進入高級定制部分。看看又該如何進行改進。
高級自定義
要實現上述效果,我們首先要做的就是將QComboBox設置為可以編輯的(setEditable())。這樣,文本框中的內容才可以手動進行輸入。另外,我們還注意到,下拉框中的選項右邊還有圖標出現,QQ的登錄框中也出現了圖標。我們最直觀的想法就是用布局管理器(水平或垂直的)將所有組件組裝成一個整體,然后再添加到下拉框中去。
怎么做呢?幸運的是,QComboBox內部也是Model/View框架來維護下拉框內容的。因此,最直接的方法就是定義一個QListWidget,將這個QListWidget設置為QComboBox的View,而將QListWidget的Model設置為QComboBox的Model。QListWidget只是一個View類,因此我們還得自定義View類中的Item啊。
那好,自QWidget派生一個子類,實現水平布局,將所有子組件添加到里面去:
ComboboxItem::ComboboxItem(QWidget *parent) : QWidget(parent) { m_img = new QLabel(this); QPixmap pic(QStringLiteral(":/misc/preference")); m_img->setPixmap(pic); m_img->setFixedSize(pic.size()); m_label = new QLabel(this); m_layout = new QHBoxLayout(this); m_layout->addWidget(m_label); m_layout->addStretch(); m_layout->addWidget(m_img); m_layout->setSpacing(5); m_layout->setContentsMargins(5, 5, 5, 5); setLayout(m_layout); }
代碼很簡單,定義了兩個標簽QLabel,一個顯示文本,一個顯示圖標。用水平布局管理器添加到QWidget中去。
ThemeRoller::ThemeRoller(QWidget *parent) : QMainWindow(parent) { ui.setupUi(this); m_listWidget = new QListWidget(this); // 設置子項目代理,否則下拉框選項周圍會出現虛線框 m_listWidget->setItemDelegate(new NoFocusFrameDelegate(this)); ui.comboBox->setEditable(true); ui.comboBox->setModel(m_listWidget->model()); ui.comboBox->setView(m_listWidget); // 在下拉框中添加5個選項 for (int i = 0; i < 5; ++i) { ComboboxItem* item = new ComboboxItem(this); item->setLabelContent(QString("Account") + QString::number(i, 10)); connect(item, SIGNAL(chooseAccount(const QString&)), this, SLOT(onChooseAccount(const QString&))); QListWidgetItem* widgetItem = new QListWidgetItem(m_listWidget); m_listWidget->setItemWidget(widgetItem, item); } }
我們還將ComboboxItem的chooseAccount()信號關聯到了onChooseAccount()槽。這樣,當用戶點擊了選項中的某一個選項時,能夠在QComboBox的文本框中顯示選中的項。那么,QSS該如何編寫呢?
QComboBox QAbstractItemView::item { height: 25px; } QListView::item { background: white; } QListView::item:hover { background: #BDD7FD; }
也很簡單,只是設置了選項中的高度,和QComboBox的高度保持一致,這樣看起來不至於別扭。然后給選項設置了鼠標懸停背景色。至此,整個定制過程就結束了。看看效果如何:
小結
QComboBox分成三個定制部分:文本框(是否可編輯),按鈕(箭頭標記、邊框),下拉框(選項高度、子組件布局)。由以上可見,我們利用QSS可以很好的實現出自己想要的效果,而且效果還不賴。