当前位置:嗨网首页>书籍在线阅读

07-窗体相关的文件

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

2.1.5 窗体相关的文件

为了搞清楚窗体类的定义,以及界面功能的实现原理,这里将项目进行编译。编译后在项目目录下会自动生成一个文件ui_widget.h,这样对于一个窗体,就有4个文件了,各文件的功能说明见表2-2。

表2-2 窗体相关的4个文件

| 文件 | 功能 | | :----- | :----- | :----- | :----- | | widget.h | 定义窗体类的头文件,定义了类Widget | | widget.cpp | Widget类的功能实现源程序文件 | | widget.ui | 窗体界面文件,由UI设计器自动生成,存储了窗体上各个组件的属性设置和布局 | | ui_widget.h | 编译后,根据窗体上的组件及其属性、信号与槽的关联等自动生成的一个类的定义文件,类的名称是Ui_Widget |

下面分别分析各个文件的内容及其功能,以及它们是如何联系在一起工作,实现界面的创建与显示的。

1.widget.h文件

widget.h文件是窗体类的头文件。在创建项目时,选择窗体基类是QWidget,在widget.h中定义了一个继承自QWidget的类Widget,下面是widget.h文件的内容。

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {   //一个命名空间Ui,包含一个类Widget
class Widget;
}
class Widget : public QWidget
{
   Q_OBJECT
public:
   explicit Widget(QWidget *parent = 0);
   ~Widget();
private:
   Ui::Widget *ui;   //使用Ui::Widget定义的一个指针
};
#endif

widget.h文件有几个重要的部分。

(1)namespace声明

代码中有如下的一个namespace声明:

namespace Ui {
class Widget;
}

这是声明了一个名称为Ui的命名空间(namespace),包含一个类Widget。但是这个类Widget并不是本文件里定义的类Widget,而是ui_widget.h文件里定义的类,用于描述界面组件的。这个声明相当于一个外部类型声明(具体要看完ui_widget.h文件内的解释之后才能搞明白)。

(2)Widget类的定义。widget.h文件的主体部分是一个继承于QWidget的类Widget的定义,也就是本实例的窗体类。

在Widget类中使用了宏Q_OBJECT,这是使用Qt的信号与槽(signal和slot)机制的类都必须加入的一个宏(信号与槽在后面详细介绍)。

在public部分定义了Widget类的构造函数和析构函数。

在private部分又定义了一个指针。

Ui::Widget *ui;

这个指针是用前面声明的namespace Ui里的Widget类定义的,所以指针ui是指向可视化设计的界面,后面会看到要访问界面上的组件,都需要通过这个指针ui。

2.widget.cpp文件

widget.cpp文件是类Widget的实现代码,下面是widget.cpp文件的内容。

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :   QWidget(parent),   ui(new Ui::Widget)
{
    ui->setupUi(this);
}
Widget::~Widget()
{
    delete ui;
}

注意到,在这个文件的包含文件部分自动加入了如下一行内容:

#include "ui_widget.h"

这个就是Qt编译生成的与UI文件widget.ui对应的类定义文件。

目前只有构造函数和析构函数。其中构造函数头部是:

Widget::Widget(QWidget *parent) :   QWidget(parent),   ui(new Ui::Widget)

其意义是:执行父类QWidget的构造函数,创建一个Ui::Widget类的对象ui。这个ui就是Widget的private部分定义的指针变量ui。

构造函数里只有一行语句:

ui->setupUi(this)

它是执行了Ui::Widget类的setupUi()函数,这个函数实现窗口的生成与各种属性的设置、信号与槽的关联(后面会具体介绍)。

析构函数只是简单地删除用new创建的指针ui。

所以,在ui_widget.h文件里有一个namespace名称为Ui,里面有一个类Widget是用于描述可视化设计的窗体,且与widget.h里定义的类同名。在Widget类里访问Ui::Widget类的成员变量或函数需要通过Widget类里的ui指针,如同构造函数里执行ui->setupUi( this)函数那样。

3.widget.ui文件

widget.ui是窗体界面定义文件,是一个XML文件,定义了窗口上的所有组件的属性设置、布局,及其信号与槽函数的关联等。用UI设计器可视化设计的界面都由Qt自动解析,并以XML文件的形式保存下来。在设计界面时,只需在UI设计器里进行可视化设计即可,而不用管widget.ui文件是怎么生成的。

下面是widget.ui文件的内容。

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>336</width>
    <height>216</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>My First Demo</string>
  </property>
  <widget class="QLabel" name="LabDemo">
   <property name="geometry">
    <rect>
     <x>60</x>
     <y>50</y>
     <width>211</width>
     <height>51</height>
    </rect>
   </property>
   <property name="font">
    <font>
     <pointsize>20</pointsize>
     <weight>75</weight>
     <bold>true</bold>
    </font>
   </property>
   <property name="text">
    <string>Hello, World</string>
   </property>
  </widget>
  <widget class="QPushButton" name="btnClose">
   <property name="geometry">
    <rect>
     <x>200</x>
     <y>140</y>
     <width>81</width>
     <height>31</height>
    </rect>
   </property>
   <property name="text">
    <string>Close</string>
   </property>
  </widget>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections>
  <connection>
   <sender>btnClose</sender>
   <signal>clicked()</signal>
   <receiver>Widget</receiver>
   <slot>close()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>240</x>
     <y>195</y>
    </hint>
    <hint type="destinationlabel">
     <x>199</x>
     <y>149</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>

4.ui_widget.h文件

ui_widget.h是在对widget.ui文件编译后生成的一个文件,ui_widget.h会出现在编译后的目录下,或与widget.ui同目录(与项目的shadow build编译设置有关)。

文件ui_widget.h并不会出现在Qt Creator的项目文件目录树里,当然,可以手工将ui_widget.h添加到项目中。方法是在项目文件目录树上,右击项目名称节点,在调出的快捷菜单中选择“Add Existing Files…”,找到并添加ui_widget.h文件即可。

注意 ui_widget.h是对widget.ui文件编译后自动生成的,widget.ui又是通过UI设计器可视化设计生成的。所以,对ui_widget.h手工进行修改没有什么意义,所有涉及界面的修改都应该直接在UI设计器里进行。所以,ui_widget.h也没有必要添加到项目里。

下面是ui_widget.h文件的内容。

/**********************************************************************
** Form generated from reading UI file 'widget.ui'
** Created by: Qt User Interface Compiler version 5.9.0
** WARNING! All changes made in this file will be lost when recompiling UI file!
*********************************************************************/
#ifndef UI_WIDGET_H
#define UI_WIDGET_H
#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>
Qt_BEGIN_NAMESPACE
class Ui_Widget
{
public:
    QLabel *LabDemo;
    QPushButton *btnClose;
    void setupUi(QWidget *Widget)
    {
        if (Widget->objectName().isEmpty())
            Widget->setObjectName(QStringLiteral("Widget"));
        Widget->resize(280, 168);
        LabDemo = new QLabel(Widget);
        LabDemo->setObjectName(QStringLiteral("LabDemo"));
        LabDemo->setGeometry(QRect(50, 20, 201, 51));
        QFont font;
        font.setPointSize(20);
        font.setBold(true);
        font.setWeight(75);
        LabDemo->setFont(font);
        btnClose = new QPushButton(Widget);
        btnClose->setObjectName(QStringLiteral("btnClose"));
        btnClose->setGeometry(QRect(150, 120, 75, 23));
        retranslateUi(Widget);
        QObject::connect(btnClose, SIGNAL(clicked()), Widget, SLOT(close()));
        QMetaObject::connectSlotsByName(Widget);
    } // setupUi
    void retranslateUi(QWidget *Widget)
    {
        Widget->setWindowTitle(QApplication::translate("Widget", "My First Demo", Q_NULLPTR));
        LabDemo->setText(QApplication::translate("Widget", "Hello, World", Q_NULLPTR));
        btnClose->setText(QApplication::translate("Widget", "Close", Q_NULLPTR));
    } // retranslateUi
};
namespace Ui {
    class Widget: public Ui_Widget {};
} // namespace Ui
Qt_END_NAMESPACE
#endif // UI_WIDGET_H

查看ui_widget.h文件的内容,发现它主要做了以下的一些工作。

(1)定义了一个类Ui_Widget,用于封装可视化设计的界面。

(2)自动生成了界面各个组件的类成员变量定义。在public部分为界面上每个组件定义了一个指针变量,变量的名称就是设置的objectName。比如,在窗体上放置了一个QLabel和一个QPushButton并命名后,自动生成的定义是:

QLabel *LabDemo;
QPushButton *btnClose;

(3)定义了setupUi()函数,这个函数用于创建各个界面组件,并设置其位置、大小、文字内容、字体等属性,设置信号与槽的关联。

setupUi()函数体的第一部分是根据可视化设计的界面内容,用C++代码创建界面上各组件,并设置其属性。

接下来,setupUi()调用了函数retranslateUi(Widget),用来设置界面各组件的文字内容属性,如标签的文字、按键的文字、窗体的标题等。将界面上的文字设置的内容独立出来作为一个函数retranslateUi(),在设计多语言界面时会用到这个函数。

setupUi()函数的第三部分是设置信号与槽的关联,本文件中有以下两行:

QObject::connect(btnClose, SIGNAL(clicked()), Widget, SLOT(close()));
QMetaObject::connectSlotsByName(Widget);

第1行是调用connect()函数,将在UI设计器里设置的信号与槽的关联转换为语句。这里是将btnClose按键的clicked()信号与窗体Widget的close()槽函数关联起来,就是在图2-4中设置的信号与槽的关联的程序语句实现。这样,当单击btnClose按钮时,就会执行Widget的close()槽函数,而close()槽函数的功能是关闭窗口。

第2行是设置槽函数的关联方式,用于将UI设计器自动生成的组件信号的槽函数与组件信号相关联。

所以,在Widget的构造函数里调用 ui->setupUI(this),就实现了窗体上组件的创建、属性设置、信号与槽的关联。

(4)定义namespace Ui,并定义一个从Ui_Widget继承的类Widget。

namespace Ui {
    class Widget: public Ui_Widget {};
}

提示 ui_widget.h文件里实现界面功能的类是Ui_Widget。再定义一个类Widget从Ui_Widget继承而来,并定义在namespace Ui里,这样Ui:: Widget与widget.h里的类Widget同名,但是用namespace区分开来。所以,界面的Ui:: Widget类与文件widget.h里定义的Widget类实际上是两个类,但是Qt的处理让用户感觉不到Ui:: Widget类的存在,只需要知道在Widget类里用ui指针可以访问可视化设计的界面组件就可以了。