19-Look and feel customization

在某些環境中,我們可能會想改變一下QT內置部件的外觀。可能我們只想做一點微小的改變,或者將它完全實現爲另一種風格,給我們的應用一種獨特的,與衆不同的外觀。這裏有三種不同的方法來重定義Qt內置控件的外觀。

  • 我們可以子類化單獨的控件,並重新實現它們的繪圖和鼠標事件。使用這種方法你可以完全控制它並實現你的想法,但是你需要做很多工作。

  • 也可以子類化QStyle或者預定義一個類如QWindowsStyle.此方法非常強大。它通常被用來根據不同的平臺提供此平臺本地化的外觀。

  • 從Qt4.2開始,Qt style sheet成爲了我們的另一種選擇。Qt style sheets靈感來源於HTML(Cascading
    Style Sheets)。我們並不需要掌握任何的編程知識就可以掌控Qt style
    sheets,因爲它只是一種在運行時被解釋的普通文本。

我們已經在第5,7章討論過第一種技術,儘管我們的重點放在了創建自定義部件。在這章,我們將回顧一下最後兩種方法。我們將呈現兩種自定義的風格:Candy風格,由style sheet指定。Bronze 風格,通過子類化QStyle實現(如 19.1所示)。爲保證對話框大小可控,所有的控件都是經過小心挑選的。
這裏寫圖片描述

使用Qt Style Sheets

Qt style sheets靈感來源於CSS,但它適用於控件。一個style sheets由style規則組成,style規則影響控件的渲染。所有的style規則都是普通文本。由於style sheets在是運行時解析的,通過Qt Designer’s Style editor或者開發時內嵌一個QTextEdit,使用-stylesheet file.css命令行選擇爲一個應用指定一個style sheet,我們可以很容易地使用不同的設計做實驗。
Style sheets被應用於當前活動QStyle的最頂層(如,QWindowsVistaStyle或QPlastiqueStyle)。由於創建style sheets不牽涉任何子類化,所以它們是對存在控件微小自定義的最佳選擇。假設我們想用黃色作爲QLineEdit的背景色,可以使用下面的style sheet:

    QLineEdit {
        background-color: yellow;
    }

對於CSS來說,QLineEdit被稱作(選擇器)selector, background-color被稱爲屬性(attribute),yellow爲值(value)。

對於這類型的自定義來說,使用style sheet往往能產生比擺弄控件的調色板更可靠的效果。因爲QPalette入口(Base, Button, Highlight等等)因不同的風格使用各異。比如,QWindowsStyle使用Base調色板入口填充一個只讀combobox背景,然而QPlastiqueStyle使用Button入口。再者,傳入整個調色板,特定風格使用硬編碼來渲染特定元素。而style sheets恰恰相反,它保證不管哪個QStyle是活動的,指定的顏色會被恰當使用。

QApplication::setStyleSheet()爲整個應用設置一個style sheet:
qApp->setStyleSheet(“QLineEdit {background-color: yellow; }”);

我們也可以對一個控件(widget)設置style sheet,它的子控件使用QWidget::setStyleSheet()。如
dialog->setStyleSheet(“QLineEdit { background-color: yellow; }”)

如果我們對QLineEdit直接設置style sheet,我們可以忽略QLineEdit選擇器(selector)和花括號:
lineEdit->setStyleSheet(“background-color: yellow;”);

目前爲止,我們只爲單一控件類設置了單個屬性。在實際應用中,經常多種Style rules結合使用。如,下面的Style rules爲六個控件類及它們的子類設置了前景色和背景色:

QCheckBox, 
QCombBox, 
QLineEdit, 
QListView, 
QRadioButton, 
QSpinBox {
    color: #050505;
    background-color: yellow;
}

顏色也可以由名字指定,由HTML風格的字符串#RRGGBB格式,或者RGB或RGBA值:

QLineEdit {
   color: rgb(0, 88, 120);
   background-color: rgba(99%, 78% 12% 59%);
}

使用名字時,我們可以使用任何QColor::setNamedColor()能識別的顏色名。對於RGB來說,我們必須指定red, green, blue部分,每部分爲0到255或0%到100%。RGBA允許我們指定額外的alpha部分,alpha對應於顏色的透明度。我們也可以指定一個調色板入口或者梯度(gradient)。

QLineEdit {
    color: palette(Base);
    background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,
                      stop: 0 white, stop: 0.4 gray, stop 1: green)
}

使用background-image屬性,我們可以爲背景指定圖片。

背景色默認從控件左上角開始展開(不包括任何margin),在橫向,縱向重複以填滿整個部件(widget).此行爲可以使用background-position和background-repeat屬性配置. 如:

QLineEdit {
    background-image: url(:/images/yellow-bg.png);
    background-position: top right;
    background-repeat: repeat-y;
}

如果同時指定背景圖和背景色,背景色會透過背景圖的半透明部分。

目前爲止,我們討論過的所有的選擇器(selector)都是類名。我們還可以使用其它的選擇器(selector);如,如果我們想爲OK和Cancel按鈕指定前景色,可以這樣寫:

QPushButton[text="OK"] {
    color: green;
}
QPushButton[text="Cancel"] {
    color: red;
}

此種選擇器語法適用於任何Qt屬性,儘管我們必須記住Style sheet並不知道什麼時候一個屬性被修改。選擇器也可以以多種方式結合。如選擇所有名爲”okButon”,且x, y屬性爲0,名爲”frame”的QFrame的直屬子控件的QPushButton。我們可以這樣寫:

QFrame#frame > QPushButton[x="0"][y="0"]#okButton {
    ...    
}

即使Qt沒有定義任何mandatoryFiled屬性,我們仍然可以很容易地創建一個:QObject:setProperty().從Qt4.2開始,動態設置一個不存在的屬性值會導致此屬性被創建。如:

nameLineEdit->setProperty(“mandatoryFiled”, true);
genderComboBox->setProperty(“mandatoryFiled”, true);
ageSpinBox->setProperty(“mandatoryFiled”, true);

對於一個有着許多line editors和comboboxes的表格應用來說,將mandatory fileds的背景色設爲黃色是非常普遍的。我們假設將此約定應用到我們的應用,我們以下列開始我們的style sheet:

*[mandatoryFile="true"] {
    background-color: yellow;
}

Style sheet不僅僅在控制顏色方面有用,它也可以用來調整部分的大小和位置。例如,以下的規則用來增加checkbox的大小,radio按鈕的指示器(indicator)到20px,並確保指示器(indicator)和文本之間的間隙和8pixels。

QCheckBox::indicator, QRadioButton::indicator {
    width: 20px;
    height: 20px;
}
QCheckBox, QRadioButton {
    spacing: 8px;
}

注意第一條rule的選擇器(selector)語法。我們寫的是QCheckBox而不是QCheckBox::indicator,我們本應該指定整個部件的大小而不是它們的指示器(indicator),第一條規則於圖19.3中解釋。
這裏寫圖片描述

除了輔助控制,Style sheet也可以引用部件(widget)的狀態。例如,當鼠標懸停於checkbox之上時,我們想將checkbox的文本顏色指定爲白色:

QCheckBox:hover {
    color: white;
}

狀態由一個:指示,而subcontrol由::指示。我們可以依次指定多個狀態,每個狀態由:隔開。此種情況適用於所有狀態的滿足。例如,以下規則只適用於鼠標懸停於一個checked checkbox:

QCheckBox:checked:hover {
    color: white;
}

如果我們想將之適用於任一狀態,,應該使用多個選擇器(selector),並使用,隔開:

QCheckBox:hover, QCheckBox:checked {
    color: white;
}

邏輯非用!表示:

QCheckBox: !checked {
    color: blue;
}

狀態可與子控制相結合:

QComboBox::drop-down:hover {
    image: url(:/images/downarrow_bright.png);
}

Style sheet可與其它技術相結合來表達更復雜的自定義。例如,假如我們想放置一個小“erase”按鈕到一個QLineEdit框中,於文本的左邊。這涉及到創建一個EraseButton類,將之放於QLineEdit之上(如,使用Layout),並且爲輸入文本預留空間,防止文本與按鈕衝突。用子類化QStyle來實現很不方便,這意味着我們還必鬚子類化此應用使用的每種風格(QWindowsVistaStyle, QPlastiqueStyle, etc).使用style sheet只需以下幾行文本就可以實現:

QLineEdit {
    padding: 0px 15px 0px 0px;
}

padding屬性讓我們指定部件的top, right, bottom, lef填充。此填充被插入在QLineEdit的文本的框架之間。CSS也定義了padding-top, padding-right, padding-bottom和padding-left,以便我們想單獨設定某一值。如:

QLineEdit {
    padding-right: 16px;
}

如可以使用Style sheet自定義的Qt widget一樣, QLineEdit支持box模型(box model)如圖19.6所示. 此模型指定了四個影響部件佈局和渲染的矩形。

  1. contents rectangle是最內層的矩形。這是實際內容繪畫的地方。
  2. padding rectangle包圍contents rectangle。它將padding屬性指定的任何填充都計算在內。
  3. border rectangle又包圍着padding rectangle。它爲邊框預留空間。margin rectangle是最外層的矩形。它包圍着border rectangle並將任何指定的margin計算在內。
  4. 對於一個沒有padding, 沒有border,沒有margin的一個普通部件來說,the four rectangle coincide exactly.
    這裏寫圖片描述

我們將呈現一個使用Style sheet自定義的風格–Candy. 圖19.7爲顯示了Candy風格的部分部件。使用圖19.6的box模型,Candy自定義了QLineEdits,QListViews, QPushButtons。接下來我們將一一呈現Style sheet的實現。整個的Style sheet的實現可以在本書提供的例子中找到,qss/Candy.qss位於Candy例子目錄中。
這裏寫圖片描述

對話框內的部分如圖19.1所示。對話框本身的背景圖使用如下規則設置:

QDialog { [R1]
    background-image: url(:/images/background.png);
}

以下規則設置QLabels的color和font屬性:

QLabel { [R2]
    font: 9pt;
    color: rgb(0, 0, 127);
}

以下規則定義了對話框部件類QLineEdit和QListView的外觀:

QLineEdit,
QListView { [R3]
    color: rgb(127, 0, 64);
    background-color: rgb(255, 255, 241);
    selection-color: white;
    selection-background-color: rgb(191, 31, 127);
    border: 2px groove gray;
    border-radius: 10px;
    padding: 2px 4px;
}

爲了使QLineEdit和QListView更出彩,我們對常規和選中的文本都定義了前景色和背景色。除此之外我們爲border屬性定義了一個gray 2-pixel-wid “grooved” border。我們還可以單獨指定border-width, border-style, border-color. 當然,還可以通過指定border-radius爲邊框倒角,我們已經使用過以10pixel爲半徑的border-radius了。圖19.8提供了一個圖表,它表示了邊框和填充的改變。爲保證文本不與圓角”碰撞”,我們指定了2pixel縱向的填充,4pixel的橫向填充。對於QListViews來說,縱向的填充看起來不太對,所以我們使用以下規則將之覆蓋:

QListView { [R4]
    padding: 5px 4px;
}

這裏寫圖片描述
當同一部件的同一屬性被多次設置,只有最後一條規則起作用。

對QPushButtons來說,我們可以使用一個完全不同的方法。我們可以使用一個圖片作爲背景,而不是使用style sheet來畫。爲了了按鈕更容易擴展,按鈕背景使用CSS邊框圖片機制。

不像由background-image指定的背景圖片,邊框圖片被切割成3x3的格子。如圖19.9所示。當填充部件背景時,四個角A,C,G,I不變,其它5個部分被拉伸,平鋪來填滿可用空間。
這裏寫圖片描述

邊框圖片由border-image屬性指定,它要求我們輸入圖片文件名和”四刀”來定義9個格子。切割被定義爲距離上,右,下,左的相素數。如下:

border-image: url(border.png) 4, 8, 12, 16;

使用邊框圖片的時候,我們必須顯式指定border-width。正常情況下,border-width對應於切割的地方。除非四個角落將被拉伸或者壓縮來適應border-width.以border.png爲例,我們應該這樣指定邊框寬度:

border-width: 4px 8px 12px 16px;

現在我們知道了邊框圖片的工作機制,我們現在來看看它怎麼用在風格化Candy QPushButtons。下面定義了按鈕正常狀態下是怎麼被定義的:

QPushButton { [R5]
    color: white;
    font: bold 10pt;
    border-image: url(:/images/button.png) 16;
    border-width: 16;
    padding: -16px 0px;
    min-heigth: 32px;
    min-width: 60px;
}

在以上style sheet中,四次切割距離34x34像素的邊框圖片邊緣16像素,如圖19.10所示。因爲四個切割都是一致的,所以我們只需爲四個切割寫一個”16”,爲邊框寬度寫“16px;

在QPushButton的例子中如圖19.10,邊框圖片中的D,E,F區域被丟棄,因爲按鈕的高度不夠而容不下。B,H被橫向拉伸來填充額外的區域。
這裏寫圖片描述

邊框圖片的標準用法是提供一個邊框,將部件圍繞其中。但是我們已經破壞了此標準用法,並使用它來創建了部件的背景。結果造成E區被丟棄,padding rectangle的高度爲0.爲了給按鈕的文本創造空間,我們指定了一個爲-16pixel的縱向padding。圖19.11解釋了此情形。如果我們用邊框圖片(border image)來創建一個實際的邊框,文本與邊框衝撞可能不會是我們想要的結果。但是我們用它來創建一個可擴展的(scalable)背景,我們可能更想讓文本在之上而還是在它裏面。
這裏寫圖片描述

我們使用min-width和min-height來設置按鈕內容的最小值。選定值保證爲邊框圖片角預留了足夠的空間,OK按鈕一般要比剛好需要的值寬一些,好讓它與Cancel按鈕看起來更自然一些。

先前的QPushButton規則適用於所有的按鈕。現在我們定義一些只適用於特定狀態的按鈕規則。

QPushButton:hover { [R6]
    border-image: url(:/images/button-hover.png) 16;
}

當鼠標懸停於一個按鈕之上,:hover狀態爲真,此時的規則將會覆蓋之間的規則。我們使用此項技術指定一個更明亮的邊框圖片,來獲得一個更漂亮的懸停效果。先前指定的其它按鈕屬性規則同樣適合,只是border-image這個屬性改變了。

QPushButton:pressed { [R7]
    color: lightgray;
    border-image: url(:/images/button-pressed.png) 16;
    padding-top: -15px;
    padding-bottom: -17px;
}

當用戶按下按鈕,我們前景色變爲亮灰色,用一個更加暗邊框圖片,並通過把padding往下移動製造出按鈕文本往下沉的效果。

我們最後的風格規則(style rules)將自定義QComboBox的外觀。爲了顯示style sheets的操控性和精確性,我們將可編輯comboboxes與不可編輯comboboxes區別開來,如圖19.12.只讀combobox被渲染成一個按鈕加上一個位於右邊的向下箭頭。然而,可編輯comboboxes由一個類似QLineEdit的部件和一個小圓角按鈕組成。這意味着我們可以使用大部分定義好的QLineEdit,QListView和QPushButtons的規則。
這裏寫圖片描述

用來定義QLineEdit部件的規則可以用來風格化可編輯comboboxes:

QComboBox:editable,
QLineEdit,
QListView { [R3A]
    color: rgb(127, 0, 63);
    background-color: rgb(255, 255, 241);
    selection-color: white;
    selection-background-color: rgb(191, 31, 127);
    border: 2px groove gray;
    padding: 2px 4px;
}

爲QPushButton正常狀態定義的規則也適用於只讀comboboxes:

QComboBox: !editable,
QPushButton { [R5A]
    color:white;
    font: bold 10pt;
    border-image: url(:/images/button.png) 16;
    border-width: 16px;
    padding: -16px 0px;
    min-height: 32px;
    min-width: 60px;
}

當懸停於一個只讀combobox或懸停於一個可編輯comboboxes的下拉按鈕的時候應該改變背景圖片,就像對QPushButton的做法一樣:

QComboBox:!editable:hover,
QComboBox:drop-down:editable:hover,
QPushButton:hover { [R6A] 
    border-image: url(:/images/button-hover.png) 16;
}

按一個只讀combobox就像按一個QPushButton:

QComboBox:!editable,
QPushButton:pressed { [R7A]
    color: lightgray;
    border-image: url(:/images/button-pressed.png) 16;
    padding-top: -15px;
    padding-top: -17px;
}

重用R3, R5, R6, R7節省很多時間同時也使我們的風格保持一致。但我們還沒有定義畫下拉按鈕的規則,定義如下:

QComboBox::down-arrow {
    image: url(:/images/down-arrow.png); [R8]
}

我們自己提供下拉按鈕的圖片,所以會比標準的箭頭大一些:

QComboBox::down-arrow { [R9]
    top: 1px;
}

如果combobox是打開的,那麼箭頭向下移一個像素.

QComboBox * { [R10]
    font: 9pt;
}

當用戶點擊一個combobox的時候,combobox顯示一系統條款. R10保證combobox的彈出不會繼承規則R5A指定的大字體。

QComboBox::drop-down:!editable { [R11]
    subcontrol-origin: padding;
    subcontrol-position: center right; 
    width: 11px;
    height: 6px;
    background: none;
}

使用subcontrol-origin和subcontrol-position屬性,我們將下拉箭頭放在只讀combobox使用的右方填充矩形的縱向中心。除此之外,我們設置它的大小與文本按鈕內容對應,11x6pixel大小的圖片,我們禁止它用背景圖,因爲我們的下拉按鈕只由下拉箭頭組成。

QComboBox: !editable { [R12]
    padding-right: 15px;
}

至於只讀combobox,我們指定15pixel的填充來保證文本不會覆蓋下拉箭頭。圖19.13展示了尺寸的作用。
這裏寫圖片描述

對於可編輯combobox來說,我們需要配置下拉按鈕使之看起來像小按鈕。

QComboBox::drop-down:editable {  [R13]
    border-image: url(:/images/button.png) 16;
    border-width: 10px;
    subcontrol-origin: margin;
    subcontrol-position: center right;
    width: 7px;
    height: 6px;
}

我們指定邊框圖片爲button.png。然而,這些我們指定邊框寬度爲10pixel而不是16pixel,使圖片更小一些。指定內容的固定大小爲橫向7pixel,縱向6pixel。圖19.14爲我們做法示意圖。
Figure 19.14

如果combobox展開,我們爲下拉箭頭使用一個更暗的圖片:

QComboBox::drop-down:editable:open { [R14]
    border-image: url(:/images/button-pressed.png) 16;
}

爲給下拉按鈕提供空間,我們爲可編輯combobox指定右邊緣大小爲29pixel.如19.15所示。

QComboBox:editable {
    margin-right: 29px;
}

這裏寫圖片描述
現在我們已經完成了Candy style sheet的創建。此style sheet大約100行,使用了幾張自定義圖片,我們得到的效果是一個完全不同的對話框。
使用style sheet來創建自己風格會經歷很多困難和錯誤, 尤其對於沒有使用過CSS的人來說.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章