零基础学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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值