baizy77的个人主页

http://www.qtcn.org/bbs/u/88608  [收藏] [复制]

baizy77

小白

  • 2

    关注

  • 8

    粉丝

  • 1

    访客

  • 等级:新手上路
  • 总积分:0
  • 男,1977-11-27

最后登录:2021-01-06

更多资料

日志

2.2 案例2 整理一下目录吧

2018-10-23 15:45
本案例对应的源代码目录:src/chapter02/ks02_02。程序运行效果见图2-11。

图2-11 案例2运行效果
使用Qt进行开发的目的之一是开发界面类应用。本节将介绍用Qt开发界面类应用的基本步骤,并介绍如何通过修改pro文件的配置使源代码目录保持整洁。
开发界面类项目的过程大概分为四步。
(1)使用Designer绘制对话框资源文件(ui)并保存。
(2)编写界面ui对应的类CDialog。
(3)将相关文件添加到pro。
(4)使用CDialog定义对象。
下面分步骤讲解。
1.使用Designer绘制对话框资源文件(ui)并保存
启动Designer,出现如图2-12所示界面,选择template\forms中的Dialog with  Buttons Bottom,然后单击【创建】按钮即可完成新建窗体工作。

图2-12 Designer新建窗体
然后,为新建的窗体设置类名:请在窗体空白处单击,然后在属性框中设置对话框的objectName(见图2-13)。设置对话框类名为CDialog,并将UI文件保存为dialog.ui。请记下这两个名称,因为后面会用到。可以根据实际需要对UI文件名、对话框类名进行命名。

图2-13 CDialog属性
然后,从Designer的工具箱的【Display Widgets】选项卡中选择Label控件(类型为QLabel,见图2-14),并拖入窗体。

图2-14 文本控件
双击该Label控件,将文字修改为“This is my dialog!”,如图2-15所示。

图2-15 编辑文本控件内容
然后,单击窗体空白处选中整个窗体,再单击工具栏上的【栅格布局】按钮为窗体设置布局(见图2-16)。

图2-16 对窗体进行栅格布局
2.编写界面ui对应的类CDialog
为dialog.ui编写对应的类CDialog,目的是为对话框增加业务功能。此处的类名CDialog来自图2-13中窗体的objectName。CDialog的头文件dialog.h的代码请见代码清单2-3。
代码清单2-3

// dialog.h
#pragma once                                                                          
#include <QDialog>
namespace Ui {
    class CDialog;                                                                    
}
// 基类的名称来自UI文件中对话框的类名:对象查看器中的类名
class CDialog : public QDialog {                                                      
public:
    CDialog(QWidget* pParent);
    ~CDialog();
private:                
    Ui::CDialog* m_pUi;                                                              
};

请注意代码清单2-3中标号①处:#pragma once。该代码的作用是防止编译该头文件时发生重入的情况(多次编译同一个头文件),以免出现编译错误。
在标号②处,对命名空间Ui中的类CDialog做了前置声明,这是因为标号④处要用Ui::CDialog定义指针m_pUi。使用指针和前置声明有2个好处:一是无须在头文件中包含Ui::CDialog的头文件ui_dialog.h,因为在dialog.cpp中将包含该头文件,这在中大型项目中非常重要,因为这样做可以节省很多编译时间;二是当CDialog类(不是Ui::CDialog)需要作为DLL中的类被引出时,不会导致包含dialog.h的其他项目出现编译错误(比如,找不到ui_dialog.h)。
在标号③处,CDialog类的基类是QDialog,这是因为在Designer中绘制UI文件时选择的就是QDialog。如果不清楚CDialog的基类应该选哪一个,可以在Designer的【对象查看器】中查看。如图2-17所示,第一行对象Dialog的类名QDialog就是标号③处的基类名称。

图2-17 对象查看器
在标号④处,为了使用dialog.ui中的布局,需要为类CDialog添加私有成员Ui::CDialog* m_pUi,该对象用来初始化界面。Ui::CDialog来自dialog.ui,是通过Qt的uic命令解析dialog.ui后得到的。在项目生成的临时文件ui_dialog.h中可以找到它的定义,见代码清单2-4。在代码清单2-4中,如标号①处所示,注释中明确指出:对该文件的手工修改都将在重新编译UI文件时被覆盖。在标号②、标号③处,是界面中buttonBox、label这两个控件对象的定义。在标号④处,是初始化界面接口setupUi()的实现,在该接口中完成了对界面的初始化,包括对buttonBox、label这两个控件的构建。在标号⑤处,在命名空间Ui中定义类CDialog,该类(Ui::CDialog)将被用来定义对象m_pUi,见代码清单2-3中标号④处。
代码清单2-4

// ui_dialog.h
/********************************************************************************
** Form generated from reading UI file 'dialog.ui'
**
** Created by: Qt User Interface Compiler version 5.11.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!  
********************************************************************************/
#ifndef UI_DIALOG_H
#define UI_DIALOG_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDialog>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QLabel>
QT_BEGIN_NAMESPACE
class Ui_CDialog
{
public:
    QDialogButtonBox *buttonBox;                                                  
    QLabel *label;                                                                
    void setupUi(QDialog *CDialog)                                                
    {
        if (CDialog->objectName().isEmpty())
            CDialog->setObjectName(QStringLiteral("CDialog"));
        CDialog->resize(329, 184);
        buttonBox = new QDialogButtonBox(CDialog);
        buttonBox->setObjectName(QStringLiteral("buttonBox"));
        buttonBox->setGeometry(QRect(80, 120, 161, 32));
        buttonBox->setOrientation(Qt::Horizontal);
        buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
        label = new QLabel(CDialog);
        label->setObjectName(QStringLiteral("label"));
        label->setGeometry(QRect(100, 40, 171, 16));
        retranslateUi(CDialog);
        QObject::connect(buttonBox, SIGNAL(accepted()), CDialog, SLOT(accept()));
        QObject::connect(buttonBox, SIGNAL(rejected()), CDialog, SLOT(reject()));
        QMetaObject::connectSlotsByName(CDialog);
    } // setupUi
    void retranslateUi(QDialog *CDialog)
    {
        CDialog->setWindowTitle(QApplication::translate("CDialog", "Dialog", nullptr));
        label->setText(QApplication::translate("CDialog", "This is my dialog!", nullptr));
    } // retranslateUi
};
namespace Ui {
    class CDialog: public Ui_CDialog {};                                          
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_DIALOG_H

下面给出CDialog的实现文件dialog.cpp,请见代码清单2-5。
代码清单2-5

// dialog.cpp
#include "dialog.h"
#include "ui_dialog.h" // 头文件名称: dialog.ui -> ui_dialog.h                         ①
CDialog::CDialog(QWidget* pParent) : QDialog(pParent),m_pUi(new Ui::CDialog) {        
    m_pUi->setupUi(this);                                                              
}
CDialog::~CDialog() {
    if (NULL != m_pUi){                                                                
        delete m_pUi;
        m_pUi = NULL;
    }
}

代码清单2-5中,标号①处的代码包含ui_dialog.h头文件,目的是让编译器可以看到Ui::CDialog的定义。这用到了步骤1中保存界面文件时的文件名dialog.ui。Qt的uic命令将dialog.ui文件转换为UI头文件:ui_dialog.h,即前缀ui_加上文件名dailog.ui中的dialog共同拼接成ui_dialog.h。
标号②处,在类CDialog的构造函数的初始化列表中,除了用QDialog(pParent)调用基类的构造函数进行初始化之外,还构建了m_pUi对象。
标号③处,在构造函数中一定要调用m_pUi->setupUi(this),否则界面无法正常显示。因为这处调用就是对界面进行构建。如果对该接口感兴趣,可以单步调试一下这个调用,看看setupUi()内部到底执行了什么操作。
在标号④处,当CDialog析构时需要对m_pUi指向的内存进行释放,并将m_pUi赋值为NULL。
除了将CDialog作为DLL的引出类等特殊需要外,在后续章节中不再限制使用指针还是对象的方式使用Ui::CDilaog。如果使用对象的方式,则需要把dialog.h做两处修改,见代码清单2-6中的标号①处、标号②处,而且dialog.cpp中不再编写#include"ui_dialog.h"的代码。
代码清单2-6

// dialog.h
#pragma once
#include <QDialog>
#include "ui_dialog.h"                                                                
class CDialog : public QDialog {
    ...
private:
    Ui::CDialog m_ui;                                                                  
};

3.将相关文件添加到pro
目前已完成的工作包括:设计界面文件dialog.ui、编写CDialog类的定义文件dialog.h和实现文件dialog.cpp。现在把这些文件添加到项目的pro文件,见代码清单2-7。
代码清单2-7

// ks02_02.pro
QT += widgets
FORMS = dialog.ui
HEADERS += ks02_02.pro \
           dialog.h                      
SOURCES +=  main.cpp \
            dialog.cpp

代码清单2-7中,FORMS配置项用来描述项目中用到的UI文件列表;HEADERS和SOURCES两个配置项在前面章节介绍过,只不过在本案例中使用了多个文件。因为用到了界面控件,请确保编写QT+=widgets,否则将导致程序构建失败。
4.使用CDialog定义对象
如果用类CDialog定义对象,首先需要包含CDialog的头文件(dialog.h),然后才能定义CDialog的对象并调用其接口,见代码清单2-8。
代码清单2-8

// main.cpp
#include <QApplication>
#include <iostream>
#include "qglobal.h"
#include "dialog.h"
using std::cout;
using std::endl;
int main(int argc, char * argv[]){
    Q_UNUSED(argc);
    Q_UNUSED(argv);
    QApplication app(argc, argv);
    CDialog dlg(NULL);
    dlg.exec();
    return 0;
}

到现在为止,为项目添加界面的工作就结束了,可以构建项目了。
但是当项目构建完成后,看一下源代码目录就会发现临时文件和临时目录太多了(如图图2-18所示方框内的文件或目录),简直杂乱不堪。一般情况下,项目组会使用代码管理工具(比如SVN)管理代码,而且可以预先设置代码入库的过滤条件,因此在提交代码时临时文件或目录不会被入库。但是,当需要备份一下源代码目录并打包时,如果有这么多临时文件(有的临时文件尺寸比较大),那可太不方便了。下面介绍如何通过修改pro文件的配置来整理源代码目录。

图2-18 案例2源代码目录
整理源代码目录的方法是引入环境变量,然后通过环境变量来设置pro中的各种路径。
首先引入一个TRAINDEVHOME的环境变量,它指向项目src的上级目录。代码的目录结构如下。
TRAINDEVHOME
------bin
------obj
------src
其中bin 、obj、src都是TRAINDEVHOME的子目录。bin目录用来存放项目生成的可执行程序和动态链接库,obj是构建项目时生成的临时文件的根目录
在pro文件中使用环境变量的语法:$$(环境变量)。比如:$$(TRAINDEVHOME)。对pro的修改见代码清单2-9。
代码清单2-9

# ks02_02.pro
...
OBJECTS_DIR = $$(TRAINDEVHOME)/obj/chapter02/ks02_02
DESTDIR      = $$(TRAINDEVHOME)/bin
MOC_DIR      = $$OBJECTS_DIR/moc
UI_DIR       = $$OBJECTS_DIR/ui

在代码清单2-9中,OBJECTS_DIR是Qt的关键字,用来表示项目生成的临时文件的存放目录。将OBJECTS_DIR设置到obj下对应本案例的子目录。
DESTDIR是Qt的关键字,用来表示目标文件存放目录,即项目最终生成的EXE或DLL的存放目录。如果是EXE,将其设置到TRAINDEVHOME的bin子目录;如果是DLL,则设置到lib子目录。
MOC_DIR、UI_DIR是Qt的关键字,用来表示Qt的moc和uic命令生成的临时文件存放目录。将它们分别设置到OBJECTS_DIR下面的moc子目录和ui子目录。
除此之外,如果使用QtCreator进行开发,还需要设置默认构建路径,否则会导致影子构建时生成的临时文件被放到源代码目录中。选择QtCreator的【工具】|【选项】菜单项,出现【选项】对话框,选择【构建和运行】标签,再选择【概要】标签,最后修改Default builddirectory(见图2-19),将其值修改为(关于该值的拼写请见本书配套资源中【Qt Creator 4.7.2的Default builddirectory设置】):
%{CurrentProject:VcsTopLevelPath}/obj/%{CurrentBuild:Name}/%{JS:Util.asciify("build-%{CurrentProject:Name}-%{CurrentKit:FileSystemName}")}

图2-19 Qt Creator默认构建目录
修改pro文件并设置完Qt Creator后,再次构建项目后得到的源代码目录见图2-20。

图2-20 整理后的目录
如果使用VS2017命令行进行构建,在每次修改pro后请重新执行qmake以便更新相应的Makefile文件;如果用VS2017的IDE进行构建,则需要更新VS 2017的IDE中的项目文件(后缀为.vcxproj的文件),方法是执行qmake  –tp vc,然后用VS 2017 的IDE重新加载项目文件。
本案例介绍了开发界面类应用的步骤以及通过修改pro配置来整理源代码目录的方法,现在汇总一下知识点:
(1)如果项目中需要使用界面,那么在pro中请务必添加:

QT  +=widgets

(2)CDialog类的头文件中,请注意CDialog基类名称的来源以及私有的指针成员变量m_pUi以及对于Ui::CDialog的前置声明,见代码清单2-10。
代码清单2-10

namespace Ui {
    class CDialog;
}
class CDialog : public QDialog {
    ...
private:
    Ui::CDialog* m_pUi;
};

(3)请注意dialog.ui对应头文件ui_dialog.h的文件名的构成规则。
(4)CDialog类的构造函数中一定要调用m_pUi->setupUi(this)。
(5)要在pro中通过环境变量设置相关目录。环境变量在使用时应该用下列语法:
        $$(环境变量)
(6)可以在pro中定义变量,变量在使用时应该用下列语法:
        $$变量名称
(7)另外,还介绍了Qt关于路径设置的关键字。
  1. DESTDIR:存放最终生成的目标文件的路径。
  2. OBJECTS_DIR:本项目临时文件的存放目录。
  3. MOC_DIR:用来存放qt的moc命令生成的临时文件。
  4. UI_DIR:用来存放qt的uic命令生成的临时文件。当项目中包含UI文件时需要用到该目录。
  5. FORMS:用来描述项目中包含的UI文件列表。

《Qt 5/PyQt 5实战指南》目录
分类:Qr入门与提高|回复:0|浏览:605|全站可见|转载
 

Powered by phpwind v8.7 Certificate Copyright Time now is:04-25 22:08
©2005-2016 QTCN开发网 版权所有 Gzip disabled