零基礎學Qt 4編程實例之Qt 樣式表的應用


下面我們以一個實例來講解樣式表的應用。這個例子取材於Qt Demo,比較復雜,有一定難度,基本上覆蓋了前面幾章講述的各種技能點,主要包括:

◆ 如何自定義Qt 的樣式表

◆ 如何在應用程序中應用樣式表

◆ 如何不使用樣式表來設置應用程序的樣式

◆ 如何使用單繼承法從.ui文件創建派生類

◆ 如何自定義資源集文件

◆ 如何使信號和槽自動連接

◆ 如何在兩個窗口之間建立關聯

◆ 元對象系統方法的使用

這個程序名字叫stylesheet,其運行后的效果如圖9-17所示。

 

圖9-17 實例運行效果

該例子基於主窗口樣式,有些類似於我們在網上所常見的填寫個人資料的網站注冊程序。

程序有兩個主菜單,依次點擊【File】->【Edit Style】菜單項,將彈出如圖9-18所示的設置樣式表的對話框,在其中內置了幾種樣式供選擇,使用者也可以在編輯框中輸入自定義的樣式。設置完成后,主界面的窗口部件的樣式將依此相應的變化。里面的Coffee樣式自定義了push button、frames和tooltip,但使用了下層的風格 (例如這里是Windows XP風格)來繪制checkbox,combobox和radio button。Pagefold風格完全重新定義了對話框中使用的所有控件的外觀,從而實現了一種獨特的,平台無關的外觀。

 

圖9-18 樣式表編輯器

這個程序里面包含如下原生源文件:

◆ mainwindow.ui

◆ stylesheeteditor.ui

◆ stylesheet.qrc

◆ /qss/coffee.qss

◆ /qss/default.qss

◆ /qss/pagefold.qss

◆ /images/*.png

◆ mainwindow.h

◆ mainwindow.cpp

◆ stylesheeteditor.h

◆ stylesheeteditor.cpp

其中,mainwindow.ui和stylesheeteditor.ui分別是主程序和樣式編輯器的界面布局文件,是使用Qt Designer制作的。Stylesheet.qrc是資源集文件,在其中描述了程序中用到的樣式表文件和圖片文件的位置和名稱。在qss文件夾中包含了3個.qss文件,它們描述了程序中用到的樣式,我們的程序將讀取它們並轉換成樣式表。在images文件夾中放置了程序中用到的圖片文件。mainwindow.h和mainwindow.cpp構成了程序中的主程序類,而stylesheeteditor.h和stylesheeteditor.cpp構成了樣式編輯器類。

下面我們就結合源代碼為大家講解這個程序的功能是怎樣實現的。

首先看看mainwindow.h

1    #ifndef MAINWINDOW_H
2    #define MAINWINDOW_H
3    #include <QtGui>
4    #include "ui_mainwindow.h"
5    class StyleSheetEditor;
6    class MainWindow : public QMainWindow
     {
7           Q_OBJECT
8           public:
9                  MainWindow();
10          private slots:
11                 void on_editStyleAction_triggered();
12                 void on_aboutAction_triggered();
13          private:
14                 StyleSheetEditor *styleSheetEditor;
15                 Ui::MainWindow ui;
     };
16   #endif

第1、2和第16行一起構成了頭文件的預定義衛哨,這樣做的目的是為了防止程序中重復定義或包含頭文件,是嚴謹的編程風格,建議大家遵循這一范例的做法。

第3行引入QtGui模塊的聲明,它包含了QMainWindow類的定義。

第4行引入ui_mainwindow.h的聲明。

第5行采用前置聲明的方式引入樣式編輯器類StyleSheetEditor。

第6行聲明主程序類MainWindow單公有繼承自QMainWindow類。

第7行時必需的,因為要用到Qt的核心機制,如信號/槽機制等。

第8、9行聲明MainWindow類的構造函數。

第10-12行聲明私有槽,它們的命名遵循了Qt信號/槽的“自動關聯規則”。

第13-15行聲明了私有成員,它們分別是樣式編輯器類的對象和主程序界面類的對象。從這里也可以看出,我們對.ui文件的引入將采用單繼承的方式。

下面再來看一下mainwindow.cpp文件的內容。

1    #include "mainwindow.h"
2    #include "stylesheeteditor.h"
3    MainWindow::MainWindow()
     {
4           ui.setupUi(this);
5           ui.nameLabel->setProperty("class", "mandatory QLabel");
6           styleSheetEditor = new StyleSheetEditor(this);
7           statusBar()->addWidget(new QLabel(tr("Ready")));
8           connect(ui.exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
9           connect(ui.aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
     }
10   void MainWindow::on_editStyleAction_triggered()
     {
11          styleSheetEditor->show();
12          styleSheetEditor->activateWindow();
     }
13   void MainWindow::on_aboutAction_triggered()
     {
14          QMessageBox::about(this, tr("About Style sheet"),
                   tr("The <b>Style Sheet</b> example shows how widgets can be styled "
                   "using <a href=/"http://doc.trolltech.com/4.5/stylesheet.html/">Qt "
                   "Style Sheets</a>. Click <b>File|Edit Style Sheet</b> to pop up the "
                   "style editor, and either choose an existing style sheet or design "
                   "your own."));
     }

第1、2兩行引入程序中用到的頭文件聲明。

第4行初始化界面布局,setupUi()函數一般要放在構造函數內的第一行。

第5行設置nameLabel的屬性,setProperty()方法的原型如下:

     bool QObject::setProperty ( const char * name, const QVariant & value )

它將對象的name的屬性值設置為 value。對於本句來說,就是把nameLabel的class屬性值設置為"mandatory QLabel",這將使得該窗口部件被突出顯示。

第6行實例化styleSheetEditor類的對象,其父窗口為MainWindow。

第7行為狀態欄添加一個窗口部件,顯示"Ready"字樣,即漢語的“就緒“。

第8、9行分別連接菜單項exitAction和aboutQtAction的觸發信號triggered()和全局對象qApp的quit()槽和aboutQt()槽。

第10-12行定義了on_editStyleAction_triggered()槽函數。

第11行顯示樣式編輯器窗口。

第12行調用activateWindow()方法把樣式編輯器窗口設置為活動窗口。activateWindow()方法的原型如下:

     void QWidget::activateWindow ()

    它用來把某個頂層窗口設置為當前的活動窗口。

小貼士:活動窗口與如何設置活動窗口

所謂活動窗口就是當前能夠擁有鍵盤輸入焦點的,可見的頂層窗口。

activateWindow()方法的作用與用鼠標單擊頂層窗口的標題欄的效果是一樣的。在X11上,這個效果並不確定,因為它依賴於具體的窗口管理器,如果要確保某窗口為活動窗口,最好是在調用activateWindow()方法之后再調用raise()方法,后者本身也是Qt內置的一個槽函數。但無論如何,在這之前都需要確保該窗口首先是可見的,否則將沒有任何效果。

在Windows平台上,這種情形又有些差異。因為Microsoft不允許一個應用打斷由另一個應用建立起來的和用戶的對話(即交互過程),所以activateWindow()被調用后,該窗口部件可能還是不能成為頂層的活動窗口,而只是它的標題欄變為高亮,以告訴用戶該窗口部件的狀態發生了某些變化。這一點,請讀者朋友在使用時注意。

如果想知道某窗口是否為活動窗口,可以調用isActiveWindow()方法。

前面我們曾經講到過Qt核心機制中的“資源集文件“。現在我們看一下如何在Qt Creator中書寫或配置資源集文件。

在Qt Creator中用鼠標雙擊資源集文件,將打開資源集文件編輯器,在資源集編輯器中可以增加或刪除文件、節點等,其操作比較方便,一目了然。如圖9-19所示。

圖9-19 資源集文件編輯器

我們再來看一下stylesheeteditor.h文件的內容。

     #ifndef STYLESHEETEDITOR_H
     #define STYLESHEETEDITOR_H
     #include <QDialog>
     #include "ui_stylesheeteditor.h"
1    class StyleSheetEditor : public QDialog
     {
2           Q_OBJECT
3           public:
4                  StyleSheetEditor(QWidget *parent = 0);
5           private slots:
6                  void on_styleCombo_activated(const QString &styleName);
7                  void on_styleSheetCombo_activated(const QString &styleSheetName);
8                  void on_styleTextEdit_textChanged();
9                  void on_applyButton_clicked();
10          private:
11                 void loadStyleSheet(const QString &sheetName);
12                 Ui::StyleSheetEditor ui;
     };
13   #endif

第1行聲明StyleSheetEditor類單公有繼承自QDialog。

第5-9行聲明了程序中的私有槽,命名遵循了Qt信號/槽的“自動關聯規則”。

第11行聲明了私有方法loadStyleSheet(),它用來從.qss文件中讀取內容並轉化問樣式表。

第12行聲明了私有成員變量ui。

再來看一下stylesheeteditor.cpp。

     #include <QtGui>
     #include "stylesheeteditor.h"
1    StyleSheetEditor::StyleSheetEditor(QWidget *parent)
                   : QDialog(parent)
     {
2           ui.setupUi(this);
3           QRegExp regExp(".(.*)//+?Style");
4           QString defaultStyle = QApplication::style()->metaObject()->className();
5           if (regExp.exactMatch(defaultStyle))
            {
6                  defaultStyle = regExp.cap(1);
            }
7           ui.styleCombo->addItems(QStyleFactory::keys());
8           ui.styleCombo->setCurrentIndex(ui.styleCombo->findText(defaultStyle, Qt::MatchContains));
9           ui.styleSheetCombo->setCurrentIndex(ui.styleSheetCombo->findText("Coffee"));
10          loadStyleSheet("Coffee");
     }
11   void StyleSheetEditor::on_styleCombo_activated(const QString &styleName)
     {
12          qApp->setStyle(styleName);
13          ui.applyButton->setEnabled(false);
     }
14   void StyleSheetEditor::on_styleSheetCombo_activated(const QString &sheetName)
     {
15          loadStyleSheet(sheetName);
     }
16   void StyleSheetEditor::on_styleTextEdit_textChanged()
     {
17          ui.applyButton->setEnabled(true);
     }
18   void StyleSheetEditor::on_applyButton_clicked()
     {
19          qApp->setStyleSheet(ui.styleTextEdit->toPlainText());
20          ui.applyButton->setEnabled(false);
     }
21   void StyleSheetEditor::loadStyleSheet(const QString &sheetName)
     {
22          QFile file(":/qss/" + sheetName.toLower() + ".qss");
23          file.open(QFile::ReadOnly);
24          QString styleSheet = QLatin1String(file.readAll());
25          ui.styleTextEdit->setPlainText(styleSheet);
26          qApp->setStyleSheet(styleSheet);
27          ui.applyButton->setEnabled(false);
     }

第3-6行調用Qt中的正則表達式來取得默認的樣式。

第3行定義一個正則表達式變量regExp。

第4行是Qt元對象機制( Meta-Object System)的應用。

首先通過style()方法獲得應用程序的樣式,然后利用Qt元對象機制獲取元對象信息。style()方法是QApplication類的靜態方法,其原型如下:

     QStyle * QApplication::style ()   [static]

    它將返回應用程序的樣式對象。

    這里簡單的講解一下Qt元對象機制,詳細的內容請見第13章。

    Qt的元對象系統是Qt的核心機制之一,Qt的信號/槽機制、屬性系統、運行時型別信息等機制都是以元對象系統為基礎的。在應用程序中,每一個QObject的子類都會有一個單獨的Qt元對象實例,這個實例中保存了該類所有的元對象信息,該實例可以通過調用QObject::metaObject()方法來得到。

而QMetaObject被稱作是Qt的元對象類,它包含了Qt對象的元信息。

QObject::metaObject()方法的原型如下:

     const QMetaObject * QObject::metaObject () const   [virtual]

    可以看到,它返回本對象的指向其元對象的指針。

用來獲取元對象信息的方法有很多,本程序中用到的className()是其中的一種,它返回類的名字。

第5、6兩行應用正則表達式的校驗來獲得defaultStyle的值。

第8行是為styleCombo設置當前下拉框列表中的默認選項。findText()是QcomboBox類的方法,其原型如下:

     int QComboBox::findText ( const QString & textQt::MatchFlags flags = Qt::MatchExactly | Qt::MatchCaseSensitive ) const

它在QComboBox對象的item列表中搜索與text相匹配的項的索引值,如果沒有相匹配的項,則返回-1,該值是int型的。flags參數給出了搜索的方法,即如何與text相匹配。flags的值取自枚舉值Qt::MatchFlag,后者描述了在一個模型中搜索時可以遵循的匹配原則,如表9-2所示。

表9-2 MatchFlag的取值

常量

說明

Qt::MatchExactly

0

執行QVariant匹配(QVariant可以看做是Qt的最常用變量類型的聯合體)

Qt::MatchFixedString

8

執行按字符匹配。注意這種方式默認情況下不區分大小寫,只有同時指定Qt::MatchCaseSensitive 才區分大小寫。

Qt::MatchContains

1

搜索條件包含在(QComboBox的下拉列表)項目中

Qt::MatchStartsWith

2

匹配條件是與項的開頭相匹配,即“以XXX開頭”

Qt::MatchEndsWith

3

搜索的條件與項的結尾相匹配,即“以XXX為結尾”

Qt::MatchCaseSensitive

16

執行大小寫敏感匹配搜索

Qt::MatchRegExp

4

以一個正則表達式為匹配條件執行按字符匹配搜素,注意這種方式不區分大小寫,除非同時指定Qt::MatchCaseSensitive 條件。

Qt::MatchWildcard

5

 

Qt::MatchWrap

32

執行類似“令牌環”式的搜索,對每一個項都要經過驗證

Qt::MatchRecursive

64

執行遞歸搜索

所有上述這些標記符號都是QFlags< MatchFlag >的一部分,它們之間的組合可以用OR來連接,在程序中即是使用|符號。這些標記符號經常與QRegExp類結合使用。

第9行與第8行同理,設置styleSheetCombo的當前項為Coffee,也即當前的樣式表的名稱為Coffee。

緊接着,第10行調用loadStyleSheet()方法相應的設置應用程序的樣式表為Coffee。

第21-27行是loadStyleSheet()方法的定義。

第22行使用QFile類的對象根據實參取得Coffee樣式表對應的.qss文件全名,即帶路徑的Coffee.qss。

第23行以只讀方式打開Coffee.qss文件。

第24行讀取文件的全部內容。QLatin1String類的構造函數原型如下:

     QLatin1String::QLatin1String ( const char * str )

注意它的參數是const char * str,而不是QString類的對象。

小貼士:QLatin1String類的使用

關於QLatin1String類的使用有很多話題。這里只講一下最為基本的部分。

QLatin1String類為采用ASCII/Latin-1編碼形式的字符串操作提供了一個輕量級的封裝。

通常在操作字符串時,Qt推薦開發者盡量不要直接使用QString類,因為它的速度比較慢。替代的方法一種是使用const char*,使用它可以避免創建一個臨時的QString對象,這樣就節省了開銷。很多QString的成員函數如insert()、replace()、index()等都接受const char*作為參數或返回值。舉個例子,在下面的所有的示例代碼中假設變量str是QString類型。

     if (str == "auto" || str == "extern"
                    || str == "static" || str == "register") 
     {
             ...
     }

上面和下面的兩段代碼實現了相同的操作。由於下面的代碼在執行期間創建了4個臨時的QString變量並且對字串值進行了深度拷貝,增大了程序運行的開銷,所以上面這段代碼的執行速度就要明顯的快於下面這段代碼。

    if (str == QString("auto") || str == QString("extern")
                    || str == QString("static") || str == QString("register")) 
     {
             ...
     }

如果使用QLatin1String類,代碼可以寫成下面這樣,書寫起來比較費力氣些,但是代碼執行的效率很高,比使用QString::fromLatin1()要快得多。

    if (str == QLatin1String("auto")
                    || str == QLatin1String("extern")
                    || str == QLatin1String("static")
                    || str == QLatin1String("register") 
     {
             ...
     }

基本上在各種場合都可以使用QLatin1String來代替QString,就像下面代碼中示例的這種用法。

     QLabel *label = new QLabel(QLatin1String("MOD"), this);

第25行使用setPlainText()方法把styleTextEdit要顯示的內容設置為樣式表文件的內容。

第26行設置應用程序的樣式表。

代碼段中的其它部分比較簡單,不再贅述了。

再來看一看main.cpp的內容。

     #include <QtGui>
     #include "mainwindow.h"
     int main(int argc, char *argv[])
     {
1           Q_INIT_RESOURCE(stylesheet);
2           QApplication app(argc, argv);
3           MainWindow window;
4           window.show();
5           return app.exec();
     }

這里第1行最為重要,要使用資源集文件,就要使用Q_INIT_RESOURCE宏。

小貼士:Q_INIT_RESOURCE宏的使用

Q_INIT_RESOURCE宏的原型如下:

     void Q_INIT_RESOURCE ( name )

該宏的作用是初始化在name指定的.qrc文件中的資源文件。通常,Qt在應用程序初始化時自動加載資源。注意,在某些平台上使用靜態鏈接庫時,必須使用Q_INIT_RESOURCE()宏來存儲資源。

舉個例子,如果應用程序中用到的資源文件列在名為myapp.qrc的資源集文件中,那么為了確保資源在應用程序初始化時被加載,你需要確保在主程序的main()中加上下面這句:

    Q_INIT_RESOURCE(myapp);

有兩點需要注意,一是參數name的命名必須符合標准C++對於變量命名的規范,不可以含有不合適的字符。

第二,該宏不能用在名字空間里面。它必須在main()主函數中被調用。如果必須在名字空間中使用,可以使用下面的示例代碼。

    inline void initMyResource() { Q_INIT_RESOURCE(myapp); }
 
     namespace MyNamespace
     {
             ...
 
             void myFunction()
             {
                    initMyResource();
             }
     }

項目文件stylesheet.pro可以像下面這樣書寫。

1    TEMPLATE = app
2    TARGET = 
3    DEPENDPATH += .
4    INCLUDEPATH += .
5    # Input
6    HEADERS += mainwindow.h stylesheeteditor.h
7    FORMS += mainwindow.ui stylesheeteditor.ui
8    SOURCES += main.cpp mainwindow.cpp stylesheeteditor.cpp
9    RESOURCES += stylesheet.qrc

第1行表明應用程序的模板為app。第5行是注釋,第9行表明要包含用到的資源集文件。

通過本實例,大家可以看到,樣式表是一種在運行時解釋的普通文本文件,使用它們不需要具備編程知識。我們可以在應用程序級別和窗口級別設置樣式表。另外,如果在不同的級別都設置了樣式表,則應用程序將繼承所有有效的樣式,你看到的程序樣子是這些效果的疊加,這被稱作是樣式表效果的層疊(cascading)。

 

FROM:http://blog.csdn.net/qter_wd007/article/details/6171173


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com