(QT中记得修改时保存代码,否则有些资源会加载失败,被坑很多次了.....)
一、加载资源文件
对于图片等资源文件我们需要将其打包成文件夹,使用相对路径使得使用者能加载到对应路径文件
之后选择QT--》资源文件
之后添加前缀及添加对应文件
二、信号与槽机制(重点)
是QT中用来实现多个组件之间互相通信的机制
2.1信号与槽
信号:就是信号函数,是一个信号函数,定义在类体内signals权限下,是一个不完整的函数,只有声明没有定义,你不能直接调用它,只能通过emit关键字来触发
槽:就是槽函数,定义在类体的slots权限下,是一个完整的函数,既有声明也有定义,可以被当做普通函数被使用
槽声明了就得定义,但是信号不用,声明了不触发也可以,二者返回值一般都是void,参数可以自定义(参数的目的为了传信息)
2.2信号与槽的连接
ui界面下信号与槽的连接方式只能发射系统提供的信号,可使用系统提供的槽函数接收
也可右键转到槽,选择信号,槽函数骡子自己书写
而连接函数分为多种形式
- qt4版本的连接函数(该版本是不友好的连接,因为它即使错了也不会给你报错)
[static] QMetaObject::Connection //返回值是一个连接,并且该函数是一个静态成员函数
QObject::connect( //函数名
const QObject *sender, //信号的发射者,是组件的指针
const char *signal, //要发射的信号是一个C语言风格的字符串,将信号函数传递进来时,需要使用SIGNAL宏进行转换
const QObject *receiver, //信号的接受者,是组件的指针
onst char *method) //处理信号的槽函数,是C风格字符串,将槽函数传递进来时,需要使用SLOT宏进行转换
注意:信号函数必须是信号的发射者所在的类中有的信号函数,而槽函数也必须是信号的接受者中有的槽函数
举个例子:
QLabel *label = new QLabel;
QScrollBar *scrollBar = new QScrollBar;
QObject::connect(scrollBar, SIGNAL(valueChanged(int)),
label, SLOT(setNum(int)));
qt5版本的连接函数
[static] QMetaObject::Connection //返回值是一个连接,并且该函数是一个静态成员函数
QObject::connect( //函数名
const QObject *sender, //信号的发射者,是组件的指针
PointerToMemberFunction signal, //信号函数的函数指针变量,直接填写函数名即可
const QObject *receiver, //信号的接受者,是组件的指针
PointerToMemberFunction method) //槽函数的函数指针变量,直接填写函数名即可
举个例子:
QLabel *label = new QLabel;
QLineEdit *lineEdit = new QLineEdit;
QObject::connect(lineEdit, &QLineEdit::textChanged,
label, &QLabel::setText);
信号也可连接lambda表达式或外部函数
[static] QMetaObject::Connection //返回值是一个连接,并且该函数是一个静态成员函数
QObject::connect( //函数名
const QObject *sender, //信号的发射者,是组件的指针
PointerToMemberFunction signal, //信号函数的函数指针变量,直接填写函数名即可
Functor functor) //处理信号的功能函数,可以是全局函数,也可以是lambda表达式
举个例子:
void someFunction();
QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::clicked, someFunction);
Lambda 表达式作为槽函数:
QByteArray page = ...;
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("qt-project.org", 80);
QObject::connect(socket, &QTcpSocket::connected, [=] () {
socket->write("GET " + page + "\r\n");
});
2.3断开连接
断开信号与槽的连接时使用disconect
参数可不变
void Widget::on_btn6_clicked()
{
disconnect(btn3, SIGNAL(pressed()), ui->btn1, SLOT(hide()));
disconnect(ui->btn4, &QPushButton::clicked, this, &Widget::my_slot);
}
2.3发射自定义信号
使用emit 加自定义信号名即可发射,在与槽函数连接后,槽函数会立即响应
2.4信号与槽的案例——语音播报
先在初始配置文件中,添加TextToSpeech库
头文件中记得加头文件,及定义一个播报指针
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QDebug>
#include<QPushButton>
#include<QTextToSpeech> //文本转语音的头文件
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT //信号与槽的元对象
signals:
void my_signal(); //自定义信号函数
public slots:
void my_slot(); //自定义的槽函数
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_btn2_clicked();
void on_btn6_clicked();
private:
Ui::Widget *ui;
//自定义一个按钮
QPushButton *btn3;
//定义一个播报员指针
QTextToSpeech *speecher;
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
void fun()
{
qDebug()<<"我是过路的";
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//给播报员实例化空间
speecher = new QTextToSpeech(this);
//给btn3实例化空间
this->btn3 = new QPushButton("按钮3", this);
this->btn3->move(ui->btn2->x(), ui->btn2->y()+50);
this->btn3->resize(ui->btn2->size());
//使用qt4版本的连接,将按钮3发射的pressed信号与按钮1的槽函数进行连接
connect(btn3, SIGNAL(pressed()), ui->btn1, SLOT(hide()));
//参数1:信号的发射者,按钮3的指针
//参数2:要发射的信号,是在按钮所在类中拥有的信号函数,需要使用SIGNAL宏函数进行转换
//参数3:信号的接受者,ui界面上的按钮1指针
//参数4:处理信号的槽函数,是信号接收者所在类中拥有的槽函数
//注意:该链接方式是不友好的链接,原因是 即使宏函数中写错,编译器不报错,但是没有现象
//使用qt5版本的链接,将ui界面上的btn4按钮发射的clicked信号,与当前界面的自定义的槽函数连接
connect(ui->btn4, &QPushButton::clicked, this, &Widget::my_slot);
//将ui界面上的按钮5,发射的clicked信号,连接到全局函数中
connect(ui->btn5, &QPushButton::clicked, fun);
//将ui界面上的按钮5发射的clicked信号连接到lambda表达式中
connect(ui->btn5, &QPushButton::clicked, [&](){
// qDebug()<<"我是路人乙";
speecher->say("我是路人乙");
});
//将当前界面的my_signal信号连接到自定义的槽函数中
connect(this, &Widget::my_signal, [&](){
speecher->say("已经成功断开按钮3和按钮4的连接");
});
}
Widget::~Widget()
{
delete ui;
}
//自定义槽函数的实现
void Widget::my_slot()
{
static int num = 0;
if(num%2)
{
ui->btn2->setEnabled(false);
}else
{
ui->btn2->setEnabled(true);
}
num++;
}
//该函数就是按钮2的clicked信号对应的槽函数
void Widget::on_btn2_clicked()
{
static int num = 0;
if(num % 3 == 0)
{
ui->btn2->setStyleSheet("background-color:red;");
}else if(num % 3 == 1)
{
ui->btn2->setStyleSheet("background-color:yellow;");
}else
{
ui->btn2->setStyleSheet("background-color:green;");
}
num++;
}
//断开按钮对应的槽函数
void Widget::on_btn6_clicked()
{
disconnect(btn3, SIGNAL(pressed()), ui->btn1, SLOT(hide()));
disconnect(ui->btn4, &QPushButton::clicked, this, &Widget::my_slot);
//发射自定义的信号
emit my_signal();
}
练习:优化登录界面,当点击登录按钮后,在该按钮对应的槽函数中,判断账户和密码框内的数据是否为admin和123456,如果账户密码匹配成功,则提示登录成功并关闭登录界面,如果账户和密码匹配失败,则提示登录失败,并将密码框中的内容清空。
当点击取消按钮后,则关闭整个登录界面
要求:对登录按钮使用QT5版本的连接,对取消按钮使用qt4版本的连接
widget.cpp
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//设置页面
this -> resize(540,410);
this ->setFixedSize(540,410);
this -> setWindowTitle("weifeng client");
//this -> setStyleSheet("background-color:pink;");
this -> setWindowIcon(QIcon(":/picture/ME.png"));
//界面图
lab1 = new QLabel(this);
lab1 -> resize(540,150);
lab1 -> setPixmap(QPixmap(":/picture/starcat.png"));
lab1 -> setScaledContents(true);
lab1->setStyleSheet("border-radius: 10px;");
lab = new QLabel(this);
lab -> resize(540,(410-150));
lab -> move(0,lab1 -> height());
lab -> setPixmap(QPixmap(":/picture/main-background.png"));
lab -> setScaledContents(true);
lab->setStyleSheet("border-radius: 10px;");
effect = new QGraphicsOpacityEffect(); // 注意这里只影响 'lab'
effect->setOpacity(0.4);
lab->setGraphicsEffect(effect);
//设置登录按键
btn1 = new QPushButton("登录",this);
btn1 -> move(145,300);
btn1 -> resize(100,50);
btn1 -> setIcon(QIcon(":/picture/16.gif"));
connect(btn1,&QPushButton::clicked,this,&Widget::my_slot);
//设置取消按键
btn2 = new QPushButton("取消",this);
btn2 -> move(btn1 -> x()+150,btn1 -> y());
btn2 -> resize(100,50);
btn2 -> setIcon(QIcon(":/picture/15.gif"));
// btn2 -> connect(btn2,&QPushButton::clicked,this,&Widget::my_slot);//关闭
btn2 -> connect(btn2,SIGNAL(clicked()),this,SLOT(close()));
//账号前图片
lab2 = new QLabel(this);
lab2 -> resize(50,50);
lab2 -> move(145,175);
lab2 -> setPixmap(QPixmap(":/picture/08.gif"));
lab2 -> setScaledContents(true);
//密码前图片
lab3 = new QLabel(this);
lab3 -> resize(lab2 -> size());
lab3 -> move(lab2 -> x(),lab2 -> y()+50);
lab3 -> setPixmap(QPixmap(":/picture/12.gif"));
lab3 -> setScaledContents(true);
//账号
font.setBold(true); //设置加粗
lab4 = new QLabel("账号",this);
lab4->setFixedHeight(30);
lab4->setFont(font);
lab4 -> move(lab2 -> x()+55,lab2 -> y()+20);
//密码
lab5 = new QLabel("密码",this);
lab5 -> setFixedHeight(30);
lab5 -> setFont(font);
lab5 -> move(lab3 -> x()+55,lab3 -> y()+20);
//账号栏
edit1 = new QLineEdit(this);
edit1 -> resize(140,lab4 -> height());
edit1->setFixedHeight(30);
edit1 -> move(lab4 -> x()+50,lab4 -> y());
edit1 -> setPlaceholderText("QQ/手机/邮箱");
//密码栏
edit2 = new QLineEdit(this);
edit2 -> resize(140,lab5 -> height());
edit2->setFixedHeight(30);
edit2 -> move(lab5 -> x()+50,lab5 -> y());
edit2 -> setEchoMode(QLineEdit::Password);
edit2 -> setPlaceholderText("密码");
speecher = new QTextToSpeech(this);
}
void Widget::my_slot(){
QString username = edit1 ->text();
QString password = edit2 ->text();
if(username == "admin"){
if(password == "123456"){
speecher -> say("登录成功");
close();
return;
}
}
speecher -> say("登录失败");
edit2->clear();
}
/*
* 版本qt5
void Widget::my_slot(){
close();
}
*/
Widget::~Widget()
{
}
main
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QDebug>
#include <QIcon>
#include <QLineEdit>
#include <QLabel>
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
#include <QTextToSpeech>
#include <QFont>
#include <QString>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void my_signal();
public slots:
void my_slot();
private:
QLabel *lab1;
QLabel *lab;
QGraphicsOpacityEffect* effect;
QPushButton *btn1;
QPushButton *btn2;
QLabel *lab2;
QLabel *lab3;
QFont font;
QLabel *lab4;
QLabel *lab5;
QLineEdit *edit1;
QLineEdit *edit2;
QTextToSpeech *speecher;
};
#endif // WIDGET_H
2.5信号函数与槽函数的总结
一个信号函数发射出,多个相连接的槽函数都可执行
一个槽函数也可以同时响应多个与他连接的信号,只要一个信号被发射,该槽函数就会被执行
一个信号函数可以连接到另一个信号函数,一个被发射后一个也会被发射
信号与槽函数参数表为了传参有以下准则
1、信号函数和槽函数进行链接时,一般要求信号函数和槽函数的参数保持一致
connect(信号发送者, SIGNAL(signalFun()),信号接收者, SLOT(slotFun())); //Ok
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(int))); //Ok
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun(int, char))); //Ok
connect(信号发送者, SIGNAL(signalFun(char, int)),信号接收者, SLOT(slotFun(int, char))); //False
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(char))); //False
2、当信号函数的参数大于槽函数的参数时
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun())); //Ok
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun(int))); //Ok
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun(char))); //False
3、当信号函数的参数小于槽函数的参数时
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(int, char))); //False
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(int, char=0))); //Ok
1.多态,虚函数,纯虚函数
多态:允许对象以多种方式存在,而多态分为静态和动态,
静态多态就是函数重载/运算符重载和模板,是编译时多态,通过相同的函数名,不同的参数列表实现重载,模板就是提前假定一个类型进行,通过需要的类型传递后达成。
动态多态是运行时多态,主要是通过虚函数实现函数重写,父类的指针或者引用,指向或初始化子类的对象,调用子类对父类重写的函数,进而展开子类的功能。
虚函数:在动态多态中使用到,在函数前加上virtual关键字,父类中该函数是虚函数的话,允许继承的子类同名同类型函数对父类虚函数进行函数重写
纯虚函数:当父类中虚函数被子类用来重写,且没有定义的意义,这个时候一般把父类的虚函数设置成纯虚函数,当子类对父类的虚函数重写时,且抽象类中无纯虚函数,即实例化成功,否则也是抽象类。
2.将“引用”作为函数参数有哪些特点
普通函数参数分为值传递和地址传递,值传递的参数值出了函数作用域后,空间收回无法影响实参值,地址传递是将地址传到函数中,能影响实参,但是需要额外申请指针大小的空间,所以在c++中引入的引用参数,由于引用与被引用的参数占同一片地址,所有既可以不另外申请空间,又能影响到原来的值,这样效率更高。当不想修改值时也可使用常引用。
3.结构体和联合体有什么区别
联合体中变量占据空间各不相同,但是联合体中共同占用一块空间
结构体中每个成员也有独立的值,而联合体中修改一个成员的值也会修改到其他成员
结构体计算大小时满足字节对齐原则,而联合体中不是,主要看最大元素大小