Qt中使用信号与槽的机制完成对象之间的协同操作,当特定事件发生时,一个信号被发射,槽就是一个对应特定信号的被回调的函数。
首先要理解信号的本质?槽的本质?
信号的本质是成员函数指针类型的特殊成员变量,也就是说是类里的变量,是一个指向成员函数的指针。而槽就是对应该指针的被回调的函数?
(一)信号与槽的关联
1、connect关联
槽的声明:public slots:(public表示槽可以在类外被调用)
void showChildDialog();
给出槽的实现:void MyWidget::showChildDialog()
{
QDialog *dialog = new QDialog(this);
dialog->show();
}
(显示一个Dialog)
在构造函数中实现信号与槽的关联:MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
connect(ui->showChildButton, &QPushButton::clicked,
this, &MyWidget::showChildDialog);
}
将单击信号clicked()与新建的槽关联,四个参数分别是发射信号的对象,发射的信号,接收信号的对象,要执行的槽。
2、自动关联
将关联函数整合到槽的命名中,命名由字符on,发射信号的部件对象名和信号名组成。
void MyWidget::on_showChildButton_clicked()
{
QDialog *dialog = new QDialog(this);
dialog->show();
}
注意:信号和槽函数的声明一般位于头文件中,同时在类声明的开始位置必须加上Q_OBJECT语句,Q_OBJECT语句将告诉编译器在编译之前必须先应用moc工具进行扩展。关键字signals是对信号的声明,siganls没有public、private、protected等属性,slots是对槽函数的声明,slots有public、private、protected等属性。signals、slots关键字是QT 自己定义的,不是C++中的关键字。
(二)断开关联
当信号与槽没有必要继续保持关联时,可以使用disconnect函数来断开连接。
bool QObject::disconnect ( const QObject * sender, const char * signal, const QObject * receiver, const char *method )
注意:在disconnect函数中0可以用作一个通配符,分别表示任何信号、任何接收对象、接收对象中的任何槽函数。但是发射者sender不能为0,其它三个参数的值可以等于0。
(三)优点
1、类型安全。需要关联的信号和槽的签名必须是等同。
2、松散耦合。信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无须知道是哪个对象的哪个槽需要接收它发出的信号,它只需要做的是在适当的时间发送适当的信号就可以了,而不需要知道也不关心它的信号有没有被接收到,更不需要知道哪个对象的哪个槽接收到了信号。
(四)使用时的一些注意事项
1、信号与槽机制与普通函数的调用一样,如果使用不当的话,在程序执行时也有可能产生死循环。因此,在定义槽函数时一定要避免在槽函数中再次发射所接收到的同样信号。
2、函数指针不能作为信号或槽的参数。函数指针作为参数是不合语法,但可以使用typedef将函数指针类型重命名,使用函数指针类型作为参数是合语法的。
3、如果一个信号与多个槽相关联,那么当这个信号被发射时,与之相关的槽函数被调用的顺序将是随机的。
(五)使用
在Qt5-2源码中用信号与槽机制设计了自定义菜单。
它的实现思路是这样的:在行编辑器中输入文本,然后按下回车键,这时行编辑就会发射returnPressed()信号,而这时就调用了我们的sendText()槽,在sendText()槽中又发射了getText()信号,信号中包含了行编辑器中的文本,接着又会调用setText()槽,在setText()槽中将getText( )信号发来的文本输人到文本编辑器中。这样就完成了按下回车键将行编辑器中的文本输人到中心部件的文本编辑器中的操作。
1、信号与槽的声明:
class MyAction : public QWidgetAction
{
Q_OBJECT
public:
explicit MyAction(QObject *parent = 0);
protected:
// 声明函数,该函数是QWidgetAction类中的虚函数
QWidget* createWidget(QWidget *parent);
signals:
// 新建信号,用于在按下回车键时,将行编辑器中的内容发射出去
void getText(const QString &string);
private slots:
// 新建槽,它用来与行编辑器的按下回车键信号关联
void sendText();
private:
// 定义行编辑器对象的指针
QLineEdit *lineEdit;
};
2、之后再Myaction类里将回车键信号与发送文本槽关联。
lineEdit = new QLineEdit;
connect(lineEdit, &QLineEdit::returnPressed, this, &MyAction::sendText);
4、添加createWidget()定义,先用inherits()判断父部件是否为菜单或工具栏,如果是创建子部件并返回。在这里需要使用QSplitter分裂器,在构造函数中转递主窗口的参数。
if(parent->inherits("QMenu") || parent->inherits("QToolBar")){
QSplitter *splitter = new QSplitter(parent);
QLabel *label = new QLabel;
label->setText(tr("插入文本:"));
splitter->addWidget(label);
splitter->addWidget(lineEdit);
return splitter;
5、定义sendText函数
void MyAction::sendText()
{
emit getText(lineEdit->text()); // 发射信号,将行编辑器中的内容发射出去
lineEdit->clear(); // 清空行编辑器中的内容
}
注意当输入文本并按下回车键是,就会激发returnPressed()信号,这是调用sendText()槽。
6、声明setText槽并定义,实现向编辑器添加文本。
信号与槽的使用大概就是这样,总的来说信号与槽的机制的原理就是Qt的元对象系统,通过moc编译隐藏了具体的实现细节,这些内容可以在moc_xxx.cpp中查看。