摘要:本文主要是利用向导建立了第一个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、信号槽的基本使用
具体的使用见代码