c++multithreadingoopqt-creatorqt-signals

How may I call a GUI function from an external thread on QT Creator?


I read some topics, and now I know I have to use Signals and Slots, but I'm having some trouble doing it. At my user interface I have a Q List Widget and I need to add items to it from another thread; I'm using the following code into my tela_chat.cpp that corresponds to the screen where my QList is inserted:

//tela_chat.cpp
#include <QThread>
#incluede "tela_chat.h"

tela_chat::tela_chat(QWidget *parent) :
    QFrame(parent),
    ui(new Ui::tela_chat)
{
    ui->setupUi(this);
    connect(this, SIGNAL(call_chatReceived(QString)), this, SLOT(receivedChat(QString))); //Connect signal to slot function into the constructor of the class
}


    tela_chat::~tela_chat()
    {
        delete ui;
    }
    
    void tela_chat::receivedChat(QString mensagem)
    {
        //The signal has been received
        ui->lstWdgt_chat->addItem("Professor: " + mensagem); //Add item to QList
    }
    
    void tela_chat::on_btn_conectar_clicked()
    {
    
        //Creating an other thread
    
        class ThreadChat : public QThread {
            void run() override {
    
                //Cóigo para rodar na thread separada:
                    QString mensagem = "This is what I want to put into QList";
                    emit call_receivedChat(mensagem);
            }
        };
    
    
        //INICIALIZATING THE NEW THREAD
    
        ThreadChat *thread = new ThreadChat;
        thread -> start(); //Inicia a thread (chamando o run())
        //thread -> wait(); 
    
               
    }

And this is the linked header file:

//tela_chat.h
#include <QString>

namespace Ui {
class tela_chat;
}

class tela_chat : public QFrame
{
    Q_OBJECT

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

private slots:  
    void receivedChat(QString mensagem);
    void on_btn_conectar_clicked();

private:
    Ui::tela_chat *ui;
    
signals:
    void call_receivedChat(QString mensagem);
};

There are some other functions like a socket connection script, but I removed it to make the code as the minimum reproducible amount of code. 


Solution

  • In your constructor, you connect call_chatReceived (signal) to receivedChat (slot), but your signal is defined as call_receivedChat. Make sure the signal and slot names match. Also you should connect the signal in your tela_chat constructor, not in the method. The connection should be in the constructor to establish the connection once when the object is created.

    Here's the corrected code:

    // tela_chat.cpp
    #include <QThread>
    #include "tela_chat.h"
    
    tela_chat::tela_chat(QWidget *parent) :
        QFrame(parent),
        ui(new Ui::tela_chat)
    {
        ui->setupUi(this);
    
        // Connect the signal and slot in the constructor
        connect(this, SIGNAL(call_receivedChat(QString)), this, SLOT(receivedChat(QString));
    }
    
    void tela_chat::receivedChat(QString mensagem)
    {
        // The signal has been received
        ui->lstWdgt_chat->addItem("Professor: " + mensagem); // Add item to QList
    }
    
    void tela_chat::on_btn_conectar_clicked() {
    // Creating another thread
    class ThreadChat : public QThread {
    public:
        ThreadChat(tela_chat* chatInstance) : chatInstance(chatInstance) {}
    
        void run() override {
            QString mensagem = "This is what I want to put into QList";
            emit chatInstance->call_receivedChat(mensagem);
        }
    
    private:
        tela_chat* chatInstance;
    };
    
    // Initializing the new thread
    ThreadChat* thread = new ThreadChat(this); // Pass the instance of tela_chat
    thread->start(); // Starts the thread (calling run())
    

    }

    By passing the tela_chat instance to the ThreadChat constructor, you can then use it to emit the signal. This ensures that you have a valid instance to work with when emitting the signal from the nested class.