1. Qt消息模型
(1)Qt封装了具体操作系统的消息机制
(2)Qt遵循经典的GUI消息驱动事件模型
2. 信号与槽
(1)Qt中定义了与系统消息相关的概念
①信号(Signal):由操作系统产生的消息
②槽(Slot):程序中的消息处理函数
③连接(Connect):将系统消息绑定到消息处理函数(映射规则)
(2)Qt中的消息处理机制
(3)Qt的核心——QObject::connect函数
①函数原型
bool connect(const QObject* sender, //发送对象 const char* signal, //消息名 const QObject* receiver, //接收对象 const char* method, //接收者成员函数(消息处理函数) Qt::ConnectionType type = Qt::AutoConnection);
②在Qt中,消息用字符串进行描述,method是成员函数的名字(也是字符串类型)
③connect函数在消息名和处理函数之间建立映射
(4)Qt中的“新”关键字
①SIGNAL:用于指定消息名
②SLOT:用于指定消息处理函数名
③Q_OBJECT:所有自定义槽的类必须在类声明的开始处加上Q_OBJECT
④slots:用于在类中声明消息处理函数
【编程实验】初探信号和槽
#include <QApplication> #include <QPushButton> int main(int argc, char *argv[]) { QApplication a(argc, argv); QPushButton b; //顶级组件 b.setText("Click me to quit!"); b.show(); //将按钮b的clicked消息映射到a的quit函数 QObject::connect(&b, SIGNAL(clicked()), &a, SLOT(quit())); return a.exec(); }
3. 自定义槽
(1)只有QObject的子类才能自定义槽
(2)定义槽的类必须在声明的最开始处使用Q_OBJECT
(3)类中声明槽时需要使用slots关键字
(4)槽与所处理的信号在函数签名上必须一致(函数签名,即相同参数及类型、返回值)
(5)SIGNAL和SLOT所指定的名称中,可以包含参数类型,但不能包含具体的参数名。
【编程实验】为计算器实例添加消息处理函数
//main.cpp
#include <QApplication> #include "QCalculatorUI.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QCalculatorUI* cal = QCalculatorUI::NewInstance(); int ret = -1; if(cal != NULL) { cal->show(); ret = a.exec(); delete cal; } return ret; }
//QCalculatorUI.h
#ifndef _QCALCULATORUI_H_ #define _QCALCULATORUI_H_ #include <QWidget> #include <QLineEdit> #include <QPushButton> class QCalculatorUI : public QWidget { //要自定义信号和槽,必须在最开始的这里添加Q_OBJECT Q_OBJECT private: QLineEdit* m_edit; QPushButton* m_buttons[20]; //二阶构造法:当new一个QLineEdit和一些按钮时可能会失败,所以采用二阶构造 QCalculatorUI(); //第1阶——先隐藏构造函数 bool construct();//第2阶 private slots: //声明槽时得加slots void onButtonClicked(); public: static QCalculatorUI* NewInstance(); void show(); ~QCalculatorUI(); }; #endif //_QCALCULATORUI_H_
//QCalculatorUI.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);//le的生命期由父组件来管理 if( m_edit != NULL) { m_edit->move(10, 10); m_edit->resize(240, 30); m_edit->setReadOnly(true); //设置编辑框的只读属性 } else { ret = false; return ret; } 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 + j * 50, 50 + i * 50); m_buttons[i * 5 + j]->setText(btnText[i * 5 + j]); //消息映射 //1.消息名(信号)要加SIGNAL关键字,消息处理函数(槽):用SLOT关键字 //2.信号和槽的函数签名必须一致,即都是无参的函数,返回值void 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() { //sender是QObject类的,用于表示消息的发送者 QPushButton* btn = (QPushButton*)sender(); qDebug() << "onButtonClicked()"; qDebug() << btn->text(); } QCalculatorUI::~QCalculatorUI() { }
(6)小贴士:解决经典问题——Object::connect:No such slot…
①检查类是否继承于QObject
②检查类声明的开始处是否添加Q_OBJECT
③检查是否使用slots关键字进行槽声明
④检查槽的名称是否拼写错误
⑤重新执行qmake
4. 小结
(1)信号与槽是Qt中的核心机制
(2)不同的Qt对象可以通过信号和槽进行通信
(3)只有QObject的子类才能自定义信号和槽
(4)使用信号和槽的类必须在声明的最开始处使用Q_OBJECT
(5)信号与处理函数在函数签名上必须一致。