【QT】——信号和槽

信号和槽是Qt框架的核心机制,用于对象间通信。信号在用户交互或特定事件发生时触发,而槽是响应信号的函数。通过`connect`函数,可以将信号与槽关联,实现当信号发出时自动调用槽函数。Qt中提供了标准的信号和槽,也可自定义。自定义信号和槽需遵循特定规则,如返回值为void,使用`signals`和`slots`关键字声明。文章还介绍了Qt4和Qt5的连接方式差异。
摘要由CSDN通过智能技术生成

1. 信号和槽的概念

信号和槽是 Qt 特有的信息传输机制,是 Qt 设计程序的重要基础,它可以让互不干扰的 对象建立一种联系,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发

1.1 信号的本质

信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时候 Qt 对应的窗口类会发出某个信号,以此对用户的挑选做出反应。

信号的本质是事件:

  • 按钮单击、双击
  • 窗口刷新
  • 鼠标移动、鼠标按下、鼠标释放
  • 键盘输入
  • ....

1.2 槽的本质

槽的本质是类的成员函数,其参数可以是任意类型的。

  • 槽函数和普通 C++成员函数几乎没有区别,它可以是虚函数;也可以被重载;可以是公有的、保护的、私有的、也可以被其他 C++ 成员函数调用
  • 唯一区别的是:槽可以与信号连接在一起,每当和槽连接的信号被发射的时 候,就会调用这个槽

1.3 信号和槽的关系

  • 在 Qt 中 信号和槽函数 都是独立的个体,本身没有任何联系,但是由于某种特性需求我们可以将二者连接到一起。
  • 在 Qt 中我们需要使用 QOjbect类中的 connect 函数进二者的关联。
  • 一个信号可以连接到多个槽。
  • 一个槽可以被多个信号连接。
  • 信号也可以连接到信号,此时前者的发射信号将导致后者的发射。
  • 信号的参数类型可以与槽的参数类型对应,信号的参数可以比槽的参数多,但不可以少, 否则连接将失败。

connect(const QObject *sender, &QObject::signal, 
        const QObject *receiver, &QObject::method);


connect中的参数:

  • sender:发送信号的对象
  • signal:属于sender对象, 信号是一个函数, 这个参数的类型是函数指针, 信号函数地址
  • receIver:信号接收者
  • method:属于于receiver对象, 当检测到sender发出了signal信号, receiver对象调用method方法,信号发出之后的处理动作。

使用connect()进行信号槽连接的注意事项:

  • connect函数相对于做了信号处理动作的注册。
  • 调用conenct函数的sender对象的信号并没有产生, 因此receiver对象的 method 也不会被调用
  • method槽函数本质是一个回调函数, 调用的时机是信号产生之后, 调用是Qt框架来执行的
  • connect中的sender和recever两个指针必须被实例化了, 否则conenct不会成功

2. 标准信号/槽的使用

在 Qt 提供的很多标准类中都可以对用户触发的某些特定事件进行检测,因此当用户做了这些操作之后,事件被触发类的内部就会产生对应的信号,这些信号都是 Qt 类内部自带的,因此称之为标准信号。

如何查找控件的信号或槽函数?

1.进入QT文档,点击查找,比如我想查找QPushButton的信号和槽函数。

2.槽函数实在Public Slots中,信号函数是在Signals中,如果没有Signals,我们可以到它的基类中查找是否有Signals,也可以使用基类中的信号和槽函数

使用

功能实现:点击按钮,窗口。

// 单击按钮发出的信号
[signal] void QAbstractButton::clicked(bool checked = false)
// 关闭窗口的槽函数
[slot] bool QWidget::close()

// 单击按钮关闭窗口
connect(ui->closewindow, &QPushButton::clicked, this, &MainWindow::close);

  • connect()操作一般写在窗口的构造函数中, 相当于在事件产生之前在qt框架中先进行注册。
  •  这样在程序运行过程中假设产生了按钮的点击事件, 框架就会调用信号接收者对象对应的槽函数了, 如果信号不产生, 槽函数也就一直不会被调用


3. 自定义信号和槽的使用

 除了QT提供的标准信号和槽函数外,我们也可以自定义信号和 槽函数。

自定义信号和槽函数的条件:

  • 需要将信号 或槽函数 放在一个自定义类中。
  • 自定义的类必须 继承QObject 或者 QObject 的子类
  • 在定义类中头文件加入 Q_Object宏

3.1 自定义信号

在 Qt 中信号的本质是事件,但是在框架中也是以函数的形式存在的,只不过信号对应的函数只有声明,没有定义。

自定义信号的要求:

  1. 信号是类的成员函数;
  2. 返回值必须是void类型
  3. 信号名称可以根据实际情况进行指定。
  4. 信号可以支持重载
  5. 信号需要使用signals关键字进行声明,使用方法类似public等关键字
  6. 信号只需要声明,不需要定义(没有函数体实现)
  7. 习惯性在信号函数前加关键字: emit, 但是可以省略不写
    emit只是显示的声明一下信号要被发射了, 没有特殊含义
    底层 emit == #define emit

如下:

class myWindow:public QObject
{
    Q_OBJECT;
signals:
    
    void my_signal1(int a);
    void my_signal1();
   //信号的参数的作用是传递给 槽函数
};

3.2 自定义槽函数

槽函数就是信号的处理动作,在 Qt 中槽函数可以作为普通的成员函数来使用,自定义槽函数和自定义的普通函数写法是一样的。

自定义槽函数的要求:

  1. 返回值必须是void类型
  2. 槽函数支持重载
  3. Qt 中的槽函数可以是类的成员函数、全局函数、静态函数、Lambda表达式(匿名函数)
  4. 槽函数的参数是用来接收信号传递的数据的, 信号传递的数据就是信号的参数,信号函数和槽函数的参数类型需要进行匹配。

举例:

信号函数:void my_slots(int a);
槽函数:void my_signal1(int a);

信号的参数可以大于等于槽函数的个数:

//下面的信号和槽函数可以用connect进行链接
//其中槽函数只接受 int类型参数
信号函数: void testsig (int a, double b);
槽函数: void testslot (int a);

//下面的信号和槽函数不能用connect进行链,因为参数不匹配
信号函数: void testsig (QString a);
槽函数: void testslot (int a);

 

  • 槽函数可以使用关键字进行声明: slots (Qt5中slots可以省略不写)
public slots:
private slots: –> 这样的槽函数不能在类外部被调用
protected slots: –> 这样的槽函数不能在类外部被调用

实例:

需求:点击一下按钮,则闹钟响了,我起来做饭

定义闹钟类:

class my_clock:public QObject
{
    Q_OBJECT;

   signals:
    void alarm();
};

定义个人类:

class me:public QObject{
    Q_OBJECT;
public slots:
    void Do()//槽函数
    {
        qDebug()<<"起床做饭";
    }
};

QMainWindow类:


class MainWindow : public QMainWindow
{
    Q_OBJECT

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

public slots:
    void m_trigger(){
       c->alarm();
   }

private:
    Ui::MainWindow *ui;
    me* m;
    my_clock* c;
};

QMainWindow的构造函数:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m=new me;
    c=new my_clock;
    
    //点击按钮发出的click信号,被QMaindow接收,触发m_trigger槽函数
    //m_trigger槽函数中触发 my_clock 发送alarm信号
    //me接收到 alarm信号,触发Do槽函数,打印起床做饭
    connect(ui->button,&QPushButton::clicked,this,&MainWindow::m_trigger);
    connect(c,&my_clock::alarm,m,&me::Do);
}

展示效果: 

4. 信号和槽的两种连接方式

connect中有两种连接方式,一种是QT4,另一种是QT5.他们的主要差别是 参数 信号 和 槽函数的差别。

4.1 QT4的连接方式 

  • QT4中中的信号和槽函数的参数是 字符串类型,信号槽函数通过宏 SIGNAL 和 SLOT 转换为字符串类型。
  • 一般是不推荐使用QT4连接方式,因为信号槽函数的转换是通过宏来进行转换的,因此传递到宏函数内部的数据不会被进行检测, 如果使用者传错了数据,编译器也不会报错
  • QT4中声明槽函数必须使用slots关键字,不能省略
[static] QMetaObject::Connection QObject::connect(
    const QObject *sender, const char *signal, 
    const QObject *receiver, const char *method, 
    Qt::ConnectionType type = Qt::AutoConnection);



connect(const QObject *sender,SIGNAL(信号函数名(参数1, 参数2, ...)),
        const QObject *receiver,SLOT(槽函数名(参数1, 参数2, ...)));


4.2 QT5的连接方式

QT5中的信号和槽函数的参数是 函数指针:

// 信号和槽函数也就是第2,4个参数传递的是地址, 编译器在编译过程中会对数据的正确性进行检测
connect(const QObject *sender, &QObject::signal, 
        const QObject *receiver, &QObject::method);

4.3 实例:

场景描述:我肚子饿了,我要吃东西。

QT的连接方式:

class Me:QObject{
    Q_OBJECT;
public:
    void eat(){
        qDebug()<<"我要吃酸菜鱼";
    }
    void eat(QString s){
        qDebug()<<"我要吃"<<s;
    }
    
signals:
    void hungry();
    void hungry(QString s);
    
};

Me m;

connect(m,SIGNAL(hungry()),m,SLOT(eat()));
connect(m,SIGNAL(hungry(QString)),m,SLOT(eat(QString)));

 QT5的连接方式:

// Qt5处理方式
connect(&m, &Me::eat, &m, &Me::hungury);	// error

上面这种连接方式是错误的,,信号和槽都是通过函数名去关联函数的地址, 但是这个同名函数对应两块不同的地址, 一个带参, 一个不带参, 因此编译器就不知道去关联哪块地址了, 所以如果我们在这种时候通过以上方式进行信号槽连接, 编译器就会报错。

正确的做法:

  1. 通过定义函数指针的方式指定出函数的具体参数,这样就可以确定函数的具体地址了。
  2. 例如使用将带QString参数的
void (Me::*m_slot)(QString)=&Me::eat; //指定带参数Qtring的 槽函数
void (Me::*m_signal)(QString) = &Me::hungry;//指定带参数Qtring的信号
connect(&m,m_slot,&m,m_signal);//连接

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值