文章目录
1、在pro文件里
.pro就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件。.pro文件的写法如下:
注释
从“#”开始,到这一行结束。
模板变量告诉qmake为这个应用程序生成哪种makefile。下面是可供使用的选择:TEMPLATE = app; app -建立一个应用程序的makefile。这是默认值,所以如果模板没有被指定,这个将被使用。
#指定生成的应用程序名:
TARGET = QtDemo
#工程中包含的头文件
HEADERS += include/painter.h
#工程中包含的.ui设计文件
FORMS += forms/painter.ui
#工程中包含的源文件
SOURCES += sources/main.cpp sources
#工程中包含的资源文件
RESOURCES += qrc/painter.qrc
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
这条语句的含义是,如果QT_MAJOR_VERSION大于4(也就是当前使用的Qt5及更高版本)需要增加widgets模块。如果项目仅需支持Qt5,也可以直接添加“QT += widgets”一句。不过为了保持代码兼容,最好还是按照QtCreator生成的语句编写。
#配置信息
CONFIG用来告诉qmake关于应用程序的配置信息。
CONFIG += c++11 //使用c++11的特性
在这里使用“+=”,是因为我们添加我们的配置选项到任何一个已经存在中。这样做比使用“=”那样替换已经指定的所有选项更安全。
pro文件里注释是用 #
QT += core gui #包含的模块
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets#大于4版本以上的 包含widget模块
TARGET = tcpTestServer #目标 生成的.exe程序的名称
TEMPLATE = app # 模板 应用程序模板 Application
SOURCES += main.cpp\ # 源文件
mainwindow.cpp
HEADERS += mainwindow.h # 头文件
FORMS += mainwindow.ui
2、 一些命名规范
//命令规范
//类名 首字母大写 单词和单词之间首字母大写
//函数名 变量名称 首字母小写 单词和单词之间首字母大写
//快捷键
//注释和取消注释都是 ctrl + /
//运行 ctrl + r
//编译 ctrl+b
//ctrl + f 查找
//自动对齐 ctrl +i
//同名之间的**.h和.cpp文件之间的切换** F4
//帮助文档 F1
3、在.cpp文件里进行一些基础的设置
myWidgetcpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QPushButton> //按钮控制的头文件
myWidget::myWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::myWidget)
{
//创建第一个按钮
QPushButton * btn1=new QPushButton;//创建一个按钮并显示
btn1->show();//show以顶层的方式弹出控件
btn1->setParent(this);//让btn对象 依赖在myWidget窗口中 this指针指向当前对象 myWidget
//让按钮里显示指定的文字
btn1->setText("Mr.liang 的第一个按钮");
//创建第二个按钮 按照控件的大小创建窗口
QPushButton * btn2=new QPushButton("第二个按钮",this);
btn2->show();
//移动btn1的位置
btn1->move(100,100);
//重新设置按钮的大小
btn1->resize(200,30);
//重置窗口的大小 此时用户是可以随意拖拽窗口的大小的
resize(555,555);
//设置固定的窗口大小
setFixedSize(600,400);
//设置窗口标题
setWindowTitle("Mrliang");
//ui->setupUi(this); //把这行代码注释掉可以自定义窗口title
}
myWidget::~myWidget()
{
delete ui;
}
设置好的窗口界面这样的:
qt中的对象树
4、添加mypushbutton.h 和mypushbutton.cpp文件进行父类调用子类的实现,此时的父类是mywidgetqs
其实主要是通过引入对象树的概念,来讲解父类和子类的构造和析构顺序:
构造:先父类再子类
析构:先子类再父类
此时文件包含:mypushbutton.h
#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H
#include <QPushButton>
class MyPushButton : public QPushButton
{
Q_OBJECT
public:
explicit MyPushButton(QWidget *parent = nullptr);
~MyPushButton();//析构函数
signals:
};
#endif // MYPUSHBUTTON_H
mypushbutton.cpp
#include "mypushbutton.h"
#include<QDebug> //包含打印信息的头文件
MyPushButton::MyPushButton(QWidget *parent) : QPushButton(parent)
{
qDebug()<<"我的按钮类构造调用";
}
MyPushButton::~MyPushButton()
{
qDebug()<<"我的按钮析构";
}
此时的mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QPushButton> //按钮控制的头文件
#include"mypushbutton.h"
#include<QDebug> //包含打印信息的头文件
myWidget::myWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::myWidget)
{
//创建第一个按钮
QPushButton * btn1=new QPushButton;//创建一个按钮并显示
btn1->show();//show以顶层的方式弹出控件
btn1->setParent(this);//让btn对象 依赖在myWidget窗口中 this指针指向当前对象 myWidget
//让按钮里显示指定的文字
btn1->setText("Mr.liang 的第一个按钮");
//创建第二个按钮 按照控件的大小创建窗口
QPushButton * btn2=new QPushButton("第二个按钮",this);
btn2->show();
//移动btn1的位置
btn1->move(100,100);
//重新设置按钮的大小
btn1->resize(200,30);
//重置窗口的大小 此时用户是可以随意拖拽窗口的大小的
resize(555,555);
//设置固定的窗口大小
setFixedSize(600,400);
//设置窗口标题
setWindowTitle("Mrliang");
//创建一个我自己的按钮
MyPushButton * mybtn=new MyPushButton;
mybtn->setText("Mrliangz自己的按钮");
mybtn->move(200,200);
mybtn->setParent(this);//设置到对象树中
// myWidget::~myWidget()
// {
// qDebug()<<"myWidget的析构调用";
// };
//ui->setupUi(this); //把这行代码注释掉可以自定义窗口title
}
myWidget::~myWidget()
{
qDebug()<<"myWidget的析构调用";
delete ui;
}
5、qt里的窗口坐标系
对于嵌套窗口,其坐标是相对于父窗口而言的
6、信号和槽
主要通过槽的技术实现按钮和信号的关联;
伪代码:
connect( 信号的发送者,发送的具体信号,信号的接收者,信号的处理(槽) )
**信号槽的优点:松散耦合,信号发送槽和接收端本身是没有关联的;通过connct连接将两端连接在一起 **
实现代码:
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QPushButton> //按钮控制的头文件
#include"mypushbutton.h"
#include<QDebug> //包含打印信息的头文件
myWidget::myWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::myWidget)
{
//创建第一个按钮
QPushButton * btn1=new QPushButton;//创建一个按钮并显示
btn1->show();//show以顶层的方式弹出控件
btn1->setParent(this);//让btn对象 依赖在myWidget窗口中 this指针指向当前对象 myWidget
//让按钮里显示指定的文字
btn1->setText("Mr.liang 的第一个按钮");
//创建第二个按钮 按照控件的大小创建窗口
QPushButton * btn2=new QPushButton("第二个按钮",this);
btn2->show();
//移动btn1的位置
btn1->move(100,100);
//重新设置按钮的大小
btn1->resize(200,30);
//重置窗口的大小 此时用户是可以随意拖拽窗口的大小的
resize(555,555);
//设置固定的窗口大小
setFixedSize(600,400);
//设置窗口标题
setWindowTitle("Mrliang");
//创建一个我自己的按钮
MyPushButton * mybtn=new MyPushButton;
mybtn->setText("我自己的按钮");
mybtn->move(200,200);
mybtn->setParent(this);//设置到对象树中
//信号和槽
//提出需求:点击我自己的按钮 关闭窗口
//参数1 信号的发送者 参数2 发送的信号(函数的地址) 参数3 信号的接收者 参数4 处理的槽函数
//j具体实现
//第一种实现方式
connect(mybtn,&MyPushButton::clicked,this,&myWidget::close );
//第二种实现方式
connect(mybtn,&QPushButton::clicked,this,&QWidget::close);
//ui->setupUi(this); //把这行代码注释掉可以自定义窗口title
}
myWidget::~myWidget()
{
qDebug()<<"myWidget的析构调用";
delete ui;
}
达到的效果:此时点击"我自己的按钮“就可以关闭这个窗口
实现自定义的信号和槽及其重载时的解决方案
添加student类 ,响应teacher的要求:
此时的widget.cpp
#include "widget.h"
#include "ui_widget.h"
//提出需求
//Teacher 类 老师类
//Student 类 学生类
//下课后 老师会触发一个信号 饿了;学生响应信号 请客吃饭
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建老师对象
this->zl=new Teacher(this);
//创建学生对象
this->st=new Student(this);
//这是没有参数的连接实现
//实现老师饿了 学生请客的连接 信号和槽的连接
// connect(zl,&Teacher::hungry,st,&Student::treat);
// //在类内调用下课的函数
// classIsOver();
//当实现有参数的函数重载时的解决方案
//实现带参数的连接函数 由于设置了函数的重载 这里无法区分到底是哪个函数地址 需要创建一个函数指针来指向函数地址
//创建两个函数指针明确指出指向的是有参数的那个 就不会发生歧义了
void( Teacher:: *teacherSignal)(QString)= &Teacher::hungry;
void(Student:: *studentSlot)(QString)=&Student::treat;
connect(zl,teacherSignal,st,studentSlot);
classIsOver();
}
//类外实现下课的方法
void Widget::classIsOver()
{
//无参数
//下课函数 调用后 触发老师饿了的信号
emit zl->hungry();
//有参数
emit zl->hungry("宫保鸡丁");
}
Widget::~Widget()
{
delete ui;
}
添加的student.h文件和.cpp文件
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
signals:
public slots:
//在这里定义一些槽函数 返回值void 需要声明 也需要实现
//可以有参数 可以发生重载
void treat();//请客函数
void treat(QString foodName);//定义函数重载
};
#endif // STUDENT_H
student.cpp
#include "student.h"
#include<QDebug> //输出信号包含的头文件
Student::Student(QObject *parent) : QObject(parent)
{
}
//在源文件里实现槽函数
void Student::treat()
{
qDebug()<<"请老师吃饭";
}
//实现重载的函数
void Student::treat(QString foodName)
{
qDebug()<<"请老师吃饭 老师要吃:"<< foodName ;//此时的foodName时字符串类型
//用下面的方式将string 类型转换为char *类型
qDebug()<<"请老师吃饭,老师要吃:"<<foodName.toUtf8().data();
}
teacher.h
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
//在这里写自定义的信号
//无返回值 void 只需要声明 不需要实现
//可以有参数 可以重载
void hungry(); //饿了
void hungry(QString foodName);//信号重载 qt里的字符串:QString
};
#endif // TEACHER_H
teacher.cpp
#include "teacher.h"
Teacher::Teacher(QObject *parent) : QObject(parent)
{
}
//这里不需要实现
实现触发按钮再响应操作:连接与断开连接
主要是在widget.cpp中实现:
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>//按钮类
//提出需求
//Teacher 类 老师类
//Student 类 学生类
//下课后 老师会触发一个信号 饿了;学生响应信号 请客吃饭
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建老师对象
this->zl=new Teacher(this);
//创建学生对象
this->st=new Student(this);
//这是没有参数的连接实现
//实现老师饿了 学生请客的连接 信号和槽的连接
// connect(zl,&Teacher::hungry,st,&Student::treat);
// //在类内调用下课的函数
// classIsOver();
//当实现有参数的函数重载时的解决方案
//实现带参数的连接函数 由于设置了函数的重载 这里无法区分到底是哪个函数地址 需要创建一个函数指针来指向函数地址
//创建两个函数指针明确指出指向的是有参数的那个 就不会发生歧义了
void( Teacher:: *teacherSignal)(QString)= &Teacher::hungry;
void(Student:: *studentSlot)(QString)=&Student::treat;
connect(zl,teacherSignal,st,studentSlot);
classIsOver();
//实现点击一个下课的按钮 再触发下课
QPushButton * btn=new QPushButton("下课",this);
//重置窗口的大小
this->resize(600,400);
//点击按钮 触发下课 实质是信号调用槽函数
//connect(btn,&QPushButton::clicked,this,&Widget::classIsOver);
//无参信号和槽连接
void( Teacher:: *teacherSignal2)(void)= &Teacher::hungry;
void(Student:: *studentSlot2)(void)=&Student::treat;
connect(zl,teacherSignal2,st,studentSlot2);
//实现信号连接信号,中间的信号再连接槽的操作 (按钮点击信号--老师信号--学生请客
//点击QPushbutton 连接到信号teacherSignal2,然后再连接到槽函数studentSlot2
connect(btn,&QPushButton::clicked,zl,teacherSignal2);
//实现断开信号之间的连接 断开老师信号和学生信号之间的连接 那么无法响应最开始按钮的响应了
disconnect(zl,teacherSignal2,st,studentSlot2);
//补充一些扩展
//1 信号是可以连接信号的
//2 一个信号可以连接多个槽函数
//3 多个信号可以连接同一个槽函数
//4 信号和槽函数的类型一一对应
//5 信号和槽函数的个数不一定保持一致 信号个数可以比槽函数个数多
//qt4版本之前的信号和槽的连接方式
//利用qt4信号槽 连接无参版本
connect(zl,SIGNAL(hungry()),st,SLOT(treat()));
//qt4版本的优点是参数直观 缺点是类型不会做检测
//qt5以上版本 支持qt4的版本写法 反之不支持
//补充关于lambda表达式
// []标识符 匿名函数
// = 址传递
// & 引用传递
// ()参数
// {}函数实现体
// mutable 修饰址传递变量 可以修改拷贝出的数据 但是改变不了主体
// 返回值 []()->{}
//用lambda表达式实现
// =是址传递的方式 &是传引用的方式
[=](){
btn->setText("aaaa");
}(); //后面的小括号是调用这个lambda函数
//若改写以下
// QPushButton * btn2=new QPushButton;
//捕获列表[ ]
[btn]()
{
btn->setText("aa");
//btn2->setText("bbbb");//此时 btn2是不能被捕捉到的 因为捕捉列表里是btn
}();
// int ret = []()->int{return 1000;}();
// qDebug()<<"ret="<<ret;
//利用lambda表达式 实现点击按钮 关闭窗口.
QPushButton *btn2=new QPushButton;
btn2->setText({"关闭"});
btn->move(100,0);
btn2->setParent(this);
connect(btn2,&QPushButton::clicked,this,[=](){
// this->close();//实现关闭窗口
// emit zl->hungry("宫保鸡丁");
btn2->setText("aaaa");
});
}
//类外实现下课的方法
void Widget::classIsOver()
{
//无参数
//下课函数 调用后 触发老师饿了的信号
emit zl->hungry();
//有参数
emit zl->hungry("宫保鸡丁");
}
Widget::~Widget()
{
delete ui;
}
信号和槽的机制
7、补充一下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修饰符。
&a。将a按引用进行传递。
a, &b。将a按值进行传递,b按引用进行传递。
=,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
&, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
② 操作符重载函数参数;
标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b)
③ 可修改标示符;
mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
④ 函数返回值;
->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
⑤ 是函数体;
{},标识函数的实现,这部分不能省略,但函数体可以为空。