Qt——信号槽

信号和槽(slot)

类似于OS中的信号回调机制,一旦发射了信号就会触发(执行)槽函数**。在Qt中,信号看起来是函数的样子(之所以说看起来像,是因为实际上Qt暴露给我们的信号或者我们在Qt上自己创建的信号,都会被Qt的MOC处理成另外一种样子),而槽本质上就是一个函数。**

大部分信号都是Qt提供给我们的,但是我们也可以自定义信号。Qt也提供一些槽,但我们经常自己定义槽。对于Qt给我我们的,我们要多去查阅文档,从子类到父类向上寻找

在Qt中,信号和槽的关系是多对多关系,即一个槽可以被多个信号关联,一个信号可以关联多个槽。作为Qt的使用者,我们主要做的就是将信号源,信号,槽,接受对象关联起来,目的是使得用户操作后得到反馈。当信号源发射后信号后就会查找信号和槽的关联关系,执行与之关联的所有槽。


关联信号和槽

关联信号和槽有多种方式,我们下面逐一介绍

Connect函数方式

connect是QObject类(也就是Qt中所有类的祖宗)中的一个静态方法,因此所有类中都可以直接调用它。它常使用(connect有多个重载)的重载形式如下:

cpp 复制代码
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
                                     const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
                                     Qt::ConnectionType type = Qt::AutoConnection)
  • sender:发送信号的控件对象的地址,即设置信号源。
  • signal:要关联的信号,以函数地址的方式传入。
  • receiver:要对信号做出反应的控件对象,以对象指针的方式传入。
  • slot:槽函数,以函数指针的方式传入。
  • type:基本不会用到,暂时不做解释

接下来我们来使用connect:

cpp 复制代码
/*只要点击按钮,按钮上就会出现"哈哈"*/

void Widget::handler()
{
    button->setText("哈哈");
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


    button = new QPushButton(this);//创建按钮对象,button是widget的成员属性
    button->move(100,100);//将button安置到某位置

    //建立关联关系,&QPushButton::clicked是QAbstractButton的给我们提供的信号,
    //继承这个类的其他类型的按钮也有这个信号
    connect(button,&QPushButton::clicked,this,&Widget::handler);//建立关联关系
}
cpp 复制代码
/*只要点击按钮,窗口就会关闭*/

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


    button = new QPushButton(this);//创建按钮对象,button是widget的成员属性
    button->move(100,100);//将button安置到某位置

    //建立关联关系,&QPushButton::clicked是QAbstractButton的给我们提供的信号,
    //继承这个类的其他类型的按钮也有这个信号
    //&Widget::close是widget提供给我们的槽
    connect(button,&QPushButton::clicked,this,&Widget::close);//建立关联关系
}

顺便提一下,以前connect函数的第二和第四个参数都是char*类型的,因此每次传参都要把实参用宏强转成char*,但是现在重载了更好用的版本。

自动关联方式

右击选中想要设置信号和槽的控件,然后选择'转到槽':

然后我们选择一个按钮发射的信号:

然后我们发现widget的成员函数多了一个(头文件和源文件都写好了):

接下来我么只要在该函数编写信号的处理过程即可,该函数就是pushbutton按钮发出发射信号后所执行的槽函数。并且我们不需要手动关联信号源,信号,接收对象,槽,而是Qt通过命名方式自动关联的:

接下来我们使用一下这种方式:

cpp 复制代码
/*点击按钮,按钮上出现"哈哈"*/

void Widget::handler()
{
    button->setText("哈哈");
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

}

Widget::~Widget()
{
    delete ui;
}
void Widget::on_pushButton_clicked()
{
    ui->pushButton->setText("哈哈");
}

当我们查看ui文件生成的类,可以发现:

这个函数就是开启名字匹配的方式。


disconnect解除关联

diconnect函数和connect用法一样,只不过他是把关联关系解除


自定义槽

上面已经使用过自定义槽和Qt提供的槽(如close)。自定义槽其实就是编写一个成员函数然后传参给connect即可,但是在早期,Qt中自定义槽需要在声明时加**"slot"关键字****来帮助Qt进行元编程(现在不需要了,但是加上也可以),比如下面这样:**

槽还可以是一个lambda表达式,调用connect的时候传入即可**:**

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
     ui->setupUi(this);
     button = new QPushButton(this);
     connect(button,&QPushButton::clicked,this,[this]()->void{this->setWindowTitle("haha");});
}
Widget::~Widget()
{
    delete ui;
}

自定义信号

自定义信号只需要写出函数声明,并告诉Qt这是一个信号(使用signals关键字)即可,但是要保证信号的返回值必须是void。例如这样:

定义信号过后,就可以像正常信号一样关联使用了:

上面程序的意思就是打开窗口后Widget发送信号,然后将Widget内的按钮文本替换成"哈哈"。emit也是Qt添加的关键字,表示发射一个信号,但实际上现在不加emit也可以。


信号和槽的参数

如果要使用参数,信号和槽的对应参数的类型必须一致,但是在个数上信号的参数可以比槽更多(不可以更少),这样可以让槽被更多信号复用,关联。直接举一个使用参数的例子:

在这个例子中,我们实现了按不同按钮就设置不同标题的功能,但是设置标题的槽函数只有一个。


注意

一个类想要定义信号和槽,必须在类声明最开始的地方,写下Q_OBJECT宏:

如果不写这个宏,那么不能定义信号槽,但是使用Qt库提供给我们的信号槽是可以的

相关推荐
老骥伏枥~2 小时前
C# if / else 的正确写法与反例
开发语言·c#
不懒不懒2 小时前
【HTML容器与表格布局实战指南】
java·开发语言
J_liaty2 小时前
Java实现PDF添加水印的完整方案(支持灵活配置、平铺、多页策略)
java·开发语言·pdf
PPPPPaPeR.2 小时前
从零实现一个简易 Shell:理解 Linux 进程与命令执行
linux·开发语言·c++
Yorlen_Zhang2 小时前
python Tkinter Frame 深度解析与实战指南
开发语言·python
lly2024062 小时前
Eclipse 关闭项目详解
开发语言
LXS_3572 小时前
C++常用容器(下)---stack、queue、list、set、map
开发语言·c++·学习方法·改行学it
愚者游世2 小时前
list Initialization各版本异同
开发语言·c++·学习·程序人生·算法
Poetinthedusk2 小时前
WPF应用跟随桌面切换
开发语言·wpf