以下是本人学习笔记

一、创建项目

qt如何开发ios程序_c++


qt如何开发ios程序_qt5_02


qt如何开发ios程序_qt如何开发ios程序_03


qt如何开发ios程序_qt5_04


初学者基类可以选择QWidget,QWidget是QMainWindow和QDialog的基类

二、创建第一个Qt程序

讲解见代码注释
main.cpp

#include "mywidget.h"
#include <QApplication> // 包含一个应用程序类的头文件

int main(int argc, char *argv[])
{
	// a:应用程序对象,Qt中,有且仅有一个应用程序对象
    QApplication a(argc, argv);
    // 窗口对象,mywidget父类:QWidget
    myWidget w;
    // 窗口对象默认不会显示,必须调用show方法显示窗口
    w.show();

	// 让应用程序对象进入消息循环机制(对象一直在捕获消息)
	// 使代码阻塞在该行
    return a.exec();
}

mywidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget> // 包含头文件QWidget窗口类

class myWidget : public QWidget
{
    Q_OBJECT // Q_OBJECT宏,允许类中使用信号和槽的机制

public:
	// 构造函数(parent默认值为0,默认值的设定在声明和构造中只能有一处有)
    myWidget(QWidget *parent = 0);
    ~myWidget();
};

#endif // MYWIDGET_H

三、命名规范及快捷键

  1. 命名规范:类名使用驼峰法;函数名使用小驼峰命名法
  2. 快捷键:
    整行移动:ctrl+shift+上/下
    帮助文档:F1
    自动对齐:ctrl+i
    同名之间的.h和.cpp文件之间的切换:F4

四、QPushButton的创建

mywidget.cpp

#include "mywidget.h"
#include <QPushButton>

myWidget::myWidget(QWidget *parent)
    : QWidget(parent)
{
    // 创建一个按钮
    QPushButton* btn1 = new QPushButton;
    btn1->setParent(this);
    btn1->setText("hello");

    // 创建按钮2:更快捷,但是有弊端:会按照控件的大小创建窗口
    QPushButton* btn2 = new QPushButton("hello again", this);
    // 移动按钮2
    btn2->move(100, 100);

    // 所以可以reset窗口大小
    resize(600, 400);
    // 设置固定窗口大小
    setFixedSize(600, 400);
    
    // 设置窗口标题
    setWindowTitle("this is a window");
}

myWidget::~myWidget()
{

}
  1. QPushButton的父类的父类是QWidge,如上所述,窗口对象默认不会弹出,因此需要调用方法使其出现
  2. show函数默认以顶层的方式弹出,即控件会单独有一个窗口
  3. 调用setParent函数设置控件的父亲,使得它父亲出现的时候,这个控件就出现
    this:指向当前对象的指针
  4. 后创建的控件如果于先创建的控件重合会将其覆盖,因此需要设置每个控件的位置

五、对象树

在Qt中创建对象会提供一个Parent对象指针

  1. QObject是以对象树的形式组织起来的
    在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的Children()列表;
    当父对象析构时,这个列表中的所有对象也会被析构;
    当创建的对象在堆区时,如果指定的父亲是QObject派生下来的类或者QObject子类派生下来的类,可以不用管理释放的操作,将对象放入对象树中,一定程度上简化了内存回收的机制
  2. QWidget是能够在屏幕上显示一切组件的父类
  3. 局部对象的析构顺序与其创建顺序相反
  4. 在Qt中,尽量在构造时就指定parent对象,并且大胆在堆上创建

例:创建一个新类
MyPushButton.h

#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H

#include <QPushButton>

class MyPushButton : public QPushButton
{
    Q_OBJECT
public:
    explicit MyPushButton(QPushButton *parent = 0);
    ~MyPushButton();

signals:

public slots:
};

#endif // MYPUSHBUTTON_H

MyPushButton.cpp

#include "mypushbutton.h"
#include <QDebug>

MyPushButton::MyPushButton(QPushButton *parent) : QPushButton(parent)
{
    qDebug() << "call button class";
}

MyPushButton::~MyPushButton()
{
    qDebug() << "~MyPushButton";

}

六、Qt中的坐标系

  1. 左上角为(0,0)
  2. x以右为正方向
  3. y以下为正方向

七、信号和槽

connect(信号的发送者, 发送的具体信号, 信号的接收者, 信号的处理(槽))

  1. 信号的发送者和接收者:一般为对象
  2. 发送的具体信号:函数的地址
  3. 槽函数(slot):信号处理函数,传入函数地址(官方文档中查找对象父类的相关函数)

信号槽的优点:松散耦合,信号发送端和信号接收端本身是没有关联的,通过connect函数连接,将两端耦合在一起

1.使用系统函数

mywidget.cpp

#include "mywidget.h"
#include <QPushButton>
#include "mypushbutton.h"

myWidget::myWidget(QWidget *parent)
    : QWidget(parent)
{
    // 创建一个按钮
    QPushButton* btn1 = new QPushButton;
    btn1->setParent(this);
    btn1->setText("hello");

    // 创建按钮2:更快捷,但是有弊端:会按照控件的大小创建窗口
    QPushButton* btn2 = new QPushButton("hello again", this);
    // 移动按钮2
    btn2->move(100, 100);
    btn2->resize(100, 100);

    // 创建按钮3
    MyPushButton* btn3 = new MyPushButton;
    btn3->setText("hello a and a");
    btn3->setParent(this);
    btn3->move(200,200);

    // 所以可以reset窗口大小
    resize(600, 400);
    // 设置固定窗口大小
    setFixedSize(600, 400);

    // 设置窗口标题
    setWindowTitle("this is a window");

    // 需求:点击按钮,关闭窗口
    connect(btn1, &MyPushButton::clicked, this, &myWidget::close);
}

myWidget::~myWidget()
{

}

2.自定义的信号和槽

Student类

#ifndef STUDENT_H
#define STUDENT_H

#include <QObject>

class Student : public QObject
{
    Q_OBJECT
public:
    explicit Student(QObject *parent = nullptr);

signals:

public slots:
    // 早期Qt版本,槽函数必须写到public slots,高级版本可以写到public或者全局下
    // 返回值void,需要声明,也需要实现
    // 可以有参数,可以发生重载
    void treat();
};

#endif // STUDENT_H
#include "student.h"
#include <QDebug>

Student::Student(QObject *parent) : QObject(parent)
{

}

void Student::treat()
{
    qDebug() << "treat teacher";
}

Teacher类

#ifndef TEACHER_H
#define TEACHER_H

#include <QObject>

class Teacher : public QObject
{
    Q_OBJECT
public:
    explicit Teacher(QObject *parent = nullptr);

signals:
    // 自定义信号写到signals下
    // 返回值是void,只需要声明,不需要实现
    // 可以有参数,可以重载
    void hungry();

public slots:
};

#endif // TEACHER_H
#include "teacher.h"

Teacher::Teacher(QObject *parent) : QObject(parent)
{

}

MyWidget

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include "teacher.h"
#include "student.h"

namespace Ui {
class MyWidget;
}

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MyWidget(QWidget *parent = 0);
    ~MyWidget();

private:
    Ui::MyWidget *ui;
    Teacher* sir;
    Student* son;

    void classOver();
};

#endif // MYWIDGET_H
#include "mywidget.h"
#include "ui_mywidget.h"

// 模拟场景
// Teacher类 Student类
// 下课后,老师会触发一个信号,饿了,学生响应信号,请客吃饭

MyWidget::MyWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MyWidget)
{
    ui->setupUi(this);

    // 创建一个老师对象
    this->sir = new Teacher(this);

    // 创建一个学生对象
    this->son = new Student(this);

    // connect
    connect(sir, &Teacher::hungry, son, &Student::treat);

    // 调用下课函数
    classOver();
}

void MyWidget::classOver()
{
    // 下课函数,触发老师饿了的信号
    emit sir->hungry();
}

MyWidget::~MyWidget()
{
    delete ui;
}

注意连接和触发信号的顺序!
触发自定义信号:emit 自定义信号

3.自定义的信号和槽发生重载的解决

mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"

// 模拟场景
// Teacher类 Student类
// 下课后,老师会触发一个信号,饿了,学生响应信号,请客吃饭

MyWidget::MyWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MyWidget)
{
    ui->setupUi(this);

    // 创建一个老师对象
    this->sir = new Teacher(this);

    // 创建一个学生对象
    this->son = new Student(this);

    // connect
//    connect(sir, &Teacher::hungry, son, &Student::treat);

    // 指针指向地址
    // 函数指针指向函数地址
    void(Teacher:: *teacherSignal)(QString) = &Teacher::hungry;
    void(Student:: *studentSlot)(QString) = &Student::treat;
    // 连接带参数的信号和槽
    connect(sir, teacherSignal, son, studentSlot);

    // 调用下课函数
    classOver();
}

void MyWidget::classOver()
{
    // 下课函数,触发老师饿了的信号
//    emit sir->hungry();
    emit sir->hungry("cola");
}

MyWidget::~MyWidget()
{
    delete ui;
}

如果函数有重载,在连接信号槽时就需要创建函数指针,寻找到指定函数,明确指向函数地址

4.信号连接信号

按钮点击信号->老师信号->学生请客
详见注释
mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"
#include <QPushButton>

// 模拟场景
// Teacher类 Student类
// 下课后,老师会触发一个信号,饿了,学生响应信号,请客吃饭

MyWidget::MyWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MyWidget)
{
    ui->setupUi(this);

    // 创建一个老师对象
    this->sir = new Teacher(this);

    // 创建一个学生对象
    this->son = new Student(this);

    // 指针指向地址
    // 函数指针指向函数地址:带参数的信号和槽
    void(Teacher:: *teacherSignal1)(QString) = &Teacher::hungry;
    void(Student:: *studentSlot1)(QString) = &Student::treat;
    // no带参数的信号和槽
    void(Teacher:: *teacherSignal2)(void) = &Teacher::hungry;
    void(Student:: *studentSlot2)(void) = &Student::treat;
    // 连接带参数的信号和槽
    connect(sir, teacherSignal1, son, studentSlot1);
    // 连接no参数的信号和槽
    connect(sir, teacherSignal2, son, studentSlot2);

    // press button -> class over
    QPushButton* btn1 = new QPushButton("class over 1", this);
    QPushButton* btn2 = new QPushButton("class over 2", this);
    btn2->move(100, 0);
    this->resize(600, 400);

    // 信号连接槽
    connect(btn1, &QPushButton::clicked, this, &MyWidget::classOver);

    // 信号连接信号
    connect(btn2, &QPushButton::clicked, sir, teacherSignal2);

    // 断开信号
    // disconnect(sir, teacherSignal1, son, studentSlot1);
    disconnect(sir, teacherSignal2, son, studentSlot2);
}

void MyWidget::classOver()
{
    // 下课函数,触发老师饿了的信号
    emit sir->hungry();
    emit sir->hungry("cola");
}

MyWidget::~MyWidget()
{
    delete ui;
}
  1. 信号可以连接信号
  2. 一个信号可以连接多个槽函数
  3. 多个信号可以连接同一个槽函数
  4. 信号和槽函数的参数必须类型一一对应(相当于信号传参给槽函数)
  5. 信号的参数个数可以大于槽函数的参数,但是剩下的参数的类型需要一一对应(要求顺序)

八、Lambda表达式

qt版本5.4及之前需要在.pro文件中添加CONFIG += c++11才可以使用Lambda表达式(在文件末尾添加即可)
c++11中的Lambda表达式用于定义并创建匿名的函数对象
一个基础的Lambda表达式结构:

[](){}

函数声明

[=](){
	btn->setText("btnn");
}

函数声明及调用

[=](){
	btn->setText("btnn");
}()

解释:

  1. [ ]:函数对象参数:函数对象参数只能使用到那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this),函数对象参数有以下形式:【ps:推荐使用=】
空:		没有使用任何函数对象参数
=:		函数体内可以使用上述范围中的所有参数,值传递
&:		函数体内可以使用上述范围中的所有参数,引用传递
this:	可以使用Lambda所在类中的成员变量
a:		只能使用a的值,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符(但是无法修改参数本身)
&a:	a引用传递
=,&a:	除了a引用传递,其余值传递
&,a:	除了a值传递,其余引用传递
  1. ():操作符重载函数参数
  2. ()和{}之间可以有 可修改标识符:mutable
  3. ()和{}之间可以有 函数返回值
int ret = []()->int{return 1000;}();
  1. {}:函数体

例:使用Lambda表达式 实现点击按钮 关闭窗口

// 信号连接信号(带参数)
    connect(btn2, &QPushButton::clicked, sir, [=](){
        emit sir->hungry("cola");
        btn2->resize(120, 50);
    });

使用lambda的好处:连接时,无参的信号不可以连接有参的槽函数/信号,使用Lambda表达式可以在函数体中触发有参信号

练习

设计一个按钮,点击时弹出新窗口,再次点击时新窗口关闭