Qt Style Sheet實踐(一):按鈕及關聯菜單


導讀

     正如web前端開發中CSS(Cascade Style Sheet)的作用一樣,Qt開發中也可以使用修改版的QSS將邏輯業務和用戶界面進行隔離。這樣,美工設計人員和邏輯實現者可以各司其職而不受干擾。更重要的是,由於界面和邏輯處理是分離的,低耦合性使得代碼重構的工作量可以減少到最小。QSSCSS的語法幾乎一致,除了Qt自身增加的一些屬性之外,其余的屬性都可以在CSS2或CSS3中找到對應的屬性。因此,如果曾經有過CSS的使用經驗,那么QSS的使用將游刃有余。關於QSS的使用實踐,打算撰寫一系列博客來記錄使用過程中的一些技巧和方法。本篇是系列第一篇,主要探討QPushButtonQMenu在QSS的作用下的效果。

QSS介紹

     QSS(Qt Style Sheet)借鑒於CSS的良好思想,實現了界面和邏輯的分離。QSS中引入了盒模型(Box Model)概念,這是樣式表技術中的核心概念之一。具體的解釋網上說的挺多的,Qt本身自帶的文檔也有較為詳細的說明。在使用盒模型進行設計之前,我們得了解下Qt中哪些組件可以用盒模型進行布局設計:

Qt中可以應用盒模型的組件類
QCheckBox QCheckBox的勾選符號可以使用::indicator子組件來定制。默認情況下,勾選標記位於組件矩形的左上角。QCheckBox的spacing屬性可以用於指定勾選標記和文本內容之間的間距。
QComboBox 對於QComboBox而言,支持盒模型的其實是包裹QComboBox的外框(Frame),QComboBox的下拉單按鈕通過::drop-down子組件來定制,默認情況下下拉單按鈕位於盒模型中padding矩形的右上角。下拉按鈕中的箭頭號通過::down-arrow子組件進行定制,箭頭號默認位於子組件的正中央。
QGroupBox QGroupBox的標題用::title子組件進行定制,標題的位置依QGroupBox::textAlignment的具體值而言。對於可選的QGroupBox而言,標題中還會包含一個勾選標記,勾選標記用::indicator來定制,spacing仍然用於設置勾選標記與文本的間距。
QSpinBox(QDateEdit,QDateTimeEdit) 如圖所示,默認情況下spinbox右部分成上下兩個按鈕。以向上的箭頭為例,::up-button和::up-arrow分別用於定制按鈕及位於按鈕中的箭頭號。箭頭號默認位於按鈕的中間,對於向下的按鈕類似,只是用::down-button和::down-arrow子組件。
QToolBox QToolBox是一個具備QQ折疊功能的組件,因此其中的獨立的page使用::tab子組件定制。::tab組件支持一些偽狀態::only-one, :first, :middle, :previous-selected, :next-selected, :selected,從而達到定制特定page的目的。
QMenuBar 菜單欄組件的spacing屬性可指定菜單項之間的間距,單個菜單項還可以通過::item子組件定制風格。但是值得注意的是,由於MAC下菜單欄集成到了系統菜單欄,此時樣式表會失去作用。
QProgressBar 進度條組件使用::chunks子組件來定制進度條樣式,text-align屬性用於設定進度條中文本的對齊方向:left, center, right
QScrollBar 滾動條的組成其實非常復雜,依據垂直和水平方向的不同,由::handle, ::add-line, ::sub-line, ::add-page, ::sub-page, ::right-arrow,  ::left-arrow, ::down-arrow, ::up-arrow等子組件組成。偽狀態:horizontal, :vertical用於確定滾動條的方向,width(min-width), height(min-height)則可確定滾動條的不同長和寬。
QToolBar 工具欄的偽狀態:top, :left, :right, :bottom的使用依賴於工具欄的具體位置;而:first, :last, :middle, :only-one則用於指代工具欄中的具體位置。工具欄的分隔器用::separator子組件指代,::handle則指代移動工具欄的handle.
QMenu 菜單中的獨立項使用::item子組件定制,除了常見的偽狀態,::item還支持:selected, :default, :exclusive以及:non-exclusive等偽狀態。利用這些偽狀態,可以為不同狀態的菜單項定制出不同的外觀。對於可勾選的菜單項,使用::indicator對勾選標記進行定制,::separator則定制菜單項之間的分隔符;對於有子菜單的菜單項,其箭頭號可以用::right-arrow, ::left-arrow進行定制,還有::scroller及::tearoff兩個子組件,暫時沒搞清楚具體作用。
QLabel QLabel不支持:hover偽狀態,自Qt4.3開始,給QLabel設置樣式表也就隱式指定了QFrame::frameStyle屬性。
QLineEdit 對於QLineEidt,selection-color, selection-background-color屬性分別指定了選中文本的文本顏色和背景色,lineedit-password-character屬性說明密碼輸入顯示的字符。將在后面的實踐中說明。
QPushButton 支持:default, :flat, :checked偽狀態,對於具備關聯菜單的按鈕,可以用::menu-indicator來定制下拉菜單標記。而:open和:closed偽狀態則分別用於定制菜單打開和關閉時按鈕的外觀。
QRadioButton 同上,::indicator用於定制文本前面的選項框,spacing指定文本與選項框之間的間距。
QSlider 對於水平的QSlider,min-width和height屬性必須同時提供;對於垂直的QSlider, 必須同時提供min-height和width屬性。QSlider由::groove和::handle兩部分組成。::groove子組件是一條槽,供::handle在上面滑動。
QSplitter 窗體分割器,主要的部件是::handle。通過::handle可以動態改變分割器中的不同子窗口大小。
QTextEdit 使用selection-color, selection-background-color屬性定制,其他的定制方式見QAbstractScrollArea。
QToolButton 如果QToolButton關聯了一個菜單,那么和QPushButton是相同的處理方式。如果被設置成了QToolButton::MenuButtonPopup模式,那么::menu-button用於繪制菜單按鈕,而::menu-arrow用於繪制按鈕中的箭頭號。注意:如果設置了QToolButton的背景色,那么必須還要設置邊框的寬度才會起作用。這是因為QToolButton默認繪制的邊框會完全遮擋住用戶設置的背景色。
QAbstractScrollArea 所有派生自QAbstractScrollArea類的子類,包括QTextEdit, QAbstractItemView,都可以通過設置background-attachment屬性來實現可滾動背景。通過給background-attachment設置fixed和scroll,背景會固定不動或者跟隨滾動。
QHeaderView QHeaderView是Model/View框架中的一部分,最重要的子組件是::section,::section支持:middle, :first, :last, :only-one, :next-selected, :previous-selected, :selected, :checked等偽狀態。::up-arrow和::down-arrow用於定制表頭的排序標記。
QListView(QListWidget) show-decoration-selected屬性控制選中時是選中整項還是僅僅只是項的文本,其他和QTableView相同。
QTableView(QTableWidget) 當view支持斑馬色條時,alternate-background-color屬性指定備選色實現斑馬色帶,selection-color和selection-background-color屬性指定選定項的文本色和背景色。注意:保證同時設置了背景色和邊框寬度值。
QTreeView(QTreeWidget) show-decoration-selected屬性控制選中時是選中整項還是僅僅只是項的文本, 子組件::branch和::item用於精細化控制。

 

應用實例

     下面看看如何用QSS對按鈕及其關聯菜單進行外觀定制。我們首先用如下的代碼初始化好按鈕及其關聯菜單,並在Windows 7默認主題下看看其效果:

ui.serviceType->setFixedWidth(95);
m_mainMenu = new QMenu(this);
m_osSubMenu = new QMenu(this);
m_appSubMenu = new QMenu(this);

m_details = new QAction(QStringLiteral("Details"),this);
m_details->setCheckable(true);
m_details->setChecked(true);
m_settings = new QAction(QStringLiteral("Settings"), this);
m_settings->setIcon(QIcon(":/misc/preference"));
m_settings->setShortcut(QKeySequence::Print);
m_os = new QAction(QStringLiteral("OS"), this);
m_app = new QAction(QStringLiteral("Applications"), this);

m_github = new QAction(QStringLiteral("Github"), this);
m_github->setIcon(QIcon(":/app/github"));
m_github->setShortcut(QKeySequence("Ctrl+G"));
m_amazon = new QAction(QStringLiteral("Amazon"), this);
m_amazon->setIcon(QIcon(":/app/amazon"));
m_photoshop = new QAction(QStringLiteral("Photoshop"), this);
m_photoshop->setIcon(QIcon(":/app/photoshop"));
m_facebook = new QAction(QStringLiteral("Facebook"), this);
m_facebook->setIcon(QIcon(":/app/facebook"));

m_apple = new QAction(QStringLiteral("Apple"), this);
m_apple->setShortcut(QKeySequence("Ctrl+A"));
m_apple->setIcon(QIcon(":/os/apple"));
m_windows = new QAction(QStringLiteral("Windows"), this);
m_windows->setIcon(QIcon(":/os/windows"));
m_windows->setShortcut(QKeySequence("Ctrl+W"));
m_fedora = new QAction(QStringLiteral("Fedora"), this);
m_fedora->setIcon(QIcon(":/os/fedora"));
m_fedora->setDisabled(true);

m_osSubMenu->addAction(m_apple);
m_osSubMenu->addAction(m_windows);
m_osSubMenu->addAction(m_fedora);
m_os->setMenu(m_osSubMenu);

m_appSubMenu->addAction(m_amazon);
m_appSubMenu->addAction(m_github);
m_appSubMenu->addAction(m_facebook);
m_appSubMenu->addSeparator();
m_appSubMenu->addAction(m_photoshop);
m_app->setMenu(m_appSubMenu);

m_mainMenu->addAction(m_details);
m_mainMenu->addSeparator();
m_mainMenu->addAction(m_os);
m_mainMenu->addAction(m_app);
m_mainMenu->addAction(m_settings);

ui.serviceType->setMenu(m_mainMenu);

  先不加任何QSS效果,其效果如下:

    一片灰蒙蒙的感覺,不亮堂。對於講究實用性的軟件產品,做到這一步已然足夠。如若客戶要求具備個性一點的外觀呢?此時此刻,我們可以嘗試用QSS來進行改造。我們將所有的樣式語句放到一個*.qss文件中,然后在main函數中加載。需要注意的是,我們應該將.qss文件添加到.qrc文件中進行編譯。每一次修改.qss文件之后應該重新編譯.qrc文件。否則在界面上將看不出任何改變。代碼如下:

QFile file(":/ThemeRoller/style");
file.open(QFile::ReadOnly);
qApp->setStyleSheet(file.readAll());
file.close();

  先考慮將QPushButton作為練手對象,編寫如下QSS代碼:

QPushButton {
	background: white;
	border: 1px solid rgb(41, 57, 85);
	border-radius: 3px;  # 設置邊框具備3個像素的圓角
	font-weight: bold;    # 字體設置為加粗
}

QPushButton:hover {
	background: lightgray;
}

  效果對比如下:

     效果似乎還不錯,但是我們發現右邊的箭頭號已經偏移到右下角去了,不太和諧。我們嘗試使用subcontrol-positionsubcontrol-origin兩個屬性來進行調整(position和origin這兩個屬性在CSS中是非常容易被混淆的,具體含義需細細區分):

QPushButton::menu-indicator{
    subcontrol-position: right center;
    subcontrol-origin: padding;
}

     顯然,系統默認的箭頭號不太和諧,於是我們再嘗試換掉這個箭頭號,並且在菜單打開時設置為向下的箭頭號,菜單關閉時設置為水平向右的箭頭號:

QPushButton::menu-indicator:open {
	image: url(:/misc/down_arrow_2);
	subcontrol-position: right center;
	subcontrol-origin: padding;
}

QPushButton::menu-indicator:closed {
	image: url(:/misc/right_arrow_2);
	subcontrol-position: right center;
	subcontrol-origin: padding;
}

  得到的效果如下:

     好吧,到此位置我們的按鈕似乎好看多了。再來看看整個關聯菜單的QSS該如何編寫。首先,把背景色調整為白色是必須的,如下:

QMenu {
	background-color: white;
	padding: 1px;  # 縮小菜單項四個方向的padding
}

QMenu::item{
	background-color: transparent;
}

  

      我們可以發現一個嚴重的缺陷,當鼠標划過相應的菜單項時,文本內容看不見了,顯然是由於背景色的原因,所以我們還得修改一下啊:

QMenu::item:selected{
	background-color: rgb(234, 243, 253);
	color: black;
}

  用偽狀態:selected進行設置,當鼠標划過時將文本顏色設置為黑色,也即保持不變。但此時我們根本看不到鼠標划過的效果,因此給當前選中的菜單項一個背景色吧(rgb(234, 243, 253))。效果如何呢:

       根據不同的需要,定制出來的外觀也是千差萬別的。主要是能理解好QSS中各種屬性的作用,其余的工作就是做好布局設計和圖片設計。美觀大方的界面設計離不開精致的圖標設計和合理的布局管理。

參考

  1. Qt style sheet reference 


免責聲明!

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



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