文章目录
[1. 信号槽连接模型](#1. 信号槽连接模型)
[2. 信号槽介绍](#2. 信号槽介绍)
[3. 自定义信号槽](#3. 自定义信号槽)
前言
承接【QT5】<总览一> QT环境搭建、快捷键及编程规范。若存在版权问题,请联系作者删除!
一、QT信号与槽
1. 信号槽连接模型
1.1 信号槽连接模型:
1.2 QT中信号槽的使用方式:
**方式一:**如下图所示,在ui设计器中的中下位置可以设置信号槽的四要素。下图实现了点击按钮就会关闭当前窗口的功能。
**方式二:**如下图所示,右击按钮,再点击"转到槽",QT就会给MyWindow类自动在头文件中声明私有的void on_pushButton_clicked(); 同时也会转到源文件要求程序员实现点击的效果。
2. 信号槽介绍
2.1 概念:
- **信号:在特定情况下被发射的事件。**例如:PushButton最常见的信号就是鼠标单击时发射的click()信号。
- **槽:对信号响应的函数。**槽函数与普通的函数相比,不同在于:槽函数与指定的信号关联,当信号被发射时,该槽函数会被自动执行。
2.2 信号与槽的关联:
- **方式:**使用QObject::connect()函数。由于QT中所有类都是QObject的子类,因此可以省去QObject,其基本格式如下:
cpp//sender 是发射信号的对象的名称 //signal() 是信号名称。信号可以看做是特殊的函数,需要带括号,有参数时还需要指明参数 //receiver 是接收信号的对象名称 //slot() 是槽函数的名称,需要带括号,有参数时还需要指明参数 //SIGNAL和SLOT是Qt的宏,用于指明信号和槽,并将它们的参数转换为相应的字符串 connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
- 代码示例:
cpp/* 1.mywindow.h头文件代码 */ #ifndef MYWINDOW_H #define MYWINDOW_H #include <QMainWindow> QT_BEGIN_NAMESPACE namespace Ui { class MyWindow; } QT_END_NAMESPACE class MyWindow : public QMainWindow { Q_OBJECT public: MyWindow(QWidget *parent = nullptr); ~MyWindow(); private slots: void pushButton_clicked();//自定义槽函数 private: Ui::MyWindow *ui; }; #endif // MYWINDOW_H /************************************************/ /* 2.mywindow.cpp代码 */ #include "mywindow.h" #include "ui_mywindow.h" #include <QDebug> //MyWindow构造函数的具体实现 MyWindow::MyWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MyWindow) { ui->setupUi(this); connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(pushButton_clicked())); } //MyWindow析构函数的具体实现 MyWindow::~MyWindow() { delete ui; } //自定义槽函数的实现 void MyWindow::pushButton_clicked() { static int i; qDebug() << i++ << " "; }
2.3 信号与槽的解除连接:
- disconnect(myObject, 0, 0, 0); 断开一切与 myObject 连接的信号或槽。
- disconnect(myObject, SIGNAL(mySignal()), 0, 0); 断开所有连接到特定信号的东西。
- disconnect(myObject, 0, myReceiver, 0); 与指定的接收者断开连接。
2.4 信号槽连接规则:
- 一个信号可以连接多个槽(触发一个造成多个结果)
- 多个信号可以连接同一个槽(多个触发源造成一个结果)
- 一个信号可以连接另一个信号(连锁反应)
3. 自定义信号槽
3.1 自定义信号的语法:
cpp
//在类中使用signals关键字
//信号函数只声明不定义,并且返回值为void
signals:
void sendMessage();//自定义信号函数
3.2 自定义槽的语法:
cpp
//使用slots自定义槽
//可以使用C++的三种权限符
//声明后必须给出定义
public slots:
void goToClass();//自定义的槽函数
//槽函数定义
void Student::goToClass()
{
qDebug() << "学生上课!" << endl;
}
3.3 案例:
背景:学校发出通知,学生收到后回到班级上课。根据此情景,创建School和Student类,并且在School类中自定义信号"发出通知",在Student类中自定义槽"去班级"。为了简单演示,我们在MainWindow类中添加School和Studen类对象作为成员变量,在MainWindow的构造函数中创建对象,并构建"学校->通知->学生->上课"的信号槽连接,同时emit发射该信号,来观察实验现象。
①School.h:
cpp#ifndef SCHOOL_H #define SCHOOL_H #include <QObject> class School : public QObject { Q_OBJECT public: explicit School(QObject *parent = nullptr); signals://自定义信号 void sendMessage();//学校通知 }; #endif // SCHOOL_H
②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 goToClass(); }; #endif // STUDENT_H
③Student.cpp:
cpp#include "student.h" #include <QDebug> Student::Student(QObject *parent) : QObject(parent) { } void Student::goToClass() { qDebug() << "学生上课!" << endl; }
④MainWindow.h:
cpp#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "school.h" #include "student.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; School *school; Student *student; }; #endif // MAINWINDOW_H
⑤MainWindow.cpp:
cpp#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); school = new School(this); student = new Student(this); connect(school, SIGNAL(sendMessage()), student, SLOT(goToClass()));//信号与槽的连接 emit school->sendMessage();//发射信号 } MainWindow::~MainWindow() { delete ui; delete school; delete student; }
二、QT的对象树
1. 为什么需要设置父对象?
**答:**因为需要将某些控件附着于父对象,以便能跟着父对象一起移动,方便控件的管理。
2. 如何设置父对象?
**答:**有构造函数传参和调用setparent()两种方式,如下所示:
cpp
//方式一:构造函数传入父对象地址
pushButton = new QPushButton(this);
//方式二:调用setParent函数
pushButton->setParent(this);
3. QT的对象树机制
**答:**父对象在调用析构函数前,会将附着于其上面所有的对象析构,再调用自己的析构函数。因此,可以不用显式地delete某些附着的对象,QT的对象树机制会自动将其销毁。
三、添加资源文件
有时需要外部的图片等资源,因此需要将资源文件添加进QT。
**步骤一:**将资源文件放置于当前工程目录下。
**步骤二:**添加"QT Resource File":
**步骤三:**添加资源路径的目录:
**步骤四:**添加外部图片:
四、样式表的使用
上一章已经加载了外部图片资源文件,本章改变样式表(stylesheet)将该图片呈现于窗口中。若不知道如何编写样式表的代码,可以去"帮助"中查找"stylesheet"查看"Qt Style Sheets Reference "。
**步骤一:**在ui设计器中将QLable控件拖拽至窗口中:
**步骤二:**右击该控件,选中"改变样式表",添加图片:
五、QSS文件的使用
在上一章中,我们利用ui设计器来改变样式表从而加载外部图片。相应地,我们可以通过写代码的方式来改变样式表从而改变某些控件的外观。但是,当样式表书写过多时会影响我们代码的可读性。因此,可以通过qss文件来汇总这些样式表,而不需要在主程序中书写这些样式表。
**原始效果:**在Widget类的构造函数中写这些样式表。
**我们将上述的样式表代码转移到qss文件中,减少主程序代码的复杂度。**若不知道怎么写qss样式表,可以参考 Qt QSS样式表总结
**步骤一:**新建qss文件:
**步骤二:**编辑qss文件:
**步骤三:**main()中加载qss文件:
运行结果:
cpp
/* 加载QSS文件 */
QFile file(":/style.qss");
if (file.exists()){
file.open(QFile::ReadOnly);//只读方式打开
QString styleSheet = QLatin1String(file.readAll());//字符串方式保存结果
qApp->setStyleSheet(styleSheet);//设置全局样式
file.close();//关闭文件
}
六、常用函数与宏
常用宏如下:
**【1】Q_OBJECT:**声明在private中,用到了信号槽就要添加。
**【2】Q_UNUSED(变量名):**告诉编译器不使用某个变量。
**【3】Q_CLASSINFO(key, value):**给出类的相关信息。例如:Q_CLASSINFO("Author", "swear")
cpp
//设置类的信息
Q_CLASSINFO("Author", "swear")
Q_CLASSINFO("version", "1.0")
//循环读取类的信息,需要添加相关的头文件
const QMetaObject *meta = boy->metaObject();
for (int i = 0; i < meta->classInfoCount(); ++i){
QMetaClassInfo classInfo = meta->classInfo(i);
qDebug() << classInfo.name() << classInfo.value();
}
【4】Q_PROPERTY(...): 属性宏,用于给用户设置、获取属性。若设置属性不存在时,会设置动态属性。设置属性: "对象指针->setProperty(识别名, 值)"。获取属性:"对象指针->property(识别名).toXxx"。
cpp
//假设我们有个QPerson类,类内有属性m_name, m_age, m_score
//通过setProperty设置name时,就会去执行setName
Q_PROPERTY(QString name READ getName WRITE setName)
//以下将"age"与属性m_age绑定,之后的读取都是针对m_age
Q_PROPERTY(unsigned int age MEMBER m_age)
Q_PROPERTY(unsigned int score MEMBER m_score)
//创建对象,读写属性
QPerson p;
p.setProperty("name", "王甩笼");//就会去调用setName方法
p.setProperty("age", 10);//将p内部属性m_age设置为10
qDebug() << p.property("name").toString();//打印"王甩笼"
常用函数如下:
**【1】QWidget类中的update():**更新界面。
**【2】QWidget类中的show():**显示界面。若某个界面类在实例化时没有指定父对象,则需要调用该函数来显示界面。
**【3】sender():**在槽函数中获取发出信号的对象,用指针变量接收。例如QTcpSocket *tmpTcpSocket = (QTcpSocket*)sender();