helloqt
OVERVIEW
- helloqt
- 一、helloqt
- 1.使用向导创建
- 2.手动创建
- 3.pro文件
- 4.Qt应用程序框架
- 二、按钮创建
- main.cpp
- mywidget.cpp
- 三、对象模型
- 1.对象树引入
- 2.存在的问题
一、helloqt
创建一个qt项目,可以使用creator的向导创建,也可自己手动创建:
1.使用向导创建
-
step1:文件->新建文件或项目,选择Qt Widgets Application(创建一个qt桌面应用,包含一个基于qt设计师的主窗体)
-
step2:填写项目名称,并设置项目存放路径后,选择编译套件(例如:Desktop Qt 5.5.0 MinGW 64bit)
-
step3:向导会自动添加一个继承自CMainWindow的类,可以在此修改类的名字和基类(默认基类有QMainWindow、QWidget、QDialog)
-
step4:向导会默认添加main.cpp、mywidgets.cpp、mywidgets.h和一个.pro项目文件,点击完成即可创建出一个qt桌面程序。
2.手动创建
-
step1:文件->新建文件或项目,添加一个空项目Empty qmake Project,
-
step2:填写项目名称,并设置项目存放路径后,选择编译套件(例如:Desktop Qt 5.5.0 MinGW 64bit)生成一个空项目。
-
step3:在空项目中添加文件,选择C++ Class添加即可。
3.pro文件
.pro
就是工程文件,其是qmake自动生成的用于生产makefile的配置文件,
#包含的模块
QT += core gui
#大于Qt4版本才包含widgets模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
#使用C++11的特性
CONFIG += c++11
#应用程序名 生成的.exe程序名
TARGET = QtFirst
#应用程序模板类型
TEMPLATE = app
#工程中包含的ui设计文件
FORMS += forms/painter.ui
#工程中包含的资源文件
RESOURCES += qrc/painter.qrc
#工程中包含的源文件
SOURCES += \
main.cpp \
mypushbutton.cpp \
mywidget.cpp
#工程中包含的头文件
HEADERS += \
mypushbutton.h \
mywidget.h
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
关于模板变量变量的配置选项:
- app:建立一个应用程序的makefile,这是默认值,如果模板没有被指定,该选项将被使用。
- lib:建立一个库的makefile
- vcapp:建立一个应用程序的VisualStudio项目文件
- vclib:建立一个库的VisualStudio项目文件
- subdirs:特殊的模板,可创建一个能够进入特定目录、并且为一个项目文件生成makefile、并且为它调用make的makefile
4.Qt应用程序框架
#include "mywidget.h"
#include <QApplication>
int main(int argc, char *argv[]) {
//1.创建应用程序对象a (在Qt中应用程序对象只能有一个)
QApplication a(argc, argv);
//2.创建窗口对象 w (窗口对象mywidget父类为QWidget)
myWidget w;
//3.调用窗口对象的show方法进行窗口显示
w.show();
//4.让应用程序对象进入消息循环(让代码阻塞在该行)
return a.exec();
}
- QApplication应用程序类:
- 管理图形用户界面应用程序的控制流和主要设置,
- 是Qt的整个后台管理的核心包含主事件循环,在其中解决来自窗口系统和其他资源的所有事件处理和调度,也处理应用程序初始化和结束,并提供对话管理。
- 对任何一个使用Qt图形用户界面的应用程序,都正好存在一个QApplication对象(不论此时该应用程序有多少窗口)。
a.exec()
:程序进入消息循环,等待对用户输入进行响应- main函数将控制权转交给Qt,由Qt完成事件处理工作,当应用程序退出的时候exec的值就会返回。
- 在exec函数中Qt接受并处理用户和系统的事件,并且将它们传递给适当的窗口部件。
二、按钮创建
按钮其实就是一个QPushButton类下的对象,
并利用setParent为其指定一个依赖的父窗口,配合setText显示文字、move移动按钮位置、setWindowTitle修改窗口标题、resize指定窗口大小、setFixedSize设置固定窗口大小,
main.cpp
#include "mywidget.h"
#include <QApplication>
int main(int argc, char *argv[]) {
//1.创建应用程序对象a (在Qt中应用程序对象只能有一个)
QApplication a(argc, argv);
//2.创建窗口对象 w (窗口对象mywidget父类为QWidget)
myWidget w;
//3.调用窗口对象的show方法进行窗口显示
w.show();
//4.让应用程序对象进入消息循环(让代码阻塞在该行)
return a.exec();
}
mywidget.cpp
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
class myWidget : public QWidget {
Q_OBJECT//Q_OBJECT宏 允许类中使用信号与槽的机制
public:
myWidget(QWidget *parent = nullptr);
~myWidget();
};
#endif // MYWIDGET_H
#include "mywidget.h"
#include <QPushButton>
myWidget::myWidget(QWidget *parent):QWidget(parent){
//1.设置主窗口属性
resize(600, 400);//设置主窗口大小
setWindowTitle("第一个窗口");//设置主窗口标题
setFixedSize(600, 400);//设置主窗口大小固定
\
//2.创建并设置第一个窗口属性
QPushButton *btn = new QPushButton;//创建一个按钮
btn->setParent(this);//让btn对象依赖在mywidget窗口中显示
btn->setText("QPushBtn1");//显示文本
//3.创建并设置第二个窗口属性
QPushButton *btn1 = new QPushButton("QPushBtn2", this);//按照控件大小创建窗口
btn1->move(100, 0);//移动按钮位置
btn1->resize(100, 30);//设置按钮的大小
}
myWidget::~myWidget(){ }
三、对象模型
1.对象树引入
QObject是以对象树的形式组织起来的:
注意到Qt在按钮创建时都使用了new操作符在堆区开辟了内存,但是没有进行delete操作?因为其会自动释放,从而不需要delete操作,
在Qt中创建对象的时候会提供一个Parent对象指针,
- 当创建一个QObject对象时,其构造函数会接收一个QObject指针(parent父对象指针)作为参数,相当于在创建QObject对象时,可以为其提供一个父对象,创建的QObject对象会自动添加到父对象的children()列表中。
- 当父对象析构时children列表中的所有对象都会被析构,注意这里的父对象并不是继承意义上的父类,这种机制在 GUI 程序设计中相当有用(简化了内存回收机制)。例如按钮有一个QShortcut对象作为其子对象,当按钮被删除的时快捷键也会被删除。
- 当一个QObject对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过对象树中对象的顺序是没有定义的,这意味着销毁这些对象的顺序也是未定义的。
- 任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。
2.存在的问题
引入对象树的概念,在一定程度上解决了内存问题:
- QObject在栈上创建Qt 保持同样的行为,正常情况下这不会发生什么问题:
{
QWidget window;
QPushButton quit("Quit", &window);
}
作为父组件的 window 和作为子组件的 quit 都是QObject的子类(事实上,它们都是QWidget的子类,而QWidget是QObject的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++要求局部对象的析构顺序应该按照其创建顺序的相反过程。因此这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。
- 但如果将代码修改为下面的代码:
{
QPushButton quit("Quit");
QWidget window;
quit.setParent(&window);
}
这里析构顺序就出现了问题,在上面的代码中作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。
在析构过程中它会调用子对象列表中每一个对象的析构函数,也就是说 quit 此时就被析构了。代码继续执行在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此程序崩溃了。
由此得知 Qt 的对象树机制虽然在一定程度上解决了内存问题,但是也引入了一些值得注意的问题。这些细节在以后的开发过程中需要注意,最好从开始就养成习惯:尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。