Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QLabel won't update from Slot



  • Forgive me, I am a complete beginner. I am trying to learn how to receive a message from a client and display on the server ui. I have that figured out. My problem is when trying to update the UI with that message. For right now, I am not worrying about the exact message, but just having the UI update when a message is received. However, my UI will not update. Here is the relevant code:

    Line that calls the slot from another class:

    connect(socket, SIGNAL(readyRead()), &mw, SLOT(fillText()));
    

    mw is a MainWindow Object

    mainwindow.cpp:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    int num;
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        
        itemLabel->setText("Label Created");
    
        ui->gridLayout->addWidget(itemLabel, 0, 0);
        
    
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    void MainWindow::fillText()
    {
        qDebug() << "inside fillText function ";
        num = arc4random();
        itemLabel->setText("updated number to show SLOT is working: " + QString::number(num));
        qDebug() << "item Label's text: " + itemLabel->text();
        this->setStyleSheet("background-color: green;");
        QWidget::repaint();//was a suggestion I saw, but does not work
        
    }
    

    mainwindow.h:

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QLabel>
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
        QLabel *itemLabel = new QLabel();
        QLabel *label2;
    
    private slots:
        void fillText();
        void signalTest();
    private slots:
        
    signals:
        
    
    private:
        Ui::MainWindow *ui;
    };
    #endif // MAINWINDOW_H
    

    Thanks for all the help!!

    quick edit: I know textFill() is called because

    qDebug() << "inside fillText function ";
    

    works, and

    qDebug() << "item Label's text: " + itemLabel->text();
    

    actually prints out "item Label's text: updated number to show SLOT is working: (random num) every time I send a message from my client.


  • Lifetime Qt Champion

    Hi
    in this line
    connect(socket, SIGNAL(readyRead()), &mw, SLOT(fillText()));
    what is mw ?

    anyway, it sounds like the slot works so the connection is ok.

    so which part is not updating?
    itemLabel->setText sounds like its updating as you see the random number ?

    Ahh you mean how to read the actual massage from socket and pass that along ?



  • @mrjj Hello, mw is a MainWindow object I initialized in the header file of this other class.

    So, qDebug tells me that the label's text value is indeed updating. However, in the actual GUI window, the the text for itemLabel remains "Label Created," which according to this line:

    qDebug() << "item Label's text: " + itemLabel->text();

    is not the case.

    No, I have not yet tried to work on the actual message, just trying to get the GUI to update when a message is received is my worry for now.


  • Lifetime Qt Champion

    @rahulb1218

    Hi
    But is this the classic gotcha then?

    You already have one mainwindow, you look at but then create a
    new one called mw (which you don't call show on) and you connect to that
    but its not the one that is actually showing ?

    To fix this , you can connect where you create the "otheclass" that has the socket

    like if you create it in Mainwindow

    TheOTherclass * other = new TheOTherclass(this)
    connect(other->socket, SIGNAL(readyRead()), this, SLOT(fillText()));



  • @mrjj ohhhhh I see what I did now.
    How would I write this line since my other class has a qintptr argument:
    qDebug() << "item Label's text: " + itemLabel->text();

    mythread.h:

    #ifndef MYTHREAD_H
    #define MYTHREAD_H
    #include <QThread>
    #include <QTcpSocket>
    #include <QObject>
    #include "mainwindow.h"
    
    class MyThread : public QThread
    {
        Q_OBJECT
    public:
        explicit MyThread(qintptr ID, QObject *parent = 0);
        void run();
    signals:
        void error(QTcpSocket::SocketError socketerror);
    
    public slots:
        void readyRead();
        void disconnected();
    private:
        QTcpSocket *socket;
        qintptr socketDescriptor;
        MainWindow mw;
    };
    
    #endif // MYTHREAD_H
    
    

    mythread.cpp(contains old connect code, but I will take it out and put connect in MainWindow):

    #include "mythread.h"
    
    
    MyThread::MyThread(qintptr ID, QObject *parent) :
        QThread(parent)
    {
        this->socketDescriptor = ID;
    }
    
    void MyThread::run()
    {
        qDebug() << "Thread started";
        socket = new QTcpSocket();
    
        if(!socket->setSocketDescriptor(this->socketDescriptor))
            {
                // something's wrong, we just emit a signal
                emit error(socket->error());
                return;
            }
        connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
        connect(socket, SIGNAL(readyRead()), &mw, SLOT(fillText()));
        connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
        qDebug() << socketDescriptor << " Client connected";
        exec();
    }
    void MyThread::readyRead()
    {
        QByteArray Data = socket->readAll();
        qDebug() << socketDescriptor << " Data in: " << Data;
        socket->write("Data reply: " + Data);
    }
    void MyThread::disconnected()
    {
        qDebug() << socketDescriptor << " Disconnected";
        socket->deleteLater();
        exit(0);
    }
    
    

    thank you so much!!


  • Lifetime Qt Champion

    Hi
    What about making a new signal like the
    void error(QTcpSocket::SocketError socketerror);

    but then instead

    void DataReady(QString text );

    and then in
    void MyThread::readyRead()
    {
    QByteArray Data = socket->readAll();
    qDebug() << socketDescriptor << " Data in: " << Data;
    socket->write("Data reply: " + Data);
    emit DataReady(QString(Data)); // we got something to show in MainWindow
    }

    and then in Mainwindow (the real one) you connect the new signal DataReady
    to some slot and there you do the itemLabel->text(); thing as MainWindow has that
    Qlabel and it's NOT allowed to fiddle with Widgets across threads and MyThread is such a beast so
    you should not call setText or any function on Widgets that are in MainWindow.

    Also, Do you really need a new Thread ?
    QTcpSocket is already async and will send signal when to read new data so
    it won't block the main GUI thread.
    If you later plan on doing heavy calculation/processing on the incoming data it's fine but
    just had to ask :)



  • @mrjj oh yes that would work, and I could probably manage with one thread but I will keep it like this for now and optimize later.
    So in MainWindow, I would do this:

    MyThread * other = new MyThread(this)
    connect(other, SIGNAL(dataReceived(QString Data??)), this, SLOT(fillText()));

    But how would I initialize “*other” since has a required arg? That is my confusion.

    I need other so I can specify the sender object.

    or do you mean DataReady signal should belong to MainWindow?

    My first QT and c++ project, sorry.


  • Lifetime Qt Champion

    @rahulb1218

    Hi
    Yes but one never shows the param name in the connect so

    MyThread * other = new MyThread(this)
    connect(other, SIGNAL(dataReceived(QString)), this, SLOT(fillText()));

    ( I assume that dataReceived is your name for DataReady example)

    and you should change the filltext from

    void fillText()
    ->
    void fillText(QString data);

    As in - add new parameter to your slot, to use the data.

    • But how would I initialize “*other” since has a required arg? That is my confusion.

    You mean the connect or the
    MyThread * other = new MyThread(this)
    part ?

    • My first QT and c++ project, sorry.
      Then it goes pretty well I must say :)

    When you feel for it, you should have a look at the new way to connect
    https://wiki.qt.io/New_Signal_Slot_Syntax



  • @mrjj

    MyThread * other = new MyThread(this)
    When I use this, I get the "no matching constructor for initialization of "MyThread." I think this is because the param needs to be of type qintptr, right?

    So I tried something. I tried to grab the MyThread object that is initialized in my server files header. I will attach. I used extern and added this line to my mainwindow.cpp. However, this would give me linker error when compiling.

    myserver.h:

    #ifndef MYSERVER_H
    #define MYSERVER_H
    #include <QTcpServer>
    
    class MyServer : public QTcpServer
    {
        Q_OBJECT
    public:
        MyServer(QObject *parent = 0);
        void startServer();
    protected:
        void incomingConnection(qintptr socketDescriptor);
    };
    
    #endif // MYSERVER_H
    
    

    myserver.cpp:

    #include "myserver.h"
    #include "mythread.h"
    
    MyServer::MyServer(QObject *parent) :
        QTcpServer(parent)
    {
    
    }
    void MyServer::startServer()
    {
        int port = 1234;
        if(!this->listen(QHostAddress::Any, port))
            {
                qDebug() << "Could not start server";
            }
            else
            {
                qDebug() << "Listening to port " << port << "...";
            }
    }
    void MyServer::incomingConnection(qintptr socketDescriptor)
    {
        // We have a new connection
        qDebug() << socketDescriptor << " Connecting...";
    
        MyThread *threadly = new MyThread(socketDescriptor, this);
    
        // connect signal/slot
        // once a thread is not needed, it will be beleted later
        connect(threadly, SIGNAL(finished()), threadly, SLOT(deleteLater()));
    
        threadly->start();
    }
    
    

    In mainwindow.cpp, I added

    extern const MyThread* threadly;
    

    and

    connect(threadly, SIGNAL(dataSignal()), this, SLOT(fillText()));
    

    It would give a linker error at this line. I read online that I must #include mainwindow.moc, but QT could not find that file.

    However, if you believe that this is the wrong way to tackle this, and instead initialize another MyThread object in mainwindow, I will do that instead, but the correct way as using (this) as the param does not work for me.

    And again, thank you so much for the help!

    PS: the error detail for using MyThread(this) is: no matching constructor for initialization of 'MyThread'
    note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'MainWindow *' to 'const MyThread' for 1st argument
    note: candidate constructor not viable: no known conversion from 'MainWindow *' to 'qintptr' (aka 'long long') for 1st argument



  • @rahulb1218
    You have said:

    My first QT and c++ project, sorry.

    @mrjj oh yes that would work, and I could probably manage with one thread but I will keep it like this for now and optimize later.

    Why in the world are you soldiering on using threads? They are probably the single hardest part of Qt (or other toolkit) to get right, and you are now having problems with them. I would not dream of using threads if I were new to C++ & Qt.

    I don't know what you are trying to achieve but these Fortune examples do QTcpSocket without any threads:

    If you do want (really need!) to use threads have you had a look at:



  • @JonB

    I'm working on a project where I need to able to send a string from one device to another. So, the first thing I did was look up was examples of one script talking to another. One of the first ones I found used threads. I will however try this, thank you!



  • @rahulb1218
    Depends whether these "scripts" were for Qt or not. Qt's TCP/socket code is inherently asynchronous, whereas other examples/toolkits are likely to be synchronous and so require threads.

    I did not say using threads was necessarily wrong (depends exactly what you have to do in your proposed thread). What I did say is that if I were new to both Qt & C++ I would not choose to use threading if I could possibly help it! :)


Log in to reply