在图形界面编程中(参考《C++最好的图形库是什么?》),组件之间如何实现通信是核心的技术内容。Qt 使用了信号与槽的机制,非常的高效、简单、易学,方便开发者的使用。本文详细的介绍了Qt 当中信号与槽的概念,并演示了各种信号与槽的连接方式。

一、什么是信号和槽(Signal and Slot)

信号和槽是用于对象之间的通信,它是Qt的核心机制,在Qt编程中有着广泛的应用。如果想学好Qt,一定要充分掌握信号的槽的概念与使用。关于Qt的介绍可以

举个例子,在一个十字路口,信号灯变成了绿色,对面的汽车看到后就启动了。信号灯就是发送信号的对象,绿灯亮是它发送的信号 (signal),汽车是接收对象,汽车行驶是汽车对信号的响应,也叫槽 (slot)。

再举一个例子,比如在一个主窗口内有一个关闭按钮,如果点击这个按钮窗口就会关闭,那么关闭按钮是发送信号的对象,它发送的信号是点击,接收信号的对象是窗口,响应信号的槽是关闭窗口。

二、信号和槽的代码实例

在Qt中,发送对象、发送的信号、接收对象、槽可以通过很多种方式连接。我们下面通过一些例子逐一做演示。

(1)Qt 4 使用宏

在Qt 4的版本中,主要通过connect + 宏的方式进行通信连接。

connect(发送对象,信号,接收对象,槽函数),其中发送信号和槽函数需要用 SIGNAL() 和 SLOT() 来进行声明。

connect 函数声明如下:

[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)

比如点击按钮关闭窗口的例子,代码可以这样写:

connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(close()));

如果想自定义槽函数,需要先将槽函数的声明添加到类的slots中。比如我们对一个QLineEdit控件添加一个接收textEdited信号的槽函数onTextEdited

class MainWindow : public QMainWindow
{
Q_OBJECT public:
MainWindow(QWidget *parent = nullptr);
~MainWindow(); private slots:
void onTextEdited(QString); private:
Ui::MainWindow *ui;
};

然后实现函数,并用connect与信号连接

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(close()));
connect(ui->lineEdit, SIGNAL(textEdited(QString)), this, SLOT(onTextEdited(QString)));
} void MainWindow::onTextEdited(QString s)
{
qDebug() << s;
}

这样写的好处是信号和槽参数很直观,但缺点是因为使用宏,编译时不做类型检查,如果有问题的话,在运行的时候才会发现。

(2)使用Qt Creator 界面添加信号的槽函数

另外一种方式不需要使用 connect 函数,可以通过Qt Creator 界面来完成发送信号和槽函数的连接,比如我们右键点击一个按钮,然后选择“转到槽”:

选择信号,我们点击QAbstractButton的clicked()信号,表示按钮被点击:

接下来,Qt Creator会自动为我们生成如下代码,首先是槽函数的声明:

class MainWindow : public QMainWindow
{
Q_OBJECT public:
MainWindow(QWidget *parent = nullptr);
~MainWindow(); private slots:
void on_pushButton_clicked(); private:
Ui::MainWindow *ui;
};

然后是槽函数的实现:

void MainWindow::on_pushButton_clicked()
{ }

使用这种方法我们不需要使用connect函数将信号与槽函数做连接。 这里槽函数的命名有一定的规则,一般是 on_objectname_signal 这样来命名的。这种方法优点是减少了自己手动敲代码的工作量,缺点是究竟有哪些信号与槽函数做了连接不易被发现,没有connect 函数看起来直观。

(3)使用Qt 5 新 connect 函数

Qt 5 推出了新的 connect 函数,不需要使用 SIGNAL() 和 SLOT() 宏,可以在编译时做类型检查:

connect函数的声明如下:

[static] QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)

同样用代码实现点击按钮关闭窗口,并且添加一个QLineEdit控件,发送textEdited信号,由onTextChanged()函数作为槽函数响应。

使用这种方法槽函数的声明不需要放到slots中,只要像普通的函数一样声明就可以了,类型需要与textEdit信号保持一致

class MainWindow : public QMainWindow
{
Q_OBJECT public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void textChanged(QString); private:
Ui::MainWindow *ui;
};

使用 connect 将信号与槽函数连接,不需要再使用 SIGNAL() 和 SLOT() 宏

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::close);
connect(ui->lineEdit, &QLineEdit::textEdited, this, &MainWindow::textChanged);
} void MainWindow::textChanged(QString s)
{
qDebug() << s;
}

(4)使用函数指针

在Qt 5版本的connect 函数里,信号与槽函数的参数其实都是函数指针,当信号或槽函数有重载时,使用函数指针可以明确告诉编译器使用哪一个重载函数,避免歧义。下面的例子虽然没有使用重载,不过我们改成通过使用函数指针来向connect传递槽函数参数。

首先还是声明两个槽函数,分别响应点击按钮信号,和textEdited信号:

class MainWindow : public QMainWindow
{
Q_OBJECT public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void onButtonPushed();
void onTextEdited(QString); private:
Ui::MainWindow *ui;
};

然后对函数做简单的实现:

void MainWindow::onButtonPushed()
{
this->close();
} void MainWindow::onTextEdited(QString s)
{
qDebug() << s;
}

最后声明函数指针,并且将它们与信号连接

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
void(MainWindow:: *buttonClickSlot)() = &MainWindow::onButtonPushed;
void(MainWindow:: *textEditedSlot)(QString) = &MainWindow::onTextEdited;
connect(ui->pushButton, &QPushButton::clicked, this, buttonClickSlot);
connect(ui->lineEdit, &QLineEdit::textEdited, this, textEditedSlot);
}

(5)使用Lambda表达式

使用 Lambda表达式的好处是代码的书写更加方便快捷。在connect 函数中,槽函数参数我们可以改用Lambda表达式的方式来进行传参。

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton, &QPushButton::clicked, this, [=](){
this->close();
});
connect(ui->lineEdit, &QLineEdit::textEdited, this, [=](QString s){
qDebug() << s;
});
}

使用Lambda表达式,我们就不需要在类中对槽函数做任何的声明了。Lambda表达式是C++ 11的内容,在比较低的 Qt版本中,要注意在Pro项目文件中加入 CONFIG += C++ 11。

三、总结

Qt 当中组件之间通过信号与槽的方式进行通信非常地高效,对于开发者来说也很简单。使用 Qt 5版本的开发者建议使用上面后三种新的方式进行连接。补充一点,信号和槽之间不是一一对应的关系。一个信号可以对应多个槽,比如点击一个按钮可以触发多个不同的响应;一个槽也可以响应多个不同的信号,比如点击按钮可以关闭窗口,点击左上角的小叉也可以关闭窗口。信号和槽之间只要通过connect 函数连接就建立了耦合关系,如果想解除连接可以使用disconnect 函数。

推荐阅读:

C++最好的图形库是什么?

Linux快速搭建C/C++开发环境

一篇文章快速搞懂什么是GitHub

获取知识干货、增加面试经验、了解职场人生

欢迎关注微信公众号

Qt信号与槽使用方法最完整总结的更多相关文章

  1. QT 信号与槽connect

    QT 信号与槽connect QT 信号与槽connect connect函数调用几个限制 connect函数代码 QT中信号与槽的连接使用的connect函数是一个静态函数,在类QObject中定义 ...

  2. Qt 信号与槽

    Qt信号与槽的理解 信号和槽机制是 QT 的核心机制,要精通 QT 编程就必须对信号和槽有所了解.信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性,也是 QT 区别于其它工具包的重 ...

  3. QT信号和槽

    QT信号和槽 ============ 信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性.要正确的处理信号和槽,必须借助一个称为 moc(Meta Object Compiler) ...

  4. Qt信号与槽传递自定义数据类型——两种解决方法

    信号与槽作为qt中的核心机制,在qt应用开发中经常会用的,但是原生的信号与槽连接传参,只支持基本的数据类型,比如char,int, float,double. 如果想要在信号与槽之间传递自定义参数,比 ...

  5. Qt信号与槽自动关联机制

    参考链接1:http://blog.csdn.net/skyhawk452/article/details/6121407 参考链接2:http://blog.csdn.net/memory_exce ...

  6. 关于Qt信号与槽机制的传递方向性研究(结论其实是错误的,但是可以看看分析过程)

    最近由于项目的需求,一直在研究Qt.信号与槽机制是Qt的一大特色,该机制允许两者间传递参数,依次来实现对象间的通信.这个参数会分别存在于信号的参数列表和槽函数的参数列表中.需要注意的是,若将槽函数绑定 ...

  7. QT信号和槽在哪个线程执行问题

    时隔四个月后的第一篇,换了个公司可以登录的博客,记录一些学习内容吧 这是看到别人写的比较好的一篇,排版有点乱 QThread的使用方法 起源 昨天不小心看到Qt开发人员( Bradley T.Hugh ...

  8. Qt信号与槽机制

    一.信号和槽机制 信号和槽用于两个对象之间的通信,信号和槽机制是Qt的核心特征,也是Qt不同于其他开发框架的最突出的特征.在GUI编程中,当改变了一个部件时,总希望其他部件也能了解到该变化.更一般来说 ...

  9. [QT][转载] Qt信号和槽

    From: http://blog.csdn.net/rl529014/article/details/51346955 GUI 程序除了要绘制控件,还要响应系统和用户事件,例如重绘.绘制完成.点击鼠 ...

随机推荐

  1. 3.OSPF协议及链路状态算法

    OSPF的特点: 1.使用洪泛法向自治系统内所有路由器发送信息,即路由器通过输出端口向所有相邻的路由器发送信息,而每一个相邻路由器又再次将此信息发往其所有的相邻路由器.最终整个区域内所有路由器都得到了 ...

  2. SQL 给某字段添加汉字却显示??

    错误展示: 解决方案: 1.在要修改的数据库上单击鼠标右键,并选择“属性”.   2.在弹出的数据库属性窗口中点击“选择页”中的“选项”.   3.将排序规则由默认的SQL_Latin1_Genera ...

  3. 高效C++:让自己习惯C++

    视C++为一个联邦语言 面向过程,面向对象,泛型编程,元编程,C++同时支持,强大而迷惑 C++语言可以分为如下4个部分: C,C语言相同 C with Class,包括封装.继承.多态... Tem ...

  4. Terminal终端控制台常用操作命令

    新建文件夹和文件 cd .. 返回上一级 md test 新建test文件夹 md d:\test\my d盘下新建文件夹 cd test 进入test文件夹 cd.>cc.txt 新建cc.t ...

  5. BUUCTF-Web Easy Calc

    要素察觉 打开calc.php发现源码 过滤了很多字符.题目一开始提示了有waf,最后通过eval实现计算功能.考虑利用该函数读取flag文件,先尝试弹个phpinfo 被waf拦截,在num参数前面 ...

  6. layui常用插件(二) 时间插件

    日期和时间 html <div class="layui-inline"> <!-- 注意:这一层元素并不是必须的 --> <input type=& ...

  7. Python之数据结构:列表、元组、字典、set

    列表 列表里可以存储任意的数据类型.可修改的结构,用[ ]括起来表示或用函数list()构建. eg: y = [1,1.5,'hello',True] 列表还可以嵌套列表 eg: y = [1,1. ...

  8. android studio 部分问题及解决方案

    1 启动多个虚拟机后开启指定端口的虚拟机     https://blog.csdn.net/chuyouyinghe/article/details/72958004       adb devic ...

  9. A - A Simple Problem with Integers (线段树的区间修改与区间查询)

    You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of op ...

  10. mybatis之if判断

    今天使用mybatis开发公司中台项目踩的一个坑,分享并记录一下 踩坑前因:因项目中比较多状态字段,用了大量的Integer 0和1进行判断 在功能做完后只是粗略的点了下觉得没多大问题(来自程序员强大 ...