10.3 编译时加入处理.ui 文件的方法

编译时加入处理.ui 文件通常可以采用 3 种方式:直接使用法、单继承法和多继承法。 下面我们就以 Calculator Form 这个程序为例,分别介绍这 3 种方式的使用。 Calculator Form 程序主要实现了简单的加法计算功能,在 Qt Designer 中绘制的用户界面如图 10-2 所示。

图 10-2 Calculator Form 的.ui 文件

10.3.1 直接使用法

本小节例子的代码是来自源码包中的 calculatorform-direct 实例。

1.使用要点

这种方法的要领是创建一个子类化 QWidget 的自定义类,把它作为“容器”来存放在 calculatorform.ui 中描绘的窗体。

2.实例讲解

我们的应用程序工程中通常包含一个 main.cpp 文件以及一个.ui 文件。 如果全部采用手写代码的话,我们需要在 qmake 工程文件.pro 中加入几行:

TEMPLATE = app
FORMS = calculatorform.ui
SOURCES = main.cpp

第 2 句告诉 qmake 使用 uic 编译 calculatorform.ui 文件,这将生成

ui_calculatorform.ui 文件,通过前面对该文件内容的分析,我们知道这个文件中有一个界 面实体类,其中包含所有的界面元素的描述和配置情况 ,并且 qmake 为我们生成了一个单公有继承自这个界面实体类的一个子类 - CalculatorForm,它位于 Ui 名字空间中。

qmake 将据此生成 ui_calculatorform.h 文件,我们需要在 SOURCES 变量中列出的文件 加入这个文件的声明(这里只有 main.cpp 文件):

#include "ui_calculatorform.h"

在 main()函数中我们创建了一个标准的 QWidget 实例,并把它作为容器用来“存放”在 calculatorform.ui 中描述的窗体。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QWidget *widget = new QWidget;
    Ui::CalculatorForm ui;
    ui.setupUi(widget);
    widget->show();
    return app.exec();
}

Ui:: CalculatorForm 即是单公有继承自界面实体类的子类,它继承了界面实体类的公有方法 setupUi(),我们调用它实现界面元素的布置。

运行一下这个程序,大家就会发现问题了,它并没有完成加法计算的功能。这种方法的 局限性暴露了出来,由于在 Qt4 中,Qt Designer 只能完成基本的系统内置的信号与槽的连 接功能,而不再提供代码编辑功能,那么我们需要自定义信号和槽时,往往就无能为力了。 这就需要使用单继承法和多继承法来实现与用户的交互,因为它们可以通过代码操纵窗体元 素。

10.3.2 单继承法

本小节例子的代码是来自源码包中的 calculatorform 实例。

1.使用要点

这种做法的要领是,子类化窗体的基类(如 QWidget 或者 QDialog 等),并在该自定义 类中定义一个私有(private)成员变量,该变量是由 uic 工具生成的基于 Qt Designer 生成 的.ui 文件的界面类的实例,其形式为:

private: Ui_YourFormName ui

或者:

private:Ui::YourFormName ui

接下来,在该自定义类的构造函数中构造和初始化界面元素(使用 setupUi()函数),这之后,便可以通过实例 ui 来使用界面中的各个部件。

图 10-3 单继承方式的类关系图

2.实例讲解

我们仍然以 Calculator Form 为例,讲解这种方式的用法。首先介绍完全手写代码的方 法,了解了这个后,你可以尝试在 IDE 里面如 Qt Creator 来完成,这就比较容易了。

在引用 Ui::CalculatorForm 之前,我们需要加入由 uic 生成的 C++头文件的声明:

#include "ui_calculatorform.h"

在工程文件中也要加入 calculatorform.h 这个头文件:

HEADERS = calculatorform.h

现在看一下我们这个子类的声明:

class CalculatorForm : public QWidget
{
    Q_OBJECT
public:
    CalculatorForm(QWidget *parent = 0);
private slots:
    void on_inputSpinBox1_valueChanged(int value);
    void on_inputSpinBox2_valueChanged(int value);
private:
    Ui::CalculatorForm ui;
};

第 1 行声明了 CalculatorForm 这个类公有继承自 QWidget,接下来第 9、10 两行声明了一个私有的 ui 变量,以后就由它来完成对应用程序界面的创建 、布置以及信号与槽的关联。再看看构造函数:

CalculatorForm::CalculatorForm(QWidget *parent)
: QWidget(parent)
{
    ui.setupUi(this);
}

前面讲过,setupUi()是重要的函数,我们一般在类的构造函数中调用它,由它来完成窗体的界面元素的布局和设置。

接下来,就可以使用 ui 变量来访问界面元素了,请看下面的示例代码:

void CalculatorForm::on_inputSpinBox1_valueChanged(int value)
{
    ui.outputWidget->setText(QString::number(value + ui.inputSpinBox2->value()));
}

这种方式的优点是,应用程序能够控制用户界面的外观和显示方式 ,并能够与用户进行交互;此外,还可以使用同样的一个 .ui 文件来生成多个不同的自定义界面类,也就是可以 重用 Qt Designer 绘制的用户界面。尤其是当我们需要由一个已经存在的用户界面上派生出多个类似的界面类时,这种方法非常有效。

10.3.3 多继承法

本小节例子的代码是来自源码包中的 multipleinheritance 实例。

1.使用要点

这种做法的要领是,从使用 Qt Designer 设计的界面实体类(Ui::YourFormName)和 QWidget 或者 QDialog 及其子类中多继承派生一个自定义界面类,其形式为:

class myWidget : public QWidget,public Ui_YourFormName
{
    Q_OBJECT
public:
    myWidget( QWidget *widget = 0 );
}

或者这样写:

class myWidget : public QWidget,public Ui::YourFormName
{
    Q_OBJECT
public:
    myWidget( QWidget *widget = 0 );
}

这样,我们就可以在 myWidget 这个子类中直接使用界面类的成员,也可以显式的设置信号与槽的连接。比如你在界面中布置了一个 pushButton 类,它的 ObjectName 属性为 pushButton。那么,在程序中就可以这样使用:

pushButton->setText(tr(“Add”));

大家看看,是不是非常的简便。下面这张 UML 图显示了多继承法中的类关系图。

图 10-4 多继承方式-类关系图

2.实例讲解

我们这个实例名叫 Multiple Inheritance ,完成简单的加法计算功能,.ui 界面文 件与前述相同。

先来看看 calculatorform.h 头文件中的内容:

#include "ui_calculatorform.h"
class CalculatorForm : public QWidget, private Ui::CalculatorForm
{
    Q_OBJECT
public:
    CalculatorForm(QWidget *parent = 0);
private slots:
    void on_inputSpinBox1_valueChanged(int value);
    void on_inputSpinBox2_valueChanged(int value);
};

首先需要加入由 uic 根据 calculatorform.ui 生成的 ui_calculatorform.h 头文件,因为我们自定义的 CalculatorForm 类的基类之一的 Ui::CalculatorForm 是在该文件中定义的。 在这里我们选择私有继承自 Ui::CalculatorForm 的原因是,以后不准备再以 CalculatorForm 类为基类来创建派生类,因而需要确保界面元素保持私有。当然,你也可以选择公有或者保 护继承,那么就可以再以 CalculatorForm 为基类,继续派生。

接下来与使用单继承方式类似的,我们在构造函数中调用 setupUi()方法完成界面元素 的初始化和布局。

CalculatorForm::CalculatorForm(QWidget *parent)
: QWidget(parent)
{
    setupUi(this);
}

接下来,我们可以像前几章中介绍的完全用手写代码的方式创建窗体那样直接访问界面元素,而无需 ui 前缀了,请看下面的示例代码:

void CalculatorForm::on_inputSpinBox1_valueChanged(int value)
{
    outputWidget->setText(QString::number(value + inputSpinBox2->value()));
}
void CalculatorForm::on_inputSpinBox2_valueChanged(int value)
{
    outputWidget->setText(QString::number(value + inputSpinBox1->value()));
}

主函数可以这样编写:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    CalculatorForm calculator;
    calculator.show();
    return app.exec();
}

在 main()函数中,我们依次实例化 QApplication 和 CalculatorForm 的对象,然后调用 show()函数来显示窗体。

从以上对 3 种继承方式的分析可见,多继承方式可直接对 ui 页面上的控件或函数进行 操作调用,代码编写更加清晰、简洁;而使用单继承方式,在操作 ui 页面上的控件时需要 加上 ui 对象前缀,编写代码较为繁琐。但对于程序中所需用到的 ui 页面较多时,使用单继 承方式则要简便很多。因此,在实现较为复杂的多窗口程序时,可以优先考虑使用单继承法。 而直接使用法一般采用的比较少。