记录开发
前言:
昨天学习到信号与槽,今天先把这部分内容补全;
目录
5. 信号与槽(2)
查找 QtAssistant,发现 QPushButton 中的信号 signal 全是继承于父类;
点击 QAbstractButton 类,其 signal 有以下几种:
分别表示点击,按压,释放,切换,而现在我们按钮所要实现的功能是关闭窗口,故只需要信号 clicked 即可;
而信号的接收者应该是窗口,也即 QWidget 类,在助手中列出了一个 public slots,即公共槽函数:
点击第一个槽函数 close() 即为需要的函数:
“关闭当前窗口,若关闭成功返回 true,否则返回 false;”
那么代码语句如下:
connect(btn2, &QPushButton::clicked, this, &QWidget::close);
即可让 btn2 实现窗口关闭功能;
接下来自定义信号与槽:
创建两个类:
Teacher 类 老师类;
Student 类 学生类;
功能:
下课后,老师发送信号 饿了,学生响应信号 请客吃饭;
创建类时,由于是自定义,故直接继承最原始的父类 QObject,并且由于我选用的是 CMake 文件编译的,所以还要在 CMakeLists.txt 文件中加上如下语句:
if(ANDROID)
add_library(SignalAndSlot SHARED
main.cpp
widget.cpp
widget.h
widget.ui
teacher.h //添加到工程;
teacher.cpp //添加到工程;
student.cpp
student.h
${TS_FILES}
)
else()
add_executable(SignalAndSlot
main.cpp
widget.cpp
widget.h
widget.ui
teacher.h //添加到工程;
teacher.cpp //添加到工程;
student.cpp
student.h
${TS_FILES}
)
代码如下:
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();
};
#endif // TEACHER_H
student.h
#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
student.cpp
#include "student.h"
#include <QDebug>
Student::Student(QObject *parent) : QObject(parent)
{
}
void Student::treat() //槽函数的实现;
{
qDebug() << "treat the teacher"; //自动换行;
}
teacher.cpp 为空,除默认构造函数外没有需要实现的的函数;
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "teacher.h" //注意1:若要在 widget 中创建对象,首先就要在头文件中将他们的头文件包含进来,然后在类中加入对象成员;
#include "student.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 *Li; //添加到私有成员;
Student *Zhang;
void afterClass();
};
#endif // WIDGET_H
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 -> Li = new Teacher(this); //注意2:加父节点则不用考虑内存释放问题;
//创建一个学生对象;
this -> Zhang = new Student(this);
//创建连接;
connect(Li, &Teacher::hungry, Zhang, &Student::treat); //注意3:触发连接要有个条件,例如:下课后;
afterClass(); //注意4:先连接,再触发;
}
void Widget::afterClass()
{
//除法饿了的信号;
emit Li -> hungry(); //emit 触发信号;
}
Widget::~Widget()
{
delete ui;
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
有以下几个注意事项:
注意1:若要在 widget 中创建对象,首先就要在头文件中将他们的头文件包含进来,然后在类中加入对象成员;
注意2:加父节点则不用考虑内存释放问题;
注意3:触发连接要有个条件,例如:下课后;
注意4:先连接,再触发;
运行结果:
成功打印 “treat the teacher”;
下面给个信号的带参重载版本;
添加代码:
void hungry(QString foodName); //信号重载;
void treat(QString foodName); //槽函数重载;
void Student::treat(QString foodName) //槽函数实现;
{
qDebug() << "treat the teacher with" << foodName;
}
但此时需要注意,当重载以后,connect 不能识别信号和槽函数地址到底是哪一个,这时候要用函数指针来指向一个函数地址,写法如下:
void(Teacher:: *teacherSignal)(QString) = &Teacher::hungry;
void(Student:: *studentSlot)(QString) = &Student::treat;
此时还需注意加上成员函数的成员域;
最后触发信号:
emit Li -> hungry("beef");
若要去除引号,则将 QString 转成 char*;
首先转成 QByteArray 再转 char*;
qDebug() << "treat the teacher with" << foodName.toUtf8().data();
用按钮来触发条件:
QPushButton *btn = new QPushButton("afertclass", this);
//点击按钮,触发下课;
connect(btn, &QPushButton::clicked, this, &Widget::afterClass);
断开连接:
disconnect(Li, teacherSignal, Zhang, studentSlot);
注:
1. 信号是可以连接信号的;
2. 一个信号可以连接多个槽函数;
3. 多个信号可以连接同一个槽函数;
4. 信号和槽函数的参数必须一一对应;
5. 信号的参数个数可以多于槽函数的参数个数,因为是从信号传递给槽,但相同的类型还是要对应;
透了;
一个下午才学了这么点…