参考文献:《Qt Creator 快速入门》第三版 霍亚飞编著
Qt在标准C++对象模型的基础上添加了一些特性,形成了自己的对象模型,这些特性有:
1、一个强大的无缝对象通信机制----信号和槽(signals and slots)
2、可查询可设计的对象属性系统(object properties)
3、强大的事件和事件过滤器(events and event filters)
4、基于上下文的国际化字符串翻译机制(string translation for internationalization)
5、完善的定时器(timers)驱动,可以在一个事件驱动的gui中处理多个任务
6、分层结构的、可查询的对象树(Object trees),它使用一直很自然的方式来组织对象拥有权(object ownership)
7、守卫指针即QPointer,它在引用对象被销毁是自动将其设置为0;
8、动态的对象转换机制(dynamic cast);
9、支持创建自定义类型(custom type)
信号和槽机制是Qt的核心特征,用于两个对象之间的通信。当一个特殊的事件发生时便可以发送一个信号,比如按钮被单击时发送clicked()信号;而槽就是一个函数,它在信号发射后被调用来响应这个信号。Qt的部件类中已经定义了一些信号和槽,更常用的做法是子类化部件,然后添加自定义的信号和槽来实现想要的功能。
来一个大栗子,栗子功能主界面中创建对话框,对话框可以输入数值,当单机确定按钮时关闭对话框,并将输入的数值通过信号发射处,最后主界面接收到该信号并显示数值。
新建Qt Widgets应用,项目名为mysignalslot,基类选择QWidget。项目建立后往项目中添加新文件,模板选择“Qt设计师界面类”,模板选择Dialog without Buttons,类名设置为MyDialog。完成后使用siganls关键字在mydialog.h中添加自定义信号声明,代码如下
signals:
void dlgReturn(int);//自定义信号
signals前面不能加Public 、private等限定符,默认是Public 函数。可以从任何地方发射,但建议定义信号的类及其子类发射该信号。信号只用声明,不需要也不能对它进行定义实现。 信号没有返回值只能是void 类型。
进入mydialog.ui文件的设计模式,添加spin box 部件和一个push button部件,转到Button的单击信号对应的槽,修改代码如下,其中使用emit关键字发射信号。
void MyDialog::on_pushButton_clicked()
{
int value=ui->spinBox->value();
emit dlgReturn(value);//发射信号
close();//关闭对话框
}
然后在widget.h中添加自定义槽的声明,如下
private slots:
void showValue(int value);
槽要使用slots关键字声明,槽最大的特点是可以和信号关联,其他特性和普通c++函数一样。
进入widget.ui的设计模式,拖入label,文本改为“value:”。然后widget.cpp构造函数添加以下代码,将信号与槽关联。
MyDialog* dlg=new MyDialog(this);
//将对话框中自定义信号与主界面中的自定义槽进行关联
connect(dlg,SIGNAL(dlgReturn(int)),this,SLOT(showValue(int)));
dlg->show();
添加自定义槽的实现如下
void Widget::showValue(int value)//自定义槽
{
ui->label->setText(tr("value:%1").arg(value));
}
运行效果如下
信号和槽应该注意的几点:
1、需要继承自QObject或其子类
2、在类声明的最开始处添加Q_OBJECT宏
3、槽中参数的类型要和信号参数的类型相对应,且不能比信号的参数多;
4、信号只用声明,没有定义,且返回值为void类型。
信号和槽的关联使用QObject类的connect(),connect函数的讲解见我的另一篇博客认识qt 中connect函数。
由字符串on、部件的objectName和信号名称3部分组成,中间用下划线隔开,这种形式命名的槽可以直接和信号关联,不用再使用connect函数。但是要使用调用connectSlotsByName()函数来支持信号和槽的关联(在setupUi函数中调用了该函数),并且必须使用setObjectName指定它们的objectName,才能正常使用自动关联。
代码例子:函数声明
private slots:
void on_myButton_clicked();
在setupUi函数之前,定义部件并指定objectName代码如下
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
QPushButton* button=new QPushButton(this);//创建按钮
button->setObjectName("myButton");//指定按钮的对象名
ui->setupUi(this);
}
实现槽函数
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
QPushButton* button=new QPushButton(this);//创建按钮
button->setObjectName("myButton");//指定按钮的对象名
ui->setupUi(this);
}
自动关联使用不多,还是使用connect函数比较多。
可以使用disconnect函数来断开信号和槽的关联,其原型如下
[static] bool QObject::disconnect(const QObject *sender, const char *signal, \
const QObject *receiver, const char *method)
从帮助文档中可以看到有以下几种使用方法
最后信号和槽机制的特色和优越性:
1、类型安全的,相关联的信号和参数必须匹配
2、松耦合,信号发送者不知道也不需要知道接收者的信息
3、信号和槽可以使用任意类型的任意参数量的参数