[C++] Qt-Signals and Slots

basic concept

Signals and slots are one of the mechanisms that the Qt framework is proud of. They are used to complete the response to interface operations and are the communication mechanism between any two Qt objects.

Signal: An event emitted under specific conditions. For example, the most common signal of pushButton is to emit a clicked signal when the mouse is clicked.

Slot: A function that responds to a signal. A slot is a class member function that can have any attribute (public, protected, private), can have parameters, or can be called directly. The difference between a slot function and a general function: a slot function can be associated with a signal. When the signal is When emitted, the associated slot function is automatically executed.

When an event occurs, it will emit a signal. If an object is interested in the signal, it will bind the signal to one of its own functions (called a slot) to process the function, and the slot function will Execution, that is, callback. So the essence of slot is a class member function.

All classes that use signals and slots must have the Q_OBJECT macro.

Add signals and slots

method one

In the UI interface editor, we manually add a signal and slot from the window. Suppose we want to click a button on the window to cause the window to close and the program to exit.

We can first pull out a Push Button component and then switch to the second tool button

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-Id0tSgtP-1687728177458) (C++.assets/image-20230613181326607.png)]

Drag a line out of the drag window and release it. A pop-up box will appear with the signal on the left and the slot on the right.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-RdiKeLfn-1687728177459) (C++.assets/image-20230613181516977.png)]

Select the corresponding signals and slots as required and click OK.

The signals and slots in this way are linked through a line of connect code in ui_mainwindow.h

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-ylfyhuoY-1687728177460)(C++.assets/image-20230613182741254.png)]

Method Two

There is also a faster way to add it. After selecting a component, right-click to go to the slot, and then select a signal. Then a slot will be automatically generated in the main window of the button's parent window. In other words, the processing function of this signal is given by Custom function processing in mainwindow.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-ti7ElxnD-1687728177460) (C++.assets/image-20230613182946532.png)]

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-oILqaOcO-1687728177461) (C++.assets/image-20230613183002379.png)]

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-wWXv9Lbu-1687728177461)(C++.assets/image-20230613183022765.png)]

There will also be corresponding slot function declarations in the .h file.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-h9WyVjP2-1687728177461) (C++.assets/image-20230613183055831.png)]

The signal slot added in this way is not directly bound by the connect function, but is bound according to the name rule in setupUi of ui_mainwindow.h.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-1h2n2wC3-1687728177462) (C++.assets/image-20230613183251159.png)]

Custom signals and slots

After QMetaObject::connectSlotsByName(MainWindow); is executed, the slot function will automatically match the specific naming rules, which means we can manually add a slot function that can be automatically called. But this method is not a long-term solution. If we manually specify the binding connection between the signal and the slot, we need to use the function QObject::connect.

//方式一:*(常用推荐)
QObject::connect(
	const QObject *sender,  //发出信号的对象
    const char *signal,     //发送对象发出的具体信号
    const QObject *receiver,//接收信号的对象
    const char *method,     //接收对象在接收信号之后所需要调用的函数
    Qt::ConnectionType type = Qt::AutoConnection  //连接类型
)
    
//方式一:*(常用推荐)
QMetaObject::connection QObject::connect(
	const QObject *sender,  
    PointerToMemberFunction signal,     
    const QObject *receiver,
    PointerToMemberFunction method,   
    Qt::ConnectionType type = Qt::AutoConnection  //连接类型
)   

Method 1 specifies signals and slots using macros SIGNAL(a) and SLOT(a) respectively. These two macros eventually convert signals and slots into strings.

The signals and slots in connect need to specify the function name, the parameter list formal parameter type but does not include its variable name, and no return value is required.

CheckBox

We add a Check Box button and Label text mark to the window in the design interface

Then manually declare and define the slot function in the main window header file and source file, and bind the signal-slot link in the constructor

Slot function declaration

void slots_stateChange(int);  //返回类型为 void ,参数应当与信号保持一致

definition

void MainWindow::slots_stateChange(int a){}

connect

    //信号-槽 绑定连接
    connect(ui->checkBox/*信号的发送者(对象)*/,
            SIGNAL(stateChanged(int))/*信号*/,  //SIGNAL 宏 参数 为信号的函数名和参数列表,如果有形参名应当去掉
            this/*接收者(对象)*/,
            SLOT(slots_stateChange(int))/*接收的槽函数*/);

Through testing, we found that the formal parameter a of the slot function is 2 when checked and 0 when not checked. Then the text mark can be changed based on this parameter.

void MainWindow::slots_stateChange(int a){
    qDebug()<<"slots_stateChange :"<<a;
    if(a==0){  //不勾选
        ui->label->setText("不勾选");
    }else if(a==2){  //勾选
        ui->label->setText("勾选");
    }
}

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-9o9k3ZTM-1687728177462) (C++.assets/image-20230613191725120.png)]

We find that the parameter status is 0 and 2 is missing 1. It turns out that 1 represents the half-selected state.

So we can set the three states in the constructor

ui->checkBox->setTristate(true);  //设置三态

In this way, you can operate on the half-selected state in the slot function.

Custom QMessageBox

The static functions provided in QMessageBox only support some defined buttons, but do not support custom buttons.

So we tried building the QMessageBox dialog manually, creating a QMessageBox object in the MainWindiw constructor and adding a title, text, and buttons.

    QMessageBox * pMsg = new QMessageBox;
    pMsg->setWindowTitle("title");
    pMsg->setText("我的文本");
    pMsg->addButton("是",QMessageBox::YesRole);
    pMsg->addButton("否",QMessageBox::NoRole);

Then we can find the corresponding signal in QMessageBox, so now the signal sender, signal and receiver are all there, we only need to create another slot function.

We declare the slot function in the header file, keep the parameters consistent with the signal, and then define it in the source file. In this way we can bind and link signals and slots.

void slots_buttonClicked(QAbstractButton *button);
void MainWindow::slots_buttonClicked(QAbstractButton *button){
    
}

Binding link

connect(pMsg,SIGNAL(buttonClicked(QAbstractButton *)),this,SLOT(slots_buttonClicked(QAbstractButton *)));

Then we need to implement the slot function. There is a button in the parameter. We can use this parameter to encapsulate the button. This parameter is the return value of the button written before, so we define two variables of type QPushButton* in the class member properties. Used to connect the return values ​​​​of yes and no, and since we also created the new object in the constructor when creating QMessagebox, it is equivalent to a temporary variable connected to a permanent object, so this variable is also declared in the class member attribute. .

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-8VBbchEk-1687728177463) (C++.assets/image-20230614150142902.png)]

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-poCLM3jL-1687728177463) (C++.assets/image-20230614150154609.png)]

Then you can perform corresponding operations based on this return value.

void MainWindow::slots_buttonClicked(QAbstractButton *button){
    if(button == pYes){
        qDebug()<<"是";
    }else if(button == pNo){
        qDebug()<<"否";
    }
}

The last step is the window display. We want this window to be connected with the checkbox, so in the slot function of the checkbox, if it is checked, the window will be displayed, and if it is not checked, the window will be hidden.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-I3jhoZjC-1687728177463) (C++.assets/image-20230614150331911.png)]

have a test

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-f1Q46bs8-1687728177464) (C++.assets/image-20230614150406156.png)]

Clicking Yes will also output "Yes" on the console.

Custom signal

Add a button and a single-line text input box to the mainwindow.ui design interface, then right-click the button and select Go to Slot to directly create a slot function that emits a signal.

Then get the text in the edit box in the slot function, then emit the signal and carry the parameter QString.

So we have to define a signal ourselves. The keyword for defining signals is signals. Access modifiers cannot be added, and only declarations are not defined.

signals:  //关键字修饰,不能加访问修饰符,只声明不定义
    void signals_sendQString(QString);

Because the signal is not the final executor, it cannot be used even if it is defined, so there is no need to define it at all.

For slot functions that emit signals, an emit is usually added before the signal function to tell people who read the code that this is not a normal function, but a signal. Of course, it doesn't matter if you don't write it.

//发射信号
void MainWindow::on_pb_send_clicked(){
    QString str = ui->lineEdit->text();    //获取编辑框上的文本

    //发射信号,并且携带参数 QString
    emit signals_sendQString(str);
}

Now there is a sender and a signal, but there is no receiver and receiving slot function.

We decided to send it to another window. Now there is only one mainwindow, so we need to add a new window.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-D5rneNql-1687728177464) (C++.assets/image-20230614154514984.png)]

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-2HBtbsOj-1687728177464) (C++.assets/image-20230614154531495.png)]

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-bsZX02YU-1687728177465) (C++.assets/image-20230614154551593.png)]

After that, it is done and a new header file, source file and design window will be created.

We add a label to this window to display the received text

Now that the receiver class is available, but the object has not been defined, we define the object in the main function.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-90sOPTfA-1687728177465) (C++.assets/image-20230614154813865.png)]

We can not display this window first, and then display it after receiving the signal. Then the received object is available, and the slot function is missing.

So declare and define the slot function in this window class, just make sure that the parameters are consistent with the signal, and don't forget to add the class name scope when defining, otherwise it will become a global function.

public slots:
    void slots_recvQString(QString);
void Dialog::slots_recvQString(QString s){
}

The last step is to connect. We need to connect the signal and slot in the main function. Because signals and slots belong to different categories, they can only be connected in the main function.

QObject::connect(&w,SIGNAL(signals_sendQString(QString)),&dia,SLOT(slots_recvQString(QString)));

Here you need to add QObject before connect. You didn't need to add it before because mainwindow belongs to its subclass, and the main function belongs to outsiders and cannot be used directly.

Then in the slot function, you can display the received text signal and display the window.

void Dialog::slots_recvQString(QString s){
    ui->label->setText(s);  //设置显示的文本
    this->show();
}

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-ayMm9j5d-1687728177465)(C++.assets/image-20230614155524471.png)]

What we have done above is to transfer information from one window to another through custom signal slots and manual binding links.

Signals and slots many-to-many

One signal connects multiple slots

Add three components: a horizontal slider, a time edit box and a progress bar on the window. You want to control the other two components through the horizontal slider.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-nxBXGyTP-1687728177466) (C++.assets/image-20230614231109395.png)]

Now we have the signal sender and signal, as well as the signal receiver, the only difference is the slot function

So we declare and define slot functions related to the two components respectively.

    void slots_timeChanged(int);
    void slots_progressChanged(int);

I won’t write down the definition yet.

After we have the slot function, we can bind the signal and the slot. Because we have one signal connecting multiple slots, only the position of the slot function is different when binding.

	connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_timeChanged(int)));
    connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_progressChanged(int)));

In this way, the signal is successfully bound to these two slot functions, and the rest is to implement the slot functions.

Because the default range of our horizontal slider is 0-99, we want it to be 0-100, so we manually set the horizontal slider range

   ui->horizontalSlider->setRange(0,100);

Because our time edit box wants to be set to the time of day, we define a QTime, assign it a value based on the value of signal a (how many seconds a unit represents), and then pass the time to this component. By the way, let’s also take a look at the display format of the edit box.

    ui->timeEdit->setDisplayFormat("hh-mm:ss");  //设置显示的格式
void MainWindow::slots_timeChanged(int a){
    qDebug()<<"a = "<<a;

    QTime time(0,0);  //定义一个初始时间
    time = time.addSecs(24*6*6*a);  //初始时间加上多少秒

    ui->timeEdit->setTime(time);  //时间组件上设置时间
}

The slot function time of the progress bar is very simple. You only need to pass in the integer signal as the value of the progress bar.

void MainWindow::slots_progressChanged(int a){
    ui->progressBar->setValue(a);  //设置进度条的值
}

In this way, we have achieved a signal bound to multiple slots.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-d7IoNbUd-1687728177466) (C++.assets/image-20230614234550612.png)]

Connect multiple signals to one slot

We add two push buttons to the main window and name them door switch and bedside switch respectively. We want the signals of these two switches to control the display and hiding of the dialog window at the same time.

Now we also have a signal sender, signal and signal receiver, so we only need to write a slot function in the signal receiver class to connect.

void slots_isShow();

After we have the slot function, we start to connect. Because it is a connection between two window objects, it must be implemented in the main function. However, when we want to call the ui of the main window in the main function, we find that the ui is locked. The reason We set ui as private in the class member properties, so we can make an interface function.

public:
    Ui::MainWindow * GetUI(){return ui;}

At the same time, we need to add the ui header file to the main function

#include "ui_mainwindow.h"

The link can now be implemented

    QObject::connect(w.GetUI()->pb_door,SIGNAL(clicked()),&dia,SLOT(slots_isShow()));
    QObject::connect(w.GetUI()->pb_bed,SIGNAL(clicked()),&dia,SLOT(slots_isShow()));

The last step is to implement the function of the slot function. What we want is that if the window is displayed, then press the button to close it. If the window is closed, then press the button to display it.

void Dialog::slots_isShow(){
    if(this->isVisible()){  //是否可视
        this->hide();
    }else{
        this->show();
    }
}

Now it is realized that as long as any signal is emitted (any button is clicked), this slot will be called (Dialog will be displayed or hidden).

A signal is connected to a signal and a slot is connected

Add a SpinBox component to the main window to enter numbers, and then add an LCD Number component to the Dialog window to display numbers. We want to change the numbers on the dialog at the same time by changing the numbers on the main window.

The normal idea is to call the display of the dialog window to the switch of the main window we just wrote. But there is a limitation here, that is, the public interface of ui cannot be used, so we need to declare a signal ourselves, then use the existing signal to connect to the signal we declared, and then use this signal to connect to the slot.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-W4N6LRDY-1687728177466) (C++.assets/image-20230615115942922.png)]

So first customize a signal, and the parameters should be consistent with the signal sent by spinBox.

void signals_transportValue(int);  //信号,只声明

Then connect the two signals in the constructor of mainwindow

    //主窗口中的 spinBOx 信号 与 主窗口的信号进行连接
    connect(ui->spinBox,SIGNAL(valueChanged(int)),this,SIGNAL(signals_transportValue(int)));

In this way, we only need to go to the main function to connect the custom signal and the slot function of the dialog.

First declare a slot function in the dialog

    void slots_setValue(int);

then connect

    QObject::connect(&w,SIGNAL(signals_transportValue(int)),&dia,SLOT(slots_setValue(int)));

Finally, just define the slot function.

void Dialog::slots_setValue(int a){
    ui->lcdNumber->display(a);
}

In this way, we have implemented signal->custom signal->slot function. In fact, no matter how many signals are passed between each other, there will eventually be a slot function to receive it.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-ZXaNdc8W-1687728177467) (C++.assets/image-20230615120418834.png)]

Disconnect

Add two checkboxes to the main window and name them Disconnect. Their function is to control whether the horizontal slider, time component, and progress bar are connected.

We can declare the slot functions that define these two state changes directly by going to the slot

There are two ways to disconnect here

method one

When the parameter is 0, it means it is not checked, then reconnect. If the parameter is 2, it is checked, it is disconnected.

We can directly change the connection function to disconnect when checked, and then still connect when not checked.

void MainWindow::on_cb_time_stateChanged(int arg1)
{
    if(arg1 == 0){  //不勾选 重新连接
        connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_timeChanged(int)));
    }else if(arg1 == 2){  //勾选 断开连接
        disconnect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_timeChanged(int)));
    }
}

Method Two

Another way is to perform disconnection by returning a value

We add a QMetaObject::Connection type return value in front of the horizontal slider and progress bar connection function, and put this return value in the class member attribute

    QMetaObject::Connection con;  //连接信息
con = connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_progressChanged(int)));

Then in the slot function, if it is checked, just fill in the return value in disconnect(). If it is not checked, just execute the connection function again. Note that the return value cannot be omitted here.

void MainWindow::on_cb_progress_stateChanged(int arg1)
{
    if(arg1 == 0){  //不勾选 重新连接
        con = connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_progressChanged(int)));
    }else if(arg1 == 2){  //勾选 断开连接
        disconnect(con);  //直接传递,连接信息
    }
}

Determine whether the connection is successful

The return value can also be used to determine whether the connection is successful. If the return value is true, it is successful, otherwise the connection fails.

    if(con){  //可用于判断是否连接成功
        qDebug()<<"连接成功";
    }else{
        qDebug()<<"连接失败";
    }

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-bgDSb1d6-1687728177467) (C++.assets/image-20230615122644504.png)]

Guess you like

Origin blog.csdn.net/jia_03/article/details/131389324