用qt创建的第一个工程

摘要:本文主要是利用向导建立了第一个qt工程文件,主要介绍了工程文件的结构、main函数、按钮的建立、qt中的对象树、坐标系、qt中的信号和槽等概念。

1、工程文件的结构

利用qt导向建立好工程文件以后,会自动生成main函数、头文件、源文件和Pro文件,如下图:

2、main函数

在这个main函数中,主要创建了一个窗口对象w,调用构造函数,实现一些按钮、信号和槽的功能。

 1 #include "mywidget.h" //包含头文件
 2 #include <QApplication> //包含QApplication头文件
 3 
 4 //程序入口 argc命令行变量的数量 argv命令行变量数组
 5 int main(int argc, char *argv[])
 6 {
 7     QApplication a(argc, argv); //a 应用程序对象,对于Qt项目必须有应用程序对象,而且有且仅有一个
 8     MyWidget w; //创建一个MyWidget对象
 9     w.show(); //创建出的窗口对象并不会直接显示,需要调用show方法
10 
11     return a.exec(); //进入消息循环机制,阻塞状态
12 //    while(true)
13 //    {
14 //        if(点击叉子)
15 //            break;
16 //    }
17 }

3、myweiget.cpp和myweiget.h

在头文件中,重点注意类中加了“Q_OBJECT”,构造函数有默认参数。

 1 #ifndef MYWIDGET_H
 2 #define MYWIDGET_H
 3 
 4 #include <QWidget>
 5 
 6 //继承于QWidget
 7 class MyWidget : public QWidget
 8 {
 9     //支持Qt中的信号和槽使用
10     Q_OBJECT
11 
12 public:
13     MyWidget(QWidget *parent = 0); //构造
14     ~MyWidget(); //析构
15 };
16 
17 #endif // MYWIDGET_H

在源文件中,注意函数变量的名字两个单词之间用大写字母隔开;注意创建按钮的两种方法;为按钮添加文本,设置按钮位置,设置按钮大小;重置窗口的大小和窗口名称等方法。重点是学会查看帮助文档。

 1 #include "mywidget.h"
 2 #include<QPushButton>
 3 #include "mybutton.h"
 4 #include <QDebug>
 5 
 6 MyWidget::MyWidget(QWidget *parent)
 7     : QWidget(parent)
 8 {
 9     QPushButton * btn =new QPushButton;
10 
11     // btn->show();
12     //btn应该依赖于主窗口
13     btn->setParent(this);
14     //显示文字
15     btn->setText("德玛");
16 
17     //第二种创建方式
18     QPushButton * btn2 = new QPushButton("德玛西亚",this);
19     //移动窗口
20     btn2->move(100,100);
21     //重置窗口大小
22     resize(960,640);
23 
24     //btn可不可以 resize? 可以
25     btn2->resize(50,50);
26 
27     //设置窗口标题名称
28     this->setWindowTitle("德玛西亚万岁");
29 
30     //对象树
31     MyButton * myBtn = new MyButton();
32     myBtn->setParent(this);
33     myBtn->move(200,200);
34     myBtn->setText("我的按钮");
35 
36     //窗体的坐标系
37     //左上角为 0 0 点
38     // x 以右侧为正方向  y 以下侧为正方向
39 
40     //需求 点击“我的按钮” ,关闭窗口
41     //连接信号槽的关键字 connect
42     //4个参数 参数1 信号发送者   参数2  发送的信号   参数3  信号的接受者  参数4 处理的槽函数
43     //参数2 和参数4 需要的都是函数地址
44 
45     connect(myBtn,&QPushButton::clicked,this,&MyWidget::close);
46 
47 }
48 
49 MyWidget::~MyWidget()
50 {
51     qDebug("MyWidget析构了!");
52 }

4、对象树

先来看一幅图:

(1)在qt中,QObject是以对象树的形式组织起来的。

  •  当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表例如上面的3中的构造函数MyWidget::MyWidget(QWidget *parent): QWidget(parent)
  • 当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)这种机制在 GUI 程序设计中相当有用。例如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当我们删除按钮的时候,这个快捷键理应被删除。这是合理的。

(2)QWidget是能够在屏幕上显示的一切组件的父类。

  • QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。
  • 当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除比如,当我们删除了一个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。
  • Qt 引入对象树的概念,在一定程度上解决了内存问题。

(3)当一个QObject对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。

(4)任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。

(5)注意的问题:

如果QObject在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下下面的代码片段:

1 {
2     QWidget window;
3     QPushButton quit("Quit", &window);
4 }

作为父组件的 window 和作为子组件的 quit 都是QObject的子类(事实上,它们都是QWidget的子类,而QWidget是QObject的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。

但是,如果我们使用下面的代码:

1 {
2     QPushButton quit("Quit");
3     QWidget window;
4     quit.setParent(&window);
5 }

情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。

由此我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。

(6)总结:

  • 所有new出来的对象 不用管释放
  • 原因 children表中的对象会在窗口关闭后进行自动释放

(7)测试代码

mybutton.h

 1 #ifndef MYBUTTON_H
 2 #define MYBUTTON_H
 3 
 4 #include <QWidget>
 5 #include <QPushButton>
 6 
 7 class MyButton : public QPushButton
 8 {
 9     Q_OBJECT
10 public:
11     explicit MyButton(QWidget *parent = 0);
12     ~MyButton();
13 signals:
14 
15 public slots:
16 };
17 
18 #endif // MYBUTTON_H

mybutton.cpp

 1 #include "mybutton.h"
 2 #include <QDebug>
 3 MyButton::MyButton(QWidget *parent) : QPushButton(parent)
 4 {
 5 
 6 }
 7 
 8 MyButton::~MyButton()
 9 {
10     qDebug() << "MyButton调用析构了!";
11 
12 }

上面代码的执行结果是:

MyWidget析构了!

MyButton调用析构了!

这样的析构顺序和我我们想象的不太一致,我们会认为是先析构按钮再析构窗口,然而显示的结果却不是这样的。实际上我们的想法是正确的,这是因为在执行到窗口析构的过程中,会出现按钮的析构,等按钮析构完成以后,再返回窗口析构。

5、窗口的坐标系

原点在左上角

6、信号槽的基本使用

具体的使用见代码

猜你喜欢

转载自www.cnblogs.com/lzy820260594/p/11344566.html