QT是一套应用程序开发库,但与MFC不同,QT是跨平台的开发类库。
跨平台意味着只需要编写一次程序,在不同平台无需改动或只需少许改动编译,就可以形成在不同平台上运行的版本。
1.2 优点- 跨平台
- 接口简单,容易上手
- 一定程度上简化了内存回收
2.1名称跟创建的路径不能含中文!!!
2.2如果不编译,会显示红色
2.3 默认创建有窗口类myWidget,基类有三种选择:QWidget(类似于空窗口),QMainWindow以及QDialog三个
QWidget 是所有用户界面元素的基类,窗口和控件都是直接或间接继承自 QWidget,QMainWindow、QWidget、QDialog 三个类就是用来创建窗口的,可以直接使用也可以继承后再使用。
QMainWindow 窗口可以包含菜单栏、工具栏、状态栏、标题栏等,是最常见的窗口形式,可以作为GUI程序的主窗口。
QDialog 是对话框窗口的基类。对话框主要用来执行短期任务,或与用户进行互动,它可以是模态的也可以是非模态的。QDialog 没有菜单栏、工具栏、状态栏等。
.pro就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件。
3.2 一个简单的Qt程序程序介绍:
#include "widget.h" #include3.3 .pro文件解释//包含一个应用程序类的头文件 //main程序入口 argc命令行变量的数量 argv命令行变量的数组 int main(int argc, char *argv[]) { //a应用程序对象,在Qt中,应用程序对象,有且仅有一个 QApplication a(argc, argv); //窗口对象 Widget父类 -> Qwidget Widget w; //窗口对象 默认不会显示,必须要调用show方法显示窗口 w.show(); //让应用程序对象进入消息循环 //让代码阻塞到这行 return a.exec(); }
QT += core gui Qt包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于4版本以上 包含 widget模块
TARGET = 01_FirstProject //目标 生成的.exe程序的名称
TEMPLATE = app //模板 应用程序模板 Application
SOURCES += main.cpp //源文件
mywidget.cpp
HEADERS += mywidget.h //头文件
3.4 weidget.h 头文件
#ifndef WIDGET_H //防止重复包含(防止重命名,防止头文件重复编译) #define WIDGET_H #include3.5 命名规范//包含一个头文件 QWidget 窗口类 class Widget : public QWidget { Q_OBJECT //Q_OBJECT宏,允许类中使用信号和槽的机制 public: Widget(QWidget *parent = nullptr); //有参构造函数,默认值 ~Widget(); //析构函数 }; #endif // WIDGET_H
类名 首字母大写,单词和单词之间首字母大写
函数名和变量名 首字母可以小写,单词和单词之间首字母大写
3.6 快捷键注释 ctrl + /
运行 ctrl + r
编译 ctrl + b
字体缩放 ctrl + 鼠标滚轮
查找功能 ctrl + f
整行移动 ctrl + shift + ↑ 或者 ↓
帮助文档 F1
自动对齐 ctrl + i
同名之间的.h和.cpp切换 F4
3.7 main.cpp#include "widget.h" #include3.8 按钮控件常用API//包含一个应用程序类的头文件 //main程序入口 argc命令行变量的数量 argv命令行变量的数组 int main(int argc, char *argv[]) { //a应用程序对象,在Qt中,应用程序对象,有且仅有一个 QApplication a(argc, argv); //窗口对象 Widget父类 -> Qwidget Widget w; //窗口对象 默认不会显示,必须要调用show方法显示窗口 w.show(); //让应用程序对象进入消息循环 //让代码阻塞到这行 return a.exec(); }
示范如何使用帮助文档:
QPushButton
头文件:要包含 #include
qmake :所属的模块widgets
Inherits :继承的父类QAbstractButton
Inherited By:子类QCommandlinkButton
再点击QabstractButton会发现又继承QWidget
3.9 widget.cpp#include "widget.h" #includeWidget::Widget(QWidget *parent) : QWidget(parent) { //①、创建一个按钮 QPushButton * btn = new QPushButton; //btn->show(); //show以顶层方式弹出窗口控件 //让btn对象,依赖在widget 窗口中 btn->setParent(this); //显示文本 btn->setText("第一个按钮"); //②、创建第二个按钮 按照控件的大小创建窗口 QPushButton * btn2 = new QPushButton("第二个按钮",this); //移动btn2按钮 btn->move(100,100); //重置窗口大小 resize(600,400); //设置固定窗口大小 setFixedSize(600,400); //设置窗口标题 setWindowTitle("第一个窗口"); } Widget::~Widget() { }
常用的:
-
创建 QPushButton * btn = new QPushButton
-
设置父亲 setParent(this)
-
设置文本 setText(“文字”)
-
设置位置 move(宽,高)
-
重新指定窗口大小 resize
-
设置窗口标题 setWindowTitle
-
设置窗口固定大小 setFixedSize
- 当创建的对象在堆区时候,如果指定的父亲是QObject派生下来的类或者QObject子类派生下来的类,可以不用管理释放的操作,将对象会放入到对象树中。
- 一定程度上简化了内存回收机制
坐标体系:
以左上角为原点(0,0),X向右增加,Y向下增加。
四、信号和槽的机制 4.1 连接函数 connectconnect(信号的发送者, 发送的具体信号,信号的接受者,信号的处理(槽))
注:如果信号的接受者是 this ,那么可以省略
信号槽的优点,松散耦合,信号发送端和接收端本身是没有关联的,通过connect连接,将两端耦合在一起
4.2 参数- 参数1 信号的发送者
- 参数2 发送的信号(函数的地址)
- 参数3 信号的接受者
- 参数4 处理的槽函数
connect(myBtn,&QPushButton::clicked,this,&QWidget::close);五、自定义信号和槽 5.1 自定义信号
- 返回void
- 需要声明,不需要实现
- 可以有参数
- 写到signals 下
- 返回void
- 需要声明,也需要实现
- 可以有参数,可以重载
- 写到 public slot 下或者 public 或者 全局函数
emit 自定义信号
5.4 案例 下课后,老师触发了信号,学生响应信号,请客吃饭
student.h
#ifndef STUDENT_H #define STUDENT_H #includeclass Student : public QObject { Q_OBJECT public: explicit Student(QObject *parent = nullptr); signals: public slots: //槽函数 //早起Qt版本 必须写到public slots 高级版本可以写到public或者全局下 //返回值 void ,需要声明,也需要实现 //可以有参数,可以发生重载 void treat(); }; #endif // STUDENT_H
teacher.h
#ifndef TEACHER_H #define TEACHER_H #includeclass Teacher : public QObject { Q_OBJECT public: explicit Teacher(QObject *parent = nullptr); signals: //自定义信号 写到signal下 //返回值是void 只需要声明,不需要实现 //可以有参数 ,可以重载 void hungry(); }; #endif // TEACHER_H
widget.h
#ifndef WIDGET_H #define WIDGET_H #include#include "student.h" #include "teacher.h" QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private: Ui::Widget *ui; Teacher * zt; Student * st; void classIsOver(); }; #endif // WIDGET_H
main.cpp
#include "widget.h" #includeint main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }
student.cpp
#include "student.h" #includeStudent::Student(QObject *parent) : QObject(parent) { } void Student::treat() { qDebug()<<"请老师吃饭"; }
teacher.cpp
#include "teacher.h"
Teacher::Teacher(QObject *parent) : QObject(parent)
{
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
//Teacher 类 老师类
//Student 类 学生类
//下课后 ,老师会触发一个信号,饿了,学生响应信号,请客吃饭
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个老师对象 放到对象树中new Teacher(this) 就不用管他的释放了
this->zt = new Teacher(this);
//创建一个学生对象
this->st = new Student(this);
//老师饿了 学生请客的链接
connect(zt,&Teacher::hungry,st,&Student::treat);
//调用下课 函数
classIsOver();
}
void Widget::classIsOver()
{
//下课函数 调用后 触发老师饿了的信号
//触发 emit 关键字
emit zt ->hungry();
}
Widget::~Widget()
{
delete ui;
}
六、 当自定义信号和槽发生重载时
6.1 解决函数重载的问题
利用函数指针 明确指向函数的地址:
void( Teacher:: * tSignal )( QString ) = &Teacher::hungry;
6.2 解决输出会带 “ ”的问题QString 转 char *
- .ToUtf8() 转为 QByteArray
- .Data() 转为 char *
#include "widget.h" #include "ui_widget.h" #include6.4 信号可以连接信号//Teacher 类 老师类 //Student 类 学生类 //下课后 ,老师会触发一个信号,饿了,学生响应信号,请客吃饭 Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //创建一个老师对象 放到对象树中new Teacher(this) 就不用管他的释放了 this->zt = new Teacher(this); //创建一个学生对象 this->st = new Student(this); // //老师饿了 学生请客的链接 // connect(zt,&Teacher::hungry,st,&Student::treat); // //调用下课 函数 // classIsOver(); //连接带参数的 信号和槽 信号发生重载了 要这样写 //指针 -> 地址 //函数指针 -> 函数地址 函数注意点:1.括号不可少 2.类型要匹配 void(Teacher::*teacherSignal)(QString) = &Teacher::hungry; void(Student::*studentSlot)(QString) = &Student::treat; connect(zt,teacherSignal,st,studentSlot); classIsOver(); //点击一个 下课的按钮,再触发下课 信号连接槽 QPushButton * btn = new QPushButton("下课",this); //重置窗口的大小 this->resize(600,400); //点击按钮 触发下课 connect(btn,&QPushButton::clicked,this,&Widget::classIsOver); } void Widget::classIsOver() { //下课函数 调用后 触发老师饿了的信号 //触发 emit 关键字 //emit zt ->hungry(); emit zt->hungry("宫保鸡丁"); } Widget::~Widget() { delete ui; }
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个老师对象 放到对象树中new Teacher(this) 就不用管他的释放了
this->zt = new Teacher(this);
//创建一个学生对象
this->st = new Student(this);
//无参信号和槽连接
void(Teacher::*teacherSignal2)(void) = &Teacher::hungry;
void(Student::*studentSlot2)(void) = &Student::treat;
connect(zt,teacherSignal2,st,studentSlot2);
//信号连接信号
connect(btn,&QPushButton::clicked,zt,teacherSignal2);
//断开信号 断开哪个复制哪个
disconnect(zt,teacherSignal2,st,studentSlot2);
}
void Widget::classIsOver()
{
//下课函数 调用后 触发老师饿了的信号
//触发 emit 关键字
//emit zt ->hungry();
emit zt->hungry("宫保鸡丁");
}
6.5 断开信号 disconnect
//断开信号 断开哪个复制哪个 disconnect(zt,teacherSignal2,st,studentSlot2);七、拓展
1.信号可以连接信号
2.一个信号可以连接多个槽函数
3.多个信号 可以连接 同一个槽函数
4.信号和槽函数的参数 必须类型一一的对应
5.信号和槽的参数个数 是不是要一致?信号的个数 可以多于槽函数的参数个数
比如void(Teacher::*teacherSignal)(QString) = &Teacher::hungry;
connect(btn,&QPushButton::clicked,zt,teacherSignal); 就不行,因为clicked参数是bool类型,后面是QString
6.Qt4版本以前的信号和槽连接方式
利用Qt4信号槽 连接无参数
connect(zt,SIGNAL(hungry()),st,SLOT(treat()));
Qt4版本优点,参数直观;缺点,类型不做检测 比如connect(zt,SIGNAL(hungry()),st,SLOT(treat(QString))); 这样错的,但是不报错
//emit zt ->hungry();
emit zt->hungry(“宫保鸡丁”);
}
## 6.5 断开信号 disconnect ```cpp //断开信号 断开哪个复制哪个 disconnect(zt,teacherSignal2,st,studentSlot2);七、拓展
1.信号可以连接信号
2.一个信号可以连接多个槽函数
3.多个信号 可以连接 同一个槽函数
4.信号和槽函数的参数 必须类型一一的对应
5.信号和槽的参数个数 是不是要一致?信号的个数 可以多于槽函数的参数个数
比如void(Teacher::*teacherSignal)(QString) = &Teacher::hungry;
connect(btn,&QPushButton::clicked,zt,teacherSignal); 就不行,因为clicked参数是bool类型,后面是QString
6.Qt4版本以前的信号和槽连接方式
利用Qt4信号槽 连接无参数
connect(zt,SIGNAL(hungry()),st,SLOT(treat()));
Qt4版本优点,参数直观;缺点,类型不做检测 比如connect(zt,SIGNAL(hungry()),st,SLOT(treat(QString))); 这样错的,但是不报错
C++11中的Lambda表达式**用于定义并创建匿名的函数对象**,以简化编程工作。
首先看一下Lambda表达式的基本构成:
[capture] (parameters) mutable ->return-type
{
statement
}
[函数对象参数] (操作符重载函数参数) mutable ->返回值{函数体}
①、函数对象参数:
-
[] ,标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:
空。没有使用任何函数对象参数。 -
=。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
-
&。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
-
this。函数体内可以使用Lambda所在类的成员变量。
-
a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
例如以下这个就是错误,因为没有传递btn2
QPushButton * btn2 = new QPushButton; [btn](){ btn->setText("aaa"); btn2->setText("bbb"); // btn2看不到 }(); -
&a。将a按引用进行传递。
-
a, &b。将a按值进行传递,b按引用进行传递。
-
=,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
-
&, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
②、操作符重载函数参数:
标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
③、可修改标识符:
mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
例如:[m] ()mutable { m = 100 + 10; qDebug() << m; }
不加mutable不可以修改
//mutable关键字 用于修饰值传递的遍历,修改的是拷贝,而不是本体
QPushButton * myBtn = new QPushButton (this);
QPushButton * myBtn2 = new QPushButton (this);
myBtn2->move(100,100);
int m = 10;
connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 100 + 10; qDebug() << m; });
connect(myBtn2,&QPushButton::clicked,this,[=] () { qDebug() << m; });
qDebug() << m;
④、函数返回值:
->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
例如:
int ret = []()->int{return 10;}();
qDebug()<<"ret = "<
输出ret = 10
⑤、是函数体:
{},标识函数的实现,这部分不能省略,但函数体可以为空。
使用注意事项:
如果Lambda表达式后面不加()只是一个函数的声明,加了才是函数的调用。
[=](){
btn->setText("aaa");
}();
8.2 案例
//利用Lambda表达式 实现点击按钮 关闭窗口
QPushButton * btn2 = new QPushButton;
btn2->setText("关闭");
btn2->move(100,0);
btn2->setParent(this);
connect(btn2,&QPushButton::clicked,this,[=](){
this->close();
emit zt->hungry("宫保鸡丁");
});



