在图形界面编程中(参考《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. 本周六 Apache DolphinScheduler & Doris 将联合线上 Meetup

    活动背景 2020年,大数据成为国家基建的一个重要组成,大数据在越来越多的领域展现威力.随着大数据的应用场景越来越多,大家对数据的响应速度和数据加工工作流的方便程度也提出了更高的要求.在这种背景下,相 ...

  2. HashMap源码实现分析

    HashMap源码实现分析 一.前言 HashMap 顾名思义,就是用hash表的原理实现的Map接口容器对象,那什么又是hash表呢. 我们对数组都很熟悉,数组是一个占用连续内存的数据结构,学过C的 ...

  3. INS(Instagram)如何绑定谷歌二次验证码/谷歌身份验证/双重认证?

    1.打开Ins,找到双重验证界面   打开Ins,点击右上角“三”-“设置”-“安全”-“双重验证”-“选择安全验证方式”-“身份验证应用”-“立即开启”-“手动设置”-“复制密钥”-“输入验证码” ...

  4. python-闭包和装饰器-01-闭包(closure)

    闭包(closure) 闭包就是在一个函数定义的内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包,如: def line(a, b): def cal( ...

  5. js原型、原型链

    之前有说过继承,在js中没有类,所以在new的后面,放的是构造函数,在构造函数中有一个属性prototype,js的继承全靠它. 在js中对象的类型有很多,常见的就是普通对象,和函数对象,在对象中都会 ...

  6. 我在 GitHub 上发现了一款骚气满满的字体!

    本文转自量子位,作者栗体,如有侵权,则可删除. github字体 这个字体叫 Leon Sans,表面看去平平无奇. 但事实上,它并不是普通的字体,体内蕴藏着魔力. github字体1 Leon Sa ...

  7. 三面拿杭州研究院50offer:面对这些问题,你都能做到嘛??

    5G的到来证明了互联网行业发展一如既往的快,作为一名开发人员(Java岗)梦想自然是互联网行业的大厂,这次我的一个学员拿到了杭州的一个研究院offer,透露一下,年薪50多万,保底16,在这里分享一拨 ...

  8. Oracle连接报错之IO异常(The Network Adapter could not establish the connection)

    简单介绍:自己封装oracle jdbc的一些常用功能jar包,自己本机玩没啥问题,给别人玩儿,发现总是抛异常 IO异常(The Network Adapter could not establish ...

  9. vue学习(八) vue中样式 class 定义引用

    //style<style> .red{ color:red; } .thin{//字体粗细 font-weight:200 } .italic{//字体倾斜 font-style:ita ...

  10. MySQL(三)视图

    视图:view,是一种有结构(有行有列)但是没有结果(结构中不真实的存放数据)的虚拟表,虚拟表的结构来源不是自己定义,而是从对应的基表中产生(视图的数据来源): 视图意义: 1.视图可以节省SQL语句 ...