Qt是一个跨平台的C++图形用户界面应用框架 91年奇趣科技开发
pro工程文件介绍 .pro就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件
cpp
QT += core gui //Qt包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于4版本包含 widgets模块
TARGET = Qt_First //生成exe文件的名称
TEMPLATE = app //应用程序模板
SOURCES += main.cpp\ //源文件
mywidget.cpp
HEADERS += mywidget.h //头文件
安装和创建文件方法在上一章已经解决,接下来就是说具体知识总结
其他知识:命名规范
运行 ctrl+r
编译 ctrl+b
注释 ctrl+/
字体缩放 ctrl+鼠标滚轮
整行移动 ctrl+shift+上下箭头
自动对齐 ctrl+i
同名的h和cpp切换 F4
查找关键字 ctrl+f
帮助文档 直接找qt文档,或者左边有帮助
在Qt程序中,最常用的控件之一就是按钮了,首先我们来看下如何创建一个按钮
按钮 PushButton
cpp
QPushButton * btn = new QPushButton; 头文件 #include <QPushButton>
//设置父亲
//如果想显示时候依赖在当前的窗口中,需要设置父窗
btn->setParent(this);
//设置文字
btn->setText("德玛西亚");
//移动位置
btn->move(100,100);
//第二种创建
QPushButton * btn2 = new QPushButton("孙悟空",this);
//重新指定窗口大小
this->resize(600,400);
//设置窗口标题
this->setWindowTitle("第一个项目");
//限制窗口大小
this->setFixedSize(600,400);//就不能拉伸了
QPushButton----------QAbstractButton-----------QWidget 关系为孙父爷关系
上面代码中,一个按钮其实就是一个QPushButton类下的对象,如果只是创建出对象,是无法显示到窗口中的,所以我们需要依赖一个父窗口,也就是指定一个父亲利用setParent函数即可,如果想设置按钮上显示的文字利用setText,移动按钮位置用move
对于窗口而言,我们可以修改左上角窗口的标题setWindowTitle,重新指定窗口大小:resize,或者设置固定的窗口大小setFixedSize;
对象树
如图所示,我们言简意赅 树的创建是由上向下,释放时是由下向上,很丝滑
但你如果自己做一个析构函数,你会发现析构顺序和销毁顺序不同
析构函数,先调用父亲但是不执行(就是找到),再调用儿子,执行
Qt窗口坐标 体系
坐标体系:
以左上角为原点(0,0),X向右增加,Y向下增加。
对于嵌套窗口,其坐标是相对于父窗口来说的。
信号和槽机制
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后 ,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal) 。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数 ,意思是,将想要 处理的信号和 自己的一个函数(称为槽(slot))绑定来处理这个信号 。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
举例1,人 2.摩擦 3.神灯 4.出神灯
要做一个连接 将12与34连接起来 结果可以为人在摩擦---神灯后可以出神灯
1.信号发送者(指针) 2.发送的信号 地址 3.信号的接收者 指针 4.处理的槽函数 地址、
connect(myBtn,&MyPushButton::clicked,this,&MyWidget::close);
这个是用好封装的函数调用的地址,用qt帮助文档查找的
以下属于自定义信号和槽
widget.h中 //设置老师和学生 Teacher * zt; Student * st;
teacher.h中
//自定义信号 写到signal下 //1.返回值是void //2.只需要声明,不需要实现 //3.可以有参数,可以发生重载 signals: void hungry();//空的 void hungry(QString foodName);//传参
student.h中 signals: //自定义 //槽函数 写到public slots下,或者public下,或者全局函数,或者lambda //返回值 void //需要声明 需要实现 //可以有参数 可以重载 public slots: void treat(); void treat(QString foodName); };
student.cpp中
void Student::treat()//加作用域 { qDebug()<<"请老师吃饭"; } void Student::treat(QString foodName)//有参传递 { //QString转为char * //先调用toUtf-8转为QByteArray //再调用data 转为 char* qDebug()<<"请老师吃饭,老师要吃: "<<foodName.toUtf8().data();//消去引号 }
ps:teacher.cpp中不用添加修改什么
widget.cpp中 这里是重点,反复看里面的代码
cpp
#include "widget.h"
#include "QPushButton"
#include "QDebug"
//Teacher老师类 Student学生类
//下课后,老师会发出饿了的信号,学生进行响应,请老师吃饭
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->zt=new Teacher(this);//在窗口中连接信号槽
this->st=new Student(this);
//连接信号和槽
// connect(this->zt,&Teacher::hungry,this->st,&Student::treat);
//连接有参的信号和槽
//函数指针指向函数地址
// void(Teacher:: * teacherSignal)(QString)=&Teacher::hungry;
// void(Student:: * studentSlot)(QString)=&Student::treat;
// connect(this->zt,teacherSignal,this->st,studentSlot);
//按键
QPushButton *btn=new QPushButton;
btn->setParent(this);
btn->setText("下课");
//无参
void(Teacher:: * teacherSignal2)()=&Teacher::hungry;
void(Student:: * studentSlot2)()=&Student::treat;
connect(this->zt,teacherSignal2,this->st,studentSlot2);
// 老师饿了(--学生请客)
//信号和槽拓展
//1.信号连接信号 //无参
connect(btn,&QPushButton::clicked,this->zt,teacherSignal2);
// bool >= 0
//连接方式 点击按钮--老师饿了(--学生请客)
//2.一个信号可以连接多个槽函数
//3.多个信号可以绑定同一个槽函数
//4.信号和槽的参数类型,必须是一一对应 传的信号是宫保鸡丁,接受的也必须是宫保鸡丁QString--QSt
//5.信号的参数个数可以多于槽函数,反之不可以 例如(QString,int)---(QString)正确
//6.相同个数的参数类型也要一一对应
//7.可以利用disconnect断开信号槽的连接
// disconnect(btn,&QPushButton::clicked,this->zt,teacherSignal2);断前面
// disconnect(this->zt,teacherSignal2,this->st,studentSlot2);断后面
//下课函数调用
// classISOver();//连接作用
//lanbda表达式
QPushButton *btn2=new QPushButton;
btn2->move(100,0);
btn2->setParent(this);
btn2->setText("aaa");
//函数体内可以使用lambda所在范围内所有可见的局部变量
[=](){
btn2->setText("bbb");
}();
//->返回值类型
int num=[]()->int{return 1000;}();
qDebug()<<"num"<<num;
//[=](){}推荐使用
QPushButton * btn3=new QPushButton;
btn3->setParent(this);
btn3->move(200,200);
btn3->setText("aaa");
connect(btn3,&QPushButton::clicked,this,[=]()//优化了上面信号连接信号的有参
{
btn3->setText("ccc");
this->st->treat("鱼香肉丝");//直接调槽函数 在connect中调另一个函数
this->close();
});//用=正确(复制一个,间接改)用&错误!(直接修改)
}
void Widget::classISOver(){
//触发自定义信号
emit this->zt->hungry();
emit this->zt->hungry("宫保鸡丁");
}
Widget::~Widget()
{
}
挑里面两个重点代码说说
这里是无参信号连接槽
cpp
this->zt=new Teacher(this);//在窗口中连接信号槽
this->st=new Student(this);
//连接信号和槽
connect(this->zt,&Teacher::hungry,this->st,&Student::treat);
这里是有参信号连接槽
cpp
//连接有参的信号和槽
//函数指针指向函数地址
void(Teacher:: * teacherSignal)(QString)=&Teacher::hungry;
void(Student:: * studentSlot)(QString)=&Student::treat;
connect(this->zt,teacherSignal,this->st,studentSlot);
注意:题目前提是
下课后,老师会发出饿了的信号,学生进行响应,请老师吃饭
所以添加一个触发信号,下课了,老师肚子饿了,学生请客吃饭,符合逻辑。
cpp
void Widget::classISOver(){
//触发自定义信号
emit this->zt->hungry();
emit this->zt->hungry("宫保鸡丁");
}
信号连接信号 connect代码,点一下按钮,连接老师饿了,回到上面几行,老师饿了,学生请客
cpp
//无参
void(Teacher:: * teacherSignal2)()=&Teacher::hungry;
void(Student:: * studentSlot2)()=&Student::treat;
connect(this->zt,teacherSignal2,this->st,studentSlot2);
// 老师饿了(--学生请客)
//信号和槽拓展
//1.信号连接信号 //无参
connect(btn,&QPushButton::clicked,this->zt,teacherSignal2);
// bool > 0
//连接方式 点击按钮--老师饿了(--学生请客)
这里只能是无参调用,因为clicked为bool类型,如果后面teacherSignal2得到了参数 如:
emit this->zt->hungry("宫保鸡丁");
connect(btn,&QPushButton::clicked,this->zt,teacherSignal2);将此代码理解为clicked是bool类型为1,是信号,teacherSingnal2为槽函数,当传入参数时QString(宫保鸡丁)就为失败
因为
1一个信号可以连接多个槽函数 2.多个信号可以绑定同一个槽函数 3.信号和槽的参数类型,必须是一一对应 传的信号是宫保鸡丁,接受的也必须是宫保鸡丁QString--QString 4.信号的参数个数可以多于槽函数,反之不可以 例如(QString,int)---(QString)正确 5.相同个数的参数类型也要一一对应 6.可以利用disconnect断开信号槽的连接
自定义信号槽需要注意的事项 :
- 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
- 信号和槽函数返回值是 void
- 信号只需要声明,不需要实现
- 槽函数需要声明也需要实现
- 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
- 使用 emit 在恰当的位置发送信号;
- 使用connect()函数连接信号和槽。
- 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数
- 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
- 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。
信号槽的拓展
- 一个信号可以和多个槽相连
如果是这种情况,这些槽会一个接一个的被调用,但是它们的 调用顺序是不确定的。
- 多个信号可以连接到一个槽
只要任意一个信号发出,这个槽就会被调用。
- 一个信号可以连接到另外的一个信号
当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
- 槽可以被取消链接
这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
- 信号槽可以断开
利用disconnect关键字是可以断开信号槽的
- 使用Lambda 表达式
在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。
在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。后面我们会详细介绍什么是Lambda表达式
那人问了,那该怎么办呢?
lambda函数
引入lambda函数 Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。
**[capture](parameters) mutable ->return-type//**Lambda表达式的基本构成
{
statement
}
[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}
函数对象参数;
[],标识一个Lambda的开始 ,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式
- 空。没有使用任何函数对象参数。
- =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
3.&。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
那么如何实现lambda 实现信号连接信号有参呢?
cpp
//lanbda表达式
QPushButton *btn2=new QPushButton;
btn2->move(100,0);
btn2->setParent(this);
btn2->setText("aaa");
//函数体内可以使用lambda所在范围内所有可见的局部变量
[=](){
btn2->setText("bbb");
}();
//->返回值类型
int num=[]()->int{return 1000;}();
qDebug()<<"num"<<num;
//[=](){}推荐使用
QPushButton * btn3=new QPushButton;
btn3->setParent(this);
btn3->move(200,200);
btn3->setText("aaa");
connect(btn3,&QPushButton::clicked,this,[=]()//优化了上面信号连接信号的有参
{
btn3->setText("ccc");
this->st->treat("鱼香肉丝");//直接调槽函数 在connect中调另一个函数
this->close();
});//用=正确(复制一个,间接改)用&错误!(直接修改)
}
精华代码在这
connect(btn3,&QPushButton::clicked,this,[=]()//优化了上面信号连接信号的有参
{
btn3->setText("ccc");
this->st->treat("鱼香肉丝");//直接调槽函数 在connect中调另一个函数
this->close();
});//用=正确(复制一个,间接改)用&错误!(直接修改)
直接调槽函数 在connect中调另一个函数 ,槽函数也是函数,所以在connect中直接调用
connect中加了一个lambda函数
另外有一个重点:
在lambda[]用=正确(复制一个,间接改)用&错误!(直接修改)
完结,收工