信号+槽——QT的核心特性

在 QT里面实现控件与控件之间的联动关系(数据交互关系)

信号:

当一个组件发生任何改变(状态或者属性改变,人为主动操作/被动改变)
这些状态改变,称为 发生了事件

当任意一个组件,发生事件,该组件会发出一个与该事件关联的信号
    通过发出不同的信号,来告诉外部,自己发什么了什么改变

本质是一个函数,必须是一个控件/窗口的成员函数

槽函数:

接受并处理一个信号

    槽函数允许与一个信号关联
    如果槽函数发现与其关联的信号触发之后,就会自动调用槽函数

种类:

    1:槽函数可以是一个控件/窗口的成员函数
    2:槽函数可以是一个全局函数
    3:槽函数可以是一个匿名函数
    4:槽函数可以是一个信号函数

关联

1.当槽函数是控件/窗口的成员函数

QObject::connect(信号发送方控件地址,&信号发送方控件类型::信号名,信号接收方地址,&信号接收方控件类型::槽函数名)

le = new QLineEdit();
btn = new QPushButton();
QObject::connect(btn,&QPushButton::clicked,le,&QLineEdit::setText) 

槽函数如果带参数,实参来自于哪

信号和槽,不仅有简单的通知功能,还可传递数据

信号函数: signal(const QString& str = "hello world")
槽函数:slot(QString ptr);
signal 函数 和 slot 关联
那么,当信号函数触发, signal函数会将他的参数 str (默认为"hello world") 传递给 slot 的参数 ptr

所以注意:槽函数如果参数,说明要接受一个数据,那么信号就必须有一个相同的数据发过去,也就是信号函数必须有一个同样的参数
    反过来不一样,信号如果有参数,槽函数可以没有

 关联位置

在代码的任意处连接信号和槽
取决于,哪块位置访问需要连接的2个控件地址更为方便, 则就在哪里进行连接

2. 当槽函数是全局函数 

void func(){
    qDebug() << "hello world";
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QObject::connect(ui->pushButton_2,&QPushButton::clicked,func);
使用 connect 函数直接与全局函数连接
}

3.当槽函数是lambda表达式

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QObject::connect(ui->pushButton_2,&QPushButton::clicked,[&]()mutable{qDebug() << "lambda";});
}
connect函数的第3个参数,直接写lambda表达式的定义就好了

4.当槽函数是信号 

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QObject::connect(this,&Widget::mysignal,this,&Widget::myslot_2);
    QObject::connect(ui->pushButton_2,&QPushButton::clicked,this,&Widget::mysignal);
}
通过connect函数将 clicked 信号链接到 mysignal信号
由于mysignal信号连接到了 myslot_2,
所以,最终结果:clicked信号通过mysignal信号连接到了 myslot_2

自定义槽函数

目的

接受一个信号的槽函数,需要操作多个组件窗口,
拥有多个组件窗口地址的,一般就是一个父组件
so要为我们自己写的父组件,写一个自定义的槽函数,去操作多个组件

注意:

1:必须写在 public slots 访问权限里面

2:拥有自定义槽函数的类,必须在开头有一个宏:Q_OBJECT

        如果没有,虚表报错

        虚表报错了,再加上这个宏,编译还是报错,只能  重新构建 才能解决

3:有自定义槽函数的类,必须分文件编译

C++ 分文件的规则:一个类占一个头文件;

类里成员函数,定义在对应的.cpp 文件

练习:

编写2个聊天窗口,实现互相聊天功能

//widget.h
class Widget : public QWidget
{   Q_OBJECT

    QLineEdit* le;
    QTextEdit* te;
    QPushButton* btn;

    QWidget* sw;
    QHBoxLayout* hlay;
    QVBoxLayout* vlay;
    Widget*that;//

public:
    Widget(QWidget *parent = nullptr);
    void setThat(Widget*w);///
public slots:
    void myslot();
signals:
    void mysignal(QString text);
};



//widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent)
{
    sw = new QWidget(this);  // 创建一个子窗口
    te = new QTextEdit(this);  // 创建文本编辑器
    vlay = new QVBoxLayout(this);  // 创建垂直布局(主布局)

    // 将子窗口和文本编辑器添加到主布局
    vlay->addWidget(sw);
    vlay->addWidget(te);

    le = new QLineEdit(sw);  // 创建单行文本输入框
    btn = new QPushButton("Click Me", sw);  // 创建按钮
    hlay = new QHBoxLayout(sw);  // 创建水平布局(子窗口布局)

    // 将单行文本输入框和按钮添加到子窗口布局
    hlay->addWidget(le);
    hlay->addWidget(btn);

    // 连接按钮点击信号到槽函数
    connect(btn, &QPushButton::clicked, this, &Widget::myslot);
 }
void Widget::myslot(){
    QString text = le->text();  // 获取单行文本输入框的内容
    te->setPlainText(text);    // 将内容显示在文本编辑器中
    te->append(text);
    le->clear();
    that->te->append(text);
}
void Widget::setThat(Widget*w){//
    that=w;
}



//main.cpp
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Widget w1,w2;
    w1.setWindowTitle("Window 1");
    w2.setWindowTitle("Window 2");

    w1.setThat(&w2);
    w2.setThat(&w1);

    w1.show();
    w2.show();
    return app.exec();  // 启动事件循环
}
//widget.h
class Widget : public QWidget
{   Q_OBJECT

    QLineEdit* le;
    QTextEdit* te;
    QPushButton* btn;

    QWidget* sw;
    QHBoxLayout* hlay;
    QVBoxLayout* vlay;
    //Widget*that;
public:
    Widget(QWidget *parent = nullptr);
    //    void setThat(Widget*w);
    void receiveText(const QString& text);
public slots:
    void myslot();
signals:
    void mysignal(QString text);
};

//widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent)
{
    sw = new QWidget(this);  // 创建一个子窗口
    te = new QTextEdit(this);  // 创建文本编辑器
    vlay = new QVBoxLayout(this);  // 创建垂直布局(主布局)

    // 将子窗口和文本编辑器添加到主布局
    vlay->addWidget(sw);
    vlay->addWidget(te);

    le = new QLineEdit(sw);  // 创建单行文本输入框
    btn = new QPushButton("Click Me", sw);  // 创建按钮
    hlay = new QHBoxLayout(sw);  // 创建水平布局(子窗口布局)

    // 将单行文本输入框和按钮添加到子窗口布局
    hlay->addWidget(le);
    hlay->addWidget(btn);

    // 连接按钮点击信号到槽函数
    connect(btn, &QPushButton::clicked, this, &Widget::myslot);
 }
void Widget::myslot(){
    QString text = le->text();  // 获取单行文本输入框的内容
    //te->setPlainText(text);    // 将内容显示在文本编辑器中
    te->append(text);
    le->clear();
 //that->te->append(text);
    emit mysignal(text);
}
//void Widget::setThat(Widget*w){
//    that=w;
//}
void Widget::receiveText(const QString& text)
{
    te->setPlainText(text);  // 将接收到的内容显示在文本编辑器中
}


//main.cpp
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Widget w1,w2;
    w1.setWindowTitle("Window 1");
    w2.setWindowTitle("Window 2");

   //    w1.setThat(&w2);
    //    w2.setThat(&w1);

    // 连接窗口2的textSent信号到窗口1的receiveText槽函数
    QObject::connect(&w1, &Widget::mysignal, &w2, &Widget::receiveText);
    QObject::connect(&w2, &Widget::mysignal, &w1, &Widget::receiveText);

    w1.show();
    w2.show();
    return app.exec();  // 启动事件循环
}

自定义信号

目的:

省写一个接口
接口和信号做的是一件事情

怎么写

1:写在signals访问权限里面
2:参数,也可向槽函数发送数据,所以槽函数要什么数据,自定义信号写什么数据就好了
3:信号函数无需定义
4:发送信号时,直接调用信号函数,函数前加 emit

转到槽 功能

依赖于ui界面,ui界面中的子组件,通过ui->去访问
只适用于能够在ui界面上显示的组件
一些组件无法在ui界面上布局,只能用 connect

connect 最后一个参数的意义

Qt::AutoConnection自动连接:
如果信号和槽所在的组件,处于同一个线程,则使用Qt::DirectConnection模式,如果不处于同一个线程,则使用Qt::QueuedConnection

Qt::DirectConnection 直接连接:
当信号和槽所在的组件,处于同一个线程的时候,信号一旦发送,则立刻调用槽函数

Qt::QueuedConnection队列连接:
当信号和槽所在的组件,不处于同一个线程的时候,信号触发后,需要等待程序运行到槽函数所在的组件所处的线程运行的时候,才会触发槽函数

Qt::BlockingQueuedConnection    阻塞队列连接:
当信号和槽所在的组件,不处于同一个线程的时候,信号触发后,需要等待程序运行到槽函数所在的组件所处的线程运行的时候,才会触发槽函数
        但是 信号所在的线程会阻塞,直到槽函数运行完才接触阻塞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值