Qt之回调函数:6 在QT中使用回调函数替代信号槽

在QT中使用回调函数替代信号槽

前面讲了一堆,就是为了实现看到的这篇文章中的例子。由于对回调不懂,前前后后翻阅参考了一大堆优秀的博文,学了不少东西,可是。。。。哎,效果不理想,没信号槽快、稳定。

在QT中使用回调函数替代信号槽

一、前言

​ 在Qt中传数据,我们使用了一个信号和槽系统,但这并不意味着不能使用旧的经过验证的方法,即使用 CallBack回调函数功能。
​ 事实上使用 CallBack 功能比信号和槽要快一些。并且当发送信号的对象在程序中被销毁并且不再使用时,信号理想地从槽中分离而言,回调可以更容易使用。


二、如何使用CALLBACK工作

​ 假设A类包含B类的对象,B类有动作时想要通知到A类,B类应该有个设置回调函数的接口,A类应该定义相应的回调函数,将函数指针传递给B。

还是直接举例吧:
例如,将使用一个类,在图形场景中绘制一个正方形,并由W,A,S,D键控制。移动时,正方形必须将其坐标的数据发送到创建它的类。也就是说,它应该把这个类的函数作为它的 CallBack 函数。要做的程序效果如下,通过WASD控制方块移动,主窗口接收正方形的位置信息,并将位置信息填入QLineEdit:

这里写图片描述


程序及解释

小方块

Square.h

#pragma once

#include <QFrame>
#include <QKeyEvent>

typedef std::function<void(QPointF)> CallBack ;

class Square : public QFrame
{
    Q_OBJECT
public:
    explicit Square(QWidget *parent = nullptr);
    // 设置回调函数的函数
    void setCallbackFunc(CallBack fun);
protected:
    void keyPressEvent(QKeyEvent *e) override;

signals:
    void moved(QPointF point);
private:
    CallBack m_func;
};

Square.cpp

#include "Square.h"
#include "DebugTool.h"
#include <QPointF>
#include <QTime>
#include <QTimer>
Square::Square(QWidget* parent)
    : QFrame(parent)
{
    setAttribute(Qt::WA_StyledBackground);
    setFocusPolicy(Qt::StrongFocus);
}


// 注册回调函数
void Square::setCallbackFunc(CallBack func)
{
    m_func = func;
}

void Square::keyPressEvent(QKeyEvent* e)
{
    QPoint point = this->pos();

    int key = e->key();
    if (key == Qt::Key_W)
        point += QPoint(0, -2);
    if (key == Qt::Key_A)
        point += QPoint(-2, 0);
    if (key == Qt::Key_S)
        point += QPoint(0, 2);
    if (key == Qt::Key_D)
        point += QPoint(2, 0);
    //    cout << QTime::currentTime ();
    //    m_func(this->pos ());

    this->move(point);
    //    emit moved(point);    // 传统的信号槽
    m_func(point);      // 调用回调函数
}

主窗口

MainWindow.h

#pragma once

#include "Square.h"

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    Square *m_square;           // 声明正方形 传输回调
    // 声明一个回调函数
    void getPosition(QPointF point);
};

MainWindow.cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "DebugTool.h"

#include <QTime>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_square = ui->square;
    CallBack func;
    func = std::bind(&MainWindow::getPosition,this,std::placeholders::_1);
    m_square->setCallbackFunc (func);
    ui->square->setFocus ();
    connect (ui->square,&Square::moved,this,&MainWindow::getPosition );
    getPosition (ui->square->pos ());
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::getPosition(QPointF point)
{
//    cout << QTime::currentTime();
    ui->edt_x->setText (QString::number (point.x ()));
    ui->edt_y->setText (QString::number (point.y ()));
}

MainWindow.ui

界面简化了,没有使用Graphics View 框架

image-20210528214333124

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <widget class="QLineEdit" name="edt_x"/>
    </item>
    <item row="0" column="1">
     <widget class="QLineEdit" name="edt_y"/>
    </item>
    <item row="1" column="0" colspan="2">
     <widget class="QWidget" name="widget" native="true">
      <property name="styleSheet">
       <string notr="true">border:1px solid black;</string>
      </property>
      <widget class="Square" name="square">
       <property name="geometry">
        <rect>
         <x>280</x>
         <y>120</y>
         <width>100</width>
         <height>100</height>
        </rect>
       </property>
       <property name="styleSheet">
        <string notr="true">background-color: rgb(85, 255, 0);</string>
       </property>
       <property name="frameShape">
        <enum>QFrame::NoFrame</enum>
       </property>
       <property name="frameShadow">
        <enum>QFrame::Raised</enum>
       </property>
      </widget>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>26</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <customwidgets>
  <customwidget>
   <class>Square</class>
   <extends>QFrame</extends>
   <header location="global">Square.h</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

总结

使用 std::function< T > 和 std::bind( ) 后,结果发现居然还没有信号槽快。

### Qt回调函数的实现与用法 在 C++ 和 Qt 编程环境中,回调函数是一种常见的设计模式,用于在特定条件下触发某些功能。以下是关于 Qt 回调函数的具体实现方式及其应用。 #### 1. 基本概念 回调函数本质上是指向函数的指针[^1]。它允许一个类(如 B 类)在其内部存储另一个类(如 A 类)中定义的函数地址,并在适当的时间调用该函数。这种机制常用于事件驱动编程模型中,例如 GUI 应用程序开发中的信号槽机制。 #### 2. 实现方法 ##### 方法一:使用传统函数指针 这是最基础的方式之一。假设 `A` 是提供回调函数的类,而 `B` 是调用这些回调函数的类,则可以通过以下步骤完成: - **定义函数指针类型** ```cpp class A { public: typedef void (*CallbackFunction)(int, int); static void callbackFunc(int x, int y) { /* 函数体 */ } }; ``` - **设置回调接口** ```cpp class B { private: A::CallbackFunction m_callback; public: void setCallback(A::CallbackFunction func) { m_callback = func; } void triggerCallback() { if (m_callback != nullptr) { m_callback(10, 20); // 调用回调函数 } } }; ``` 在此例子中,`B` 的成员变量 `m_callback` 存储了一个指向 `A::callbackFunc` 的函数指针。通过调用 `setCallback()` 设置回调函数后,`triggerCallback()` 可以随时调用此函数[^4]。 --- ##### 方法二:利用 Lambda 表达式和 std::function 现代 C++ 提供了更灵活的方式来处理回调函数,即使用 `std::function` 配合 lambda 表达式。这种方式更加通用且易于扩展。 - **定义支持的标准库类型** ```cpp #include <functional> class B { private: std::function<void(int, int)> m_callback; public: void setCallback(const std::function<void(int, int)>& func) { m_callback = func; } void triggerCallback() { if (m_callback) { m_callback(10, 20); // 使用标准库封装后的回调 } } }; ``` - **绑定回调逻辑** ```cpp class A { public: static void callbackFunc(int x, int y) { printf("Callback called with %d and %d\n", x, y); } }; // 绑定过程 B bInstance; bInstance.setCallback([](int x, int y){ A::callbackFunc(x, y); }); bInstance.triggerCallback(); ``` 这种方法的优势在于能够轻松捕获上下文环境的数据,同时保持代码简洁明了[^2]。 --- ##### 方法三:基于 Qt 的 Signal & Slot 机制 虽然严格意义上讲,Signal 和 Slot 并不完全等同于传统的回调函数,但在实际应用场景下它们的功能非常相似。开发者可以直接连接两个对象之间的信号和槽,从而简化跨组件通信流程。 - **示例代码** ```cpp #include <QObject> #include <QDebug> class A : public QObject { Q_OBJECT signals: void notify(int value); public slots: void handleResponse(int result) { qDebug() << "Received response:" << result; } }; class B : public QObject { Q_OBJECT public slots: void processRequest(int input) { emit finishedProcessing(input * 2); } signals: void finishedProcessing(int output); }; int main() { A aObj; B bObj; QObject::connect(&aObj, &A::notify, &bObj, &B::processRequest); QObject::connect(&bObj, &B::finishedProcessing, &aObj, &A::handleResponse); aObj.notify(5); // 发送通知并等待响应 } ``` 在这个场景里,当 `A` 对象发出 `notify` 信号时,`B` 将接收数据并通过其自身的计算返回结果;最终再次触发 `A` 的槽函数完成整个交互链条[^3]。 --- ### 总结 以上介绍了三种不同的技术手段来实现在 Qt 环境下的回调函数需求——分别是经典函数指针形式、现代化 lambdas 加上 `std::function` 结构以及原生 Signals/Slots 构建方案。每种都有各自适用范围及优缺点,请依据项目实际情况选取合适的技术路径加以实践。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值