前言
凡是桌面应用程序,都会有消息处理,QT也不例外。
QT的信号是广播传递的,当一个按钮检测到被点击时,就发送一个信号(signal)。如果有对象对这个信号感兴趣,那么它必须使用连接(connect)函数,来把这个信号与自己的函数绑定在一起。当信号发生时,绑定的函数会自动被调用。为了方便,我们把绑定的函数称为为槽函数。
为了更好的理解QT中的信号和槽,我们先看下QT的 moc (Meta Object Compiler)
moc构成了QT强大的元对象系统
- 元对象系统依赖于QObject,QObject是QT中所有类的基类
- Q_OBJECT 宏 可以启用元对象的特性,比如:动态属性、信号、槽
- 元对象编译器(moc)会在QObject子类中添加必要的代码来实现元对象特性
moc工具读取C++源文件,如果它找到一个或多个包含Q_OBJECT宏的类声明,那么它会生成另一个C++源文件。moc就相当于预处理一样,java 的 jsp 也是相同的原理。
元对象系统主要是为了提供对象间通信的信号槽机制,但是还提供了以下机制:
- QObject::metaObject() 返回类的关联元对象
- QMetaObject::className() 在运行时以字符串形式返回类名,不需要通过C++编译器支持RTTI
- QObject::inherits() 返回对象是否为继承QObject继承树中指定类的示例
- QObject::tr() 和QObject::trUtf8() 为国际化翻译字符串
- QObject::setProperty() 和 QObject::property() 按名称动态设置和获取属性
- QMetaObject::newInstance()构造类的新实例
QT提供了一个方法用于动态转换:qobject_cast(obj)
这里官方建议继承于QObject都要使用Q_OBJECT宏
QMetaObject 相当于java 中的Class 类
我们写一个小例子进一步说明 ( 控制台程序 ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #ifndef A_H #define A_H
#include <QObject>
class A:public QObject { Q_OBJECT public: A(); void toString(); };
#endif
|
1 2 3 4 5 6 7 8 9 10
| #include "a.h" #include <QDebug> A::A() {
} void A::toString() { qDebug() << "A class"; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| #include <QCoreApplication> #include "a.h" #include <QString> #include <QDebug> #include <qobject.h>
int main(int argc, char *argv[]) { A a; const QMetaObject* aMeta = a.metaObject(); QString AclassName = aMeta->className();
qDebug()<< AclassName ;
QObject* aNew = aMeta->newInstance(); A* aA = qobject_cast<A*>(aNew); aA->toString(); delete aA;
a.setProperty("name","张三"); QVariant name = a.property("name"); qDebug()<< name; return 0; }
|
元对象系统比较复杂,先简单介绍(主要我没学到)
信号
信号本身就一个函数,返回类型为void的函数
我们可以在C++头文件写上一个函数的声明,moc 会自动帮我们处理,把这个函数当做信号。
信号可以连接任意数量的槽,也可以连接到信号
槽
槽是一个普通的C++成员函数,也可以是虚函数。
信号与槽的关联
信号与槽关联是用QObject::connect函数实现的,其基本格式是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
|
调用示例:
1 2 3
| myButton = new QPushButton(this); connect(myButton, SIGNAL(clicked()), this, SIGNAL(buttonClicked()));
|
在指定信号和方法时必须使用SIGNAL()
和SLOT()
宏
1 2 3 4
| # define SLOT(a) qFlagLocation("1"#a QLOCATION) # define SIGNAL(a) qFlagLocation("2"#a QLOCATION)
Q_CORE_EXPORT const char *qFlagLocation(const char *method);
|
可以看出这两个宏都是根据字符串来找到对应的方法。
QT 5.2 新增了一个静态方法:
1
| QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
|
我们可以这样调用:
1
| connect(this,&Widget::mySignal,this,&Widget::mySlot);
|
连接如果不用的话,记得断开连接。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #ifndef MAINWINDOW_H #define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE
class MainWindow : public QMainWindow { Q_OBJECT public slots: void setValue(int value); signals: void valueChanged(int newValue); public: void sendSignal(int value); public: MainWindow(QWidget *parent = nullptr); ~MainWindow();
private: Ui::MainWindow *ui; }; #endif
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); connect(this,&MainWindow::valueChanged,this,&MainWindow::setValue); }
MainWindow::~MainWindow() { delete ui; disconnect(this,&MainWindow::valueChanged,this,&MainWindow::setValue); } void MainWindow::setValue(int value) { emit valueChanged(value); } void MainWindow::sendSignal(int value) { qDebug()<< value; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); w.sendSignal(10); w.sendSignal(20); return a.exec(); }
|