導讀
正如web前端開發中CSS(Cascade Style Sheet)的作用一樣,Qt開發中也可以使用修改版的QSS將邏輯業務和使用者介面進行隔離。這樣,美工設計人員和邏輯實現者可以各司其職而不受干擾。更重要的是,由於介面和邏輯處理是分離的,低耦合性使得程式碼重構的工作量可以減少到最小。QSS和CSS的語法幾乎一致,除了Qt自身增加的一些屬性之外,其餘的屬性都可以在CSS2或CSS3中找到對應的屬性。因此,如果曾經有過CSS的使用經驗,那麼QSS的使用將遊刃有餘。關於QSS的使用實踐,打算撰寫一系列部落格來記錄使用過程中的一些技巧和方法。本篇是系列第一篇,主要探討QPushButton及QMenu在QSS的作用下的效果。
QSS介紹
QSS(Qt Style Sheet)借鑑於CSS的良好思想,實現了介面和邏輯的分離。QSS中引入了盒模型(Box Model)概念,這是樣式表技術中的核心概念之一。具體的解釋網上說的挺多的,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) | ![]() |
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-position和subcontrol-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中各種屬性的作用,其餘的工作就是做好佈局設計和圖片設計。美觀大方的介面設計離不開精緻的圖示設計和合理的佈局管理。