Qt 学习之路 2(3):Hello, world! 笔记

学习 Qt 开发,先搭建 Qt 开发环境。到Qt官方网站找到最新版本的。

在线安装器Qt Online Installers,它为不同Qt库版本和最新QtCreator提供了二进制和源代码安装包Qt OnlineInstaller for Linux 64-bit (29 MB)    (info)

离线安装器Offline Installers,它提供了独立的保函Qt库和Qt创建者的二进制包。 Qt 5.5.0 forLinux 64-bit (532 MB)    (info) 。

源代码包和另外的发布,它提供了单独的源代码。

Qt Creator3.4.2包含在Qt5.5.0而精致包中。

Qt Creator:基于Qt 构建的一个轻量级 IDE,现在最新版是 2.5.2(现在是3.4.2了)使用 Qt Creator 进行开发。如果你习惯VS2010可以在页面最下方找到相应的Addin。你可以阅读 Qt Creator 的清晰代码来提高自己的水平。

我们Qt Creator开发,编译器使用 mingw 或者 gcc。豆子的环境是使用 openSUSE。

在 Qt Creator 中,我们可以在菜单栏的工具-选项-构建和运行的“Qt 版本”和“工具链”这两个选项卡中配置 QtCreator 所使用的 Qt 版本和编译器。这或许是最重要的步骤,包括添加新的 Qt 版本以及以后的切换编译器或者 Qt 升级等。

下面尝试开发第一个 Qt 项目:HelloWorld。在Qt Creator 中新建一个工程:

点击这个“新建文件或工程”,在左侧选择项目-Applications,中间选择 Qt Gui 应用,然后点击“选择…”:

在弹出的对话框中填写名称、创建路径等信息:

点击“下一步”,选择该工程的编译器。这里我们只选择 mingw 调试即可(在以后的项目中,根据自己的需要选择。)Shadow Build 的含义是“影子构建”,即将构建生成的文件不放在源代码文件夹下。这样可以最大地保持源代码文件夹的整洁。

点击“下一步”,可以选择生成的主窗口文件。不过在我们的简单示例中是不需要这么复杂的窗口的,因此我们尽可能简单地选择,将“创建界面”的选择去除:

终于到了最后一步。这里是在询问我们是否添加版本控制。对于我们的小项目当然是不需要的,所以选择“无”,然后点击“完成”即可:

可以看到,Qt Creator 帮助我们在 HelloWorld 项目文件夹下生成了四个文件:main.cpp,mainwindow.cpp,mainwindow.h 和 HelloWorld.pro。pro 文件就是 Qt 工程文件(projectfile),由 qmake 处理,生成 make 程序所需要的 makefile;main.cpp 里面就是一个main函数,作为应用程序的入口函数;其他两个文件就是先前我们曾经指定的文件名的文件。

我们将 main.cpp 修改如下:

#include <QApplication> #include <QLabel>int main(int argc, char *argv[]) {QApplication app(argc, argv);QLabel label("Hello, world");label.show();return app.exec(); }

1

2

3

4

5

6

7

8

9

10

11

12

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

QApplication app(argc, argv);

QLabel label("Hello, world");

label.show();

return app.exec();

}

点击 Qt Creater 左侧下面的绿色三角按钮即可运行(这里一共有三个按钮,从上到下分别是“运行”、“调试”和“构建”)。如果没有错误的话,就会看到运行结果:

这个程序有这么几行。我们解释一下。

前两行是 C++ 的 include 语句,这里我们引入的是QApplication以及QLabel这两个类。main()函数中第一句是创建一个QApplication类的实例。对于 Qt 程序来说,main()函数一般以创建 application 对象(GUI程序是QApplication,非 GUI 程序是QCoreApplication。QApplication实际上是QCoreApplication的子类。)开始,后面才是实际业务的代码。这个对象用于管理 Qt 程序的生命周期,开启事件循环,这一切都是必不可少的。在我们创建了QApplication对象之后,直接创建一个QLabel对象,构造函数赋值“Hello,world”,当然就是能够在QLabel上面显示这行文本。最后调用QLabel的show()函数将其显示出来。main()函数最后,调用app.exec(),开启事件循环。我们现在可以简单地将事件循环理解成一段无限循环。正因为如此,我们在栈上构建了QLabel对象,却能够一直显示在那里(试想,如果不是无限循环,main()函数立刻会退出,QLabel对象当然也就直接析构了)。

示例程序我们已经讲解完毕。下面再说一点。我们可以将上面的程序改写成下面的代码吗?

#include <QApplication> #include <QLabel>int main(int argc, char *argv[]) {QApplication app(argc, argv);QLabel *label = new QLabel("Hello, world");label->show();return app.exec(); }

1

2

3

4

5

6

7

8

9

10

11

12

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

QApplication app(argc, argv);

QLabel *label = new QLabel("Hello, world");

label->show();

return app.exec();

}

答案是,可以建议这样做!

首先,按照标准 C++ 来看这段程序。这里存在着内存泄露。当exec()退出时(也就是事件循环结束的时候。窗口关闭,事件循环就会结束),label 是没办法 delete 的。这就造成了内存泄露。当然,由于程序结束,操作系统会负责回收内存,所以这个问题不会很严重。即便你这样修改了代码再运行,也不会有任何错误。

早期版本的 Qt 可能会有问题(详见本文最后带有删除线的部分,不过豆子也没有测试,只是看到有文章这样介绍),不过在新版本的 Qt 基本不存在问题。在新版本的 Qt 中,app.exec()的实现机制确定,当最后一个可视组件关闭之后,主事件循环(也就是app.exec())才会退出,main()函数结束(此时会销毁app)。这意味着,所有可视元素已经都关闭了,也就不存在后文提到的,QPaintDevice没有QApplication实例这种情况。另外,如果你是显式关闭了QApplication实例,例如调用了qApp->quit()之类的函数,QApplication的最后一个动作将会是关闭所有窗口。所以,即便在这种情况下,也不会出现类这种问题。由于是在main()函数中,当main()函数结束时,操作系统会回收进程所占用的资源,相当于没有内存泄露。不过,这里有一个潜在的问题:操作系统只会粗暴地释放掉所占内存,并不会调用对象的析构函数(这与调用delete运算符是不同的),所以,很有可能有些资源占用不会被“正确”释放。事实上,在最新版的Sailfish OS 上面就有这样的代码:

#include <QApplication>int main(int argc, char *argv[]) {QScopedPointer<QApplication> app(new QApplication(argc, argv));QScopedPointer<QQuickView> view(new QQuickView);view->setSource("/path/to/main.qml");...return app->exec(); }

1

2

3

4

5

6

7

8

9

10

#include <QApplication>

int main(int argc, char *argv[])

{

QScopedPointer<QApplication> app(new QApplication(argc, argv));

QScopedPointer<QQuickView> view(new QQuickView);

view->setSource("/path/to/main.qml");

...

return app->exec();

}

这段代码不仅在堆上创建组件实例,更是把QApplication本身创建在了堆上。不过,注意,它使用了智能指针,因此我们不需要考虑操作系统直接释放内存导致的资源占用的问题。

当然,允许使用并不一定意味着我们建议这样使用。毕竟,这是种不好的用法(就像我们不推荐利用异常控制业务逻辑一样),因为存在内存泄露。而且对程序维护者也是不好的。所以,我们还是推荐在栈上创建组件。因为要靠人工管理new和delete的出错概率要远大于在栈上的自动控制。除此之外,在堆上和在栈上创建已经没有任何区别。

如果你必须在堆上创建对象,不妨增加一句:

label->setAttribute(Qt::WA_DeleteOnClose);

1

label->setAttribute(Qt::WA_DeleteOnClose);

这点提示足够告诉程序维护者,你已经考虑到内存问题了。

严重的是,label 是建立在堆上的,app是建立在栈上的。这意味着,label 会在 app 之后析构。也就是说,label 的生命周期长于 app 的生命周期。这可是 Qt 编程的大忌。因为在 Qt 中,所有的QPaintDevice必须要在有QApplication实例的情况下创建和使用。大家好奇的话,可以提一句,QLabel继承自QWidgetQWidget则是QPaintDevice的子类。之所以上面的代码不会有问题,是因为 app 退出时,label 已经关闭,这样的话,label 的所有QPaintDevice一般都不会被访问到了。但是,如果我们的程序,在app 退出时,组件却没有关闭,这就会造成程序崩溃。(如果你想知道,怎样做才能让 app 退出时,组件却不退出,那么豆子可以告诉你,当你的程序在打开了一个网页的下拉框时关闭窗口,你的程序就会崩溃了!)

Qt 学习之路 2(3):Hello, world! 笔记相关推荐

  1. 转载: Qt 学习之路 2归档

    Qt 学习之路 2归档 http://www.devbean.net/2012/08/qt-study-road-2-catelog/

  2. 对QT学习之路12-14的源代码补充与修正

    QT学习之路12-14的源代码有些不完整,为了更好的让大家学习,本人做了一点修正与补充,谢谢.源代码如下: 头文件: #ifndef MAINWINDOW_H #define MAINWINDOW_H ...

  3. java qt gui_工控编程,Qt 学习之路

    原标题:工控编程,Qt 学习之路 Qt 是一个著名的 C++ 库--或许并不能说这只是一个 GUI 库,因为 Qt 十分庞大,并不仅仅是 GUI.使用 Qt,在一定程序上你获得的是一个"一站 ...

  4. Qt学习之路_12(简易数据管理系统)

    原文地址为: Qt学习之路_12(简易数据管理系统) 前言 最近从大陆来到台湾,之间杂事很多,挤不出时间来更新博客- 这次主要是通过做一个简易的数据库管理系统,来学习在Qt中对数据库,xml,界面的各 ...

  5. QT学习之路2 学习笔记

    QT学习之路2 学习笔记 1.Qt 是一个著名的 C++ 应用程序框架.你并不能说它只是一个 GUI 库,因为 Qt 十分庞大,并不仅仅是 GUI 组件.使用 Qt,在一定程度上你获得的是一个&quo ...

  6. 《Qt 学习之路 2》

    Home / Qt 学习之路 2 / <Qt 学习之路 2>目录 <Qt 学习之路 2>目录 序 Qt 前言 Hello, world! 信号槽 自定义信号槽 Qt 模块简介 ...

  7. QT学习之路(一)ubuntu 18.04的Qt Creator在线安装

    文章目录 前言 一.准备工作 二.安装步骤 参考链接 前言 Qt是嵌入式开发的必备工具之一,在Linux下安装尤其重要. Qt是C++的一个库,或者说是开发框架,里面集成了一些库函数,提高开发效率. ...

  8. Qt学习之路(24): QPainter(改写paintEvent)

    Qt学习之路(24): QPainter(改写paintEvent) 多些大家对我的支持啊!有朋友也提出,前面的几节有关event的教程缺少例子.因为event比较难做例子,也就没有去写,只是把大概写 ...

  9. [记录]学习QT学习之路2第四天

    今天学习内容对应<<QT学习之路2>>第18到23部分,主要是有关于QT事件这一部分的内容(=_=真是看到我快吐了,感觉这本书不适合我这种初学者). 先放出思维导图 难点总结 ...

  10. [记录]QT学习之路2学习第三天

    今天的内容对应的是<<QT学习之路2>>第13到17部分. 主要内容由思维导图总结 一,什么是QT对话框 二,什么是QT标准对话框 三,怎么样实现QT对话框数据传递 四,深入了 ...

最新文章

  1. 关于hive开窗函数的问题
  2. Motan的SPI机制实现分析
  3. boost::log::sinks::text_ipc_message_queue_backend用法的测试程序
  4. DCASE 2013任务1(声学场景分类)参赛作品相关信息
  5. OpenGL:读取图片显示
  6. web浏览器进化简史
  7. linux下CPP的认识
  8. 设计模式的七大设计原则:其五:开闭原则
  9. GitHub的SSH免密连接
  10. 一个比较完整的pytorch项目
  11. MP3 Lame 转换 参数 设置(转)
  12. 计算机教师辞职,(多篇)教师辞职报告汇总八篇
  13. H3C下一代防火墙介绍及开局指导培训答疑汇总
  14. 【目标检测】VOC2007数据集介绍
  15. php util,PHP中文工具类ChineseUtil怎样转换汉字与拼音
  16. deflate php,我可以告诉mod_deflate和PHP只跳过一个目录上的压缩吗?
  17. 自动爬取微博热门评论和点赞数并存为EXCEL文件(python2)
  18. C#保留2位小数几种场景总结 游标遍历所有数据库循环执行修改数据库的sql命令 原生js轮盘抽奖实例分析(幸运大转盘抽奖) javascript中的typeof和类型判断...
  19. 交叉编译器 arm-linux-gnueabi,arm-linux-gnueabihf,arm-none-linux等的区别
  20. ZYNQ之高速AD/DA验证实验

热门文章

  1. android simpliadapter的两种用法
  2. 从Spring中的@Transactional注解说起
  3. 翻译:SET PASSWORD语句(已提交到MariaDB官方手册)
  4. linux中^]是如何输出的
  5. hadoop安装报错
  6. JVM内存管理 (转)
  7. 查看使用yum安装的软件路径
  8. windows 7 等 公文包 功能 作用 使用说明
  9. 基于注解的 Spring MVC 简单入门
  10. Unix的***追踪