Qt Style Sheet實踐(二):組合框QComboBox的定制


導讀

     組合框是一個重要且應用廣泛的組件,一般由兩個子組件組成:文本下拉單部分和按鈕部分。在許多既需要用戶選擇、又需要用戶手動輸入的應用場景下,組合框能夠很好的滿足我們的需求。如我們經常使用的聊天軟件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可以很好的實現出自己想要的效果,而且效果還不賴。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM