信号和槽是Qt对象间通信的一种机制,是Qt的核心特性,也是Qt与其他应用框架的显著区别。
信号和槽的基本使用如下:
下面是一个Qt信号和槽的基本使用,先看一下程序的运行效果,下图所示
当点击Add1按钮后,输入框显示的数字自增1;当点击Sub1按钮后,输入框中显示的数字自减一。
头文件,SignalsAndSlotDemo.h
#ifndef SIGNALSANDSLOT_H
#define SIGNALSANDSLOT_H
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>
#include <QVBoxLayout>
#include "UIBase/UIBaseWindow.h"
class SignalsAndSlotDemo : public UIBaseWindow
{
Q_OBJECT
public:
SignalsAndSlotDemo(QWidget *parent = nullptr);
~SignalsAndSlotDemo();
private:
QLineEdit *m_ValueEdit = nullptr;
QPushButton *m_AddButton = nullptr;
QPushButton *m_SubButton = nullptr;
signals:
void subSignals(void);
private slots:
void onClickedAddButton(void);
void onClickedSubButton(void);
private:
void onRecvSubSignals(void);
};
#endif
源文件, SignalsAndSlotDemo.cpp
#include "SignalsAndSlot.h"
#include "UIBase/UIGlobalTool.h"
SignalsAndSlotDemo::SignalsAndSlotDemo(QWidget *parent)
:UIBaseWindow(parent)
{
QVBoxLayout *mainLayout = new QVBoxLayout(this);
m_ValueEdit = new QLineEdit("0");
m_ValueEdit->setReadOnly(true);
m_AddButton = new QPushButton(tr("Add 1"));
m_SubButton = new QPushButton(tr("Sub 1"));
// 添加按钮阴影
g_GlobalTool->addShadowEffect(m_AddButton);
g_GlobalTool->addShadowEffect(m_SubButton);
QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->addWidget(m_AddButton);
bottomLayout->addWidget(m_SubButton);
mainLayout->addSpacerItem(new QSpacerItem(10, 30));
mainLayout->addWidget(m_ValueEdit);
mainLayout->addLayout(bottomLayout);
// 链接信号和槽函数
QObject::connect(m_AddButton, SIGNAL(clicked()), this, SLOT(onClickedAddButton()));
QObject::connect(m_SubButton, &QPushButton::clicked, this, &SignalsAndSlotDemo::onClickedSubButton);
QObject::connect(this, &SignalsAndSlotDemo::subSignals, [&](void)->void{
onRecvSubSignals();
});
}
SignalsAndSlotDemo::~SignalsAndSlotDemo()
{
}
void SignalsAndSlotDemo::onClickedAddButton(void)
{
int value = m_ValueEdit->text().toInt();
QString valueString = QString::number(++value);
m_ValueEdit->setText(valueString);
}
void SignalsAndSlotDemo::onClickedSubButton(void)
{
emit subSignals();
}
void SignalsAndSlotDemo::onRecvSubSignals(void)
{
int value = m_ValueEdit->text().toInt();
QString valueString = QString::number(--value);
m_ValueEdit->setText(valueString);
}
代码中创建了两个按钮,当点击“Add 1”按钮后,调用槽函数onClickedAddButton(),当点击“Sub 1”按钮后, 调用槽函数onClickedSubButton(),而onClickedSubButton()槽函数中有发送信号subSignals,subSignals信号 又连接一个lambda匿名函数,lambda匿名函数中调用函数onRecvSubSignals()。
这里使用了3种信号和槽的连接方式
上面介绍信号和槽的连接时都有一个默认的参数Qt::ConnectionType = Qt::AutoConnection,这个参数就是信号和槽的连接方式,具体每一种连接详细描述如下:
当我们需要做一些耗时的事情时,可以使用线程处理这类的事情,然后使用信号的方式通知主进程当前运行的进度等信息。 下面是一个关于跨线程的信号和槽的例子:
头文件,ThreadSigAndSlotsWidget.h
#ifndef THREAD_SIGANDSLOT_H
#define THREAD_SIGANDSLOT_H
#include <QProgressBar>
#include <QWidget>
#include <QPushButton>
#include <QThread>
#include "UIBase/UIBaseWindow.h"
class SignalThread;
class ThreadSigAndSlotsWidget : public UIBaseWindow
{
Q_OBJECT
public:
ThreadSigAndSlotsWidget(QWidget *parent = nullptr);
~ThreadSigAndSlotsWidget();
private:
QProgressBar *m_ProgressBar = nullptr;
QPushButton *m_StartButton = nullptr;
SignalThread *m_Thread = nullptr;
private slots:
void recvThreadValue(int);
void onClickedStarButton(void);
};
// --------------------- Signal Thread -------------------------
class SignalThread : public QThread
{
Q_OBJECT
public:
SignalThread(QObject *parnet = nullptr);
~SignalThread();
protected:
virtual void run(void) override;
signals:
void valueChanged(int);
};
#endif
源程序,ThreadSigAndSlotsWidget.cpp
#include "ThreadSigAndSlots.h"
#include "UIBase/UIGlobalTool.h"
#include <QVBoxLayout>
#include <QDebug>
ThreadSigAndSlotsWidget::ThreadSigAndSlotsWidget(QWidget *parent)
:UIBaseWindow(parent)
{
m_ProgressBar = new QProgressBar;
m_StartButton = new QPushButton("Start");
// 添加阴影
g_GlobalTool->addShadowEffect(m_StartButton);
m_ProgressBar->setValue(0);
m_ProgressBar->setRange(0, 100);
QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->addSpacerItem(new QSpacerItem(10, 30));
mainLayout->addWidget(m_ProgressBar, 0, Qt::AlignTop | Qt::AlignVCenter);
mainLayout->addWidget(m_StartButton, 0, Qt::AlignTop | Qt::AlignVCenter);
m_Thread = new SignalThread(this);
// 链接信号和槽
QObject::connect(m_Thread, &SignalThread::valueChanged, \
this, &ThreadSigAndSlotsWidget::recvThreadValue);
QObject::connect(m_StartButton, &QPushButton::clicked, \
this, &ThreadSigAndSlotsWidget::onClickedStarButton);
m_Thread->start();
}
ThreadSigAndSlotsWidget::~ThreadSigAndSlotsWidget()
{
}
void ThreadSigAndSlotsWidget::recvThreadValue(int value)
{
m_ProgressBar->setValue(value);
qDebug() << "GUI Thread ID: " << QThread::currentThreadId();
}
void ThreadSigAndSlotsWidget::onClickedStarButton(void)
{
if (!m_Thread->isRunning())
m_Thread->start();
}
// --------------------- Signal Thread -------------------------
SignalThread::SignalThread(QObject *parnet)
:QThread(parnet)
{
}
SignalThread::~SignalThread()
{
}
void SignalThread::run(void)
{
while (1)
{
qDebug() << "Thread ID: " << QThread::currentThreadId();
for (int i=0; i<=100; ++i)
{
emit valueChanged(i);
QThread::msleep(100);
}
return;
}
}
程序的运行效果如下图所示:
点击程序启动时启动线程,Start按钮后也可以开启线程,线程中执行一些耗时操作后发送信息个主进程并显示当前的完成进度。
控制台的打印信息如下:
Thread ID: 0x1f44
GUI Thread ID: 0x38d8
可见,发送信号所在线程和槽函数所在线程是不同的。