connect用于连接qt的信号和槽,在qt编程过程中不可或缺。它其实有第五个参数,只是一般使用默认值,在满足某些特殊需求的时候可能需要手动设置。
Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。
QObject::connect()本身是线程安全的。槽函数一般是不安全的。
1.信号和槽函数在同一个线程中的情况
class Test: public QMainWindow
{undefined
Q_OBJECT
Test()
signals:
void sigFirst();
private slots:
void slotFirst();
}
Test::Test(QWidget *parent)
: QMainWindow(parent) {undefined
ui.setupUi(this);
for (int i = 0; i < 5; i++) {//采用默认方式,连接5次
connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()));
}
emit sigFirst();
}
void Test::slotFirst() {undefined
numCoon++;
qDebug() << QStringLiteral("信号第")<<numCoon<<QStringLiteral("次连接");
}
运行之后的输出内容:
"信号第" 1 "次连接"
"信号第" 2 "次连接"
"信号第" 3 "次连接"
"信号第" 4 "次连接"
"信号第" 5 "次连接"`
如果代码修改一下,改为:
connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()), Qt::UniqueConnection);//注意第五个参数
再次运行一下,查看输出:
"信号第" 1 "次连接"
这次只发送了一次信号,但是咱们连接了5次,所以采用Qt::UniqueConnection方式连接,无论连接多少次,只发送一次信号,也只会执行一次槽函数
2.信号和槽函数在不同线程中的情况
自定义线程类:
#pragma once
#include <QThread>
class QtTestThread : public QThread {undefined
Q_OBJECT
public:
QtTestThread(QObject *parent);
~QtTestThread();
protected:
void run();
signals:
void sigSecond();
};
#include "QtTestThread.h"
#include <QDebug>
QtTestThread::QtTestThread(QObject *parent)
: QThread(parent) {undefined
}
QtTestThread::~QtTestThread() {undefined
}
void QtTestThread::run() {undefined
emit sigSecond();
qDebug() << QStringLiteral("信号发送完毕!");
}
调用线程类:
class QtTestThread;
class Test: public QMainWindow
{undefined
Q_OBJECT
Test()
signals:
void sigFirst();
private slots:
void slotThread();
private:
QtTestThread* testThread;
}
#include "QtTestThread.h"
Test::Test(QWidget *parent)
: QMainWindow(parent) {undefined
ui.setupUi(this);
testThread = new QtTestThread(this);
connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()));//没有第五个参数,也就是采用默认的连接方式
testThread->start();
}
void Test::slotThread() {undefined
qDebug() << QStringLiteral("线程发送的信号-槽函数执行!");
QThread::sleep(3);
}
运行一下,输出内容:
"信号发送完毕!"
"线程发送的信号-槽函数执行!"
由此可以看出,信号发送完成信号后,就直接运行下面的代码了,而发送的信号就会被放到主线程的信号队列中等待执行。
咱们信号槽的连接方式修改一下,添加信号槽的连接方式 Qt::BlockingQueuedConnection:
connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()), Qt::BlockingQueuedConnection);
再次运行一下:
"线程发送的信号-槽函数执行!"
"信号发送完毕!"//时间等待3秒之后才输出这句话
采用Qt::BlockingQueuedConnection的连接方式就实现了信号和槽函数的同步执行。
--
完整的源码如下:
头文件:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QThread> namespace Ui { class MainWindow; } class QtTestThread : public QThread { Q_OBJECT public: QtTestThread(QObject *parent); ~QtTestThread(); protected: void run(); signals: void sigSecond(); }; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; signals: void sigFirst(); private slots: void slotFirst(); void slotThread(); private: QtTestThread* testThread; int numCoon; }; #endif // MAINWINDOW_H
源文件:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> QtTestThread::QtTestThread(QObject *parent) : QThread(parent) { } QtTestThread::~QtTestThread() { } void QtTestThread::run() { emit sigSecond(); qDebug() << QStringLiteral("信号发送完毕!"); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); for (int i = 0; i < 5; i++) {//采用默认方式,连接5次 //connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()));//没有第五个参数,也就是采用默认的连接方式 connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()), Qt::UniqueConnection);//注意第五个参数 } numCoon = 0; //emit sigFirst(); testThread = new QtTestThread(this); //connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()));//没有第五个参数,也就是采用默认的连接方式 //connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()), Qt::QueuedConnection);//效果同上 connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()), Qt::BlockingQueuedConnection);//效果不同 testThread->start(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::slotFirst() { numCoon++; qDebug() << QStringLiteral("信号第")<<numCoon<<QStringLiteral("次连接"); } void MainWindow::slotThread() { qDebug() << QStringLiteral("线程发送的信号-槽函数执行!"); QThread::sleep(3); }