1 Qt中的消息处理简单介绍
1.1Qt消息模型
Qt消息模型:
- Qt封装了具体操作系统的消息机制。
- Qt遵循经典的GUI消息驱动事件模型。
思考如下几个问题:
- Qt中如何表示用户消息?
- Qt中如何映射用户消息到消息处理函数?
- Qt中消息映射需要遵循什么规则?
1.2 信号与槽
Qt中定义了与系统消息相关的概念:
- 信号(Signal):
- 由操作系统产生的消息。
- 槽(Slot):
- 程序中的消息处理函数。
- 连接(Connect):
- 将系统消息绑定到消息处理函数。
Qt中的消息处理机制:
Qt的核心–QObject::connect函数:
Qt中的“新”关键字:
- SIGNAL:
- 用于指定消息名。
- SLOT:
- 用于指定消息处理函数名。
- Q_OBJECT:
- 所有自定义槽的类必须在类声明的开始处加上Q_OBJECT。
- slots:
- 用于在类中声明消息处理函数。
自定义槽:
- 只有QObject的子类才能自定义槽。
- 定义槽的类必须在声明的最开始使用Q_OBJECT。
- 类中声明槽时需要使用slots关键字。
- 槽与所处理的信号在函数签名上必须一致。
- SIGNAL和SLOT所指定的名称中:
- 可以包含参数类型。
- 不能包含具体的参数名。
解决经典问题:Object::connect:No such slot …
- 检查类是否继承于QObject。
- 检查类声明的开始是否添加Q_OBJECT。
- 检查是否使用slots关键字进行槽声明。
- 检查槽的名称是否拼写错误。
- 重新执行qmake。
编程实验:信号与槽初步使用
#include <QtGui/QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPushButton b;
b.setText("Click me to quit!");
b.show();
QObject::connect(&b, SIGNAL(clicked()), &a, SLOT(quit()));
return a.exec();
}
编程实例:自定义槽
QCalculator.h:
#ifndef _QCALCULATORUI_H_
#define _QCALCULATORUI_H_
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
class QCalculatorUI : public QWidget
{
Q_OBJECT
private:
QLineEdit* m_edit;
QPushButton* m_buttons[20];
QCalculatorUI();
bool construct();
private slots:
void onButtonClicked();
public:
static QCalculatorUI* NewInstance();
void show();
~QCalculatorUI();
};
#endif
QCalculator.cpp:
#include "QCalculatorUI.h"
#include <QDebug>
QCalculatorUI::QCalculatorUI() : QWidget(NULL, Qt::WindowCloseButtonHint)
{
}
bool QCalculatorUI::construct()
{
bool ret = true;
const char* btnText[20] =
{
"7", "8", "9", "+", "(",
"4", "5", "6", "-", ")",
"1", "2", "3", "*", "<-",
"0", ".", "=", "/", "C",
};
m_edit = new QLineEdit(this);
if( m_edit != NULL )
{
m_edit->move(10, 10);
m_edit->resize(240, 30);
m_edit->setReadOnly(true);
}
else
{
ret = false;
}
for(int i=0; (i<4) && ret; i++)
{
for(int j=0; (j<5) && ret; j++)
{
m_buttons[i*5 + j] = new QPushButton(this);
if( m_buttons[i*5 + j] != NULL )
{
m_buttons[i*5 + j]->resize(40, 40);
m_buttons[i*5 + j]->move(10 + (10 + 40)*j, 50 + (10 + 40)*i);
m_buttons[i*5 + j]->setText(btnText[i*5 + j]);
connect(m_buttons[i*5 + j], SIGNAL(clicked()), this, SLOT(onButtonClicked()));
}
else
{
ret = false;
}
}
}
return ret;
}
QCalculatorUI* QCalculatorUI::NewInstance()
{
QCalculatorUI* ret = new QCalculatorUI();
if( (ret == NULL) || !ret->construct() )
{
delete ret;
ret = NULL;
}
return ret;
}
void QCalculatorUI::show()
{
QWidget::show();
setFixedSize(width(), height());
}
void QCalculatorUI::onButtonClicked()
{
QPushButton* btn = (QPushButton*)sender();
qDebug() << "onButtonClicked()";
qDebug() << btn->text();
}
QCalculatorUI::~QCalculatorUI()
{
}
2 深入理解信号与槽
一个事实:
- 在实际的项目开发中,大多数时候是直接将组件中预定义的信号连接到槽函数;信号发射的时候槽函数被调用。
深度的思考:
- 信号是怎么来的?又是如何发射的?
Qt中信号(SIGNAL)的本质:
-
信号只是一个特殊的成员函数声明:
- 函数的返回值是void类型。
- 函数只能声明不能定义。
-
信号必须使用signals关键字进行声明:
- 函数的访问属性自动被设置为protected。
- 只能通过emit关键字调用函数(发射信号)。
信号定义示例:
信号与槽的对应关系:
信号与槽的关系:
- 一个信号可以连接到多个槽(一对多)。
- 多个信号可以连接到一个槽(多对一)。
- 一个信号可以连接到另一个信号(转嫁)。
- 连接可以被disconnect函数删除(移除)。
对于信号与槽我们必须注意如下:
- Qt类只能在头文件中声明。
- 信号与槽的原型应该完全相同。
- 信号参数多于槽函数时,多余的参数被忽略。
- 槽函数的返回值必须是void类型。
- 槽函数可以像普通成员函数一样被调用。
- 信号与槽的访问属性对于connect/disconnect无效。
信号与槽的意义:
- 最大限度的弱化了类之间的耦合关系。
- 在设计阶段,可以减少不必要的接口类(抽象类)。
- 在开发阶段,对象间的交互通过信号与槽动态绑定。
示例代码:
RxClass.h:
#ifndef RXCLASS_H
#define RXCLASS_H
#include <QObject>
#include <QDebug>
class RxClass : public QObject
{
Q_OBJECT
public:
protected slots:
void mySlot(int v)
{
qDebug() << "void mySlot(int v)";
qDebug() << "Sender: " << sender()->objectName();
qDebug() << "Receiver: " << this->objectName();
qDebug() << "Value: " << v;
qDebug() << endl;
}
};
#endif // RXCLASS_H
TestSignal.h:
#ifndef TESTSIGNAL_H
#define TESTSIGNAL_H
#include <QObject>
class TestSignal : public QObject
{
Q_OBJECT
public:
void send(int i)
{
emit testSignal(i);
}
signals:
void testSignal(int v);
};
#endif // TESTSIGNAL_H
main.cpp:
#include <QtCore/QCoreApplication>
#include <QDebug>
#include "TestSignal.h"
#include "RxClass.h"
void emit_signal()
{
qDebug() << "emit_signal()" << endl;
TestSignal t;
RxClass r;
t.setObjectName("t");
r.setObjectName("r");
QObject::connect(&t, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
for(int i=0; i<3; i++)
{
t.send(i);
}
}
void one_to_multi()
{
qDebug() << "one_to_multi()" << endl;
TestSignal t;
RxClass r1;
RxClass r2;
t.setObjectName("t");
r1.setObjectName("r1");
r2.setObjectName("r2");
QObject::connect(&t, SIGNAL(testSignal(int)), &r1, SLOT(mySlot(int)));
QObject::connect(&t, SIGNAL(testSignal(int)), &r2, SLOT(mySlot(int)));
t.send(100);
}
void multi_to_one()
{
qDebug() << "multi_to_one()" << endl;
TestSignal t1;
TestSignal t2;
RxClass r;
t1.setObjectName("t1");
t2.setObjectName("t2");
r.setObjectName("r");
QObject::connect(&t1, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
QObject::connect(&t2, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
t1.send(101);
t2.send(102);
}
void signal_to_signal()
{
qDebug() << "signal_to_signal()" << endl;
TestSignal t1;
TestSignal t2;
RxClass r;
t1.setObjectName("t1");
t2.setObjectName("t2");
r.setObjectName("r");
QObject::connect(&t1, SIGNAL(testSignal(int)), &t2, SIGNAL(testSignal(int)));
QObject::connect(&t2, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
t1.send(101);
t2.send(102);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// emit_signal();
// one_to_multi();
// multi_to_one();
// signal_to_signal();
return a.exec();
}
参考资料: