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

Creating custom slots and signals



  • Hi

    I have a worker thread and a main GUI. The worker thread reads values every 3 seconds.

    I want to emit that value to the main GUI every 3 seconds. How do I do that?

    I tried reading the documentation from qt5 but this is as far as I got:

    cpu_thread.h

    #ifndef CPU_THREAD_H
    #define CPU_THREAD_H
    #include <QThread>
    
    class cpu_thread : public QThread {
        Q_OBJECT
    public:
        cpu_thread();
        void run();
    public slots:
        void setValue(int value);
    
    signals:
        void valueChanged(int value);
    };
    
    #endif // CPU_THREAD_H
    

    cpu_thread.cpp

    #include "cpu_thread.h"
    #include <QDebug>
    #include <QProcess>
    #include <QTest>
    
    cpu_thread::cpu_thread() {
    
    }
    
    void cpu_thread::run() {
        while (true) {
    
            QThread *thread = new QThread;
            QProcess *process = new QProcess;
            process->moveToThread(thread);
            process->start("cat /proc/loadavg");
            process->waitForFinished(-1);
    
            QString stdout = process->readAllStandardOutput().trimmed();
            stdout.truncate(4);
            qDebug() << stdout;
    
            QTest::qWait(3000);
        }
    }
    
    void cpu_thread::setValue(int value) {
    
        emit valueChanged(value);
    }
    

    Now the main gui which is supposed to capture the value and update the gui looks like this:

    #include "ui_mainwindow.h"
    #include <QProcess>
    #include <QDebug>
    #include <QThread>
    #include <cpu_thread.h>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow) {
        ui->setupUi(this);
        cpu_thread *my_thread = new cpu_thread;
        my_thread->start();
        cores = getCpuCores();
    
    }
    
    MainWindow::~MainWindow() {
        delete ui;
    }
    
    void MainWindow::on_actionExit_triggered() {
        qApp->exit();
    }
    
    void MainWindow::mousePressEvent(QMouseEvent *event) {
        m_nMouseClick_X_Coordinate = event->x();
        m_nMouseClick_Y_Coordinate = event->y();
    }
    
    void MainWindow::mouseMoveEvent(QMouseEvent *event) {
        move(event->globalX()-m_nMouseClick_X_Coordinate,event->globalY()-m_nMouseClick_Y_Coordinate);
    }
    
    int MainWindow::getCpuCores() {
    
        QProcess *process = new QProcess;
    
        process->start("nproc");
        process->waitForFinished(-1);
    
        QString stdout = process->readAllStandardOutput().trimmed();
        QString stderr = process->readAllStandardError().trimmed();
    
        int cores = stdout.toInt();
    
        return cores/2;
    }
    
    void MainWindow::test() {
    
        QThread *thread = new QThread;
        QProcess *process = new QProcess;
        process->moveToThread(thread);
        process->start("cat /proc/loadavg");
        process->waitForFinished(-1);
    
        QString stdout = process->readAllStandardOutput().trimmed();
        qDebug() << stdout;
    
    }
    
    void MainWindow::updateCpu(QString value) {
    
        ui->lcdNumber->display(value);
    }```

  • Moderators

    @Fuchsiaff said in Creating custom slots and signals:

    cpu_thread *my_thread = new cpu_thread;
    connect(my_therad, &cpu_thread::valueChanged, this, &MainWindow::someSlotYouCreateInMainWindow);
    my_thread->start()
    

    That should do it on UI side. In your thread, you need to emit the value, that would probably look like:

    qDebug() << stdout; // BTW that's a HORRIBLE variable name!
    emit valueChanged(stdout.toInt());
    QTest::qWait(3000);
    

    But, and that is a BIG but - your code is overdoing it massively.

    You don't need a thread. QProcess already runs in a separate process - it won't block your UI at all! So you can simplify all this like so:

    • remove whole cpu_thread class
    • in MainWindow do this:
    QTimer *timer = new QTimer(this);
    timer->setInterval(3000);
    timer->setSingleShot(false);
    // Using a lambda, but you can (should) create your own slot for this, of course
    connect(timer, &QTimer::timeout, this, [this] {
      QProcess *process = new QProcess(this);
      process->start("cat /proc/loadavg");
      connect(process, &QProcess::finished, this, &MainWindow::someSlot);
    });
    
    // Your slot:
    void MainWindow::someSlot() {
      QProcess *process = qobject_cast<QProcess *>(sender());
      ui->lcdNumber->display(process->readAllStandardOutput());
      process->deleteLater();
    }
    

    Brain to terminal, not tested of course. But it should give you a general idea.


  • Lifetime Qt Champion

    Hi,

    Rather than calling cat, why not just use a QFile and read the content of /proc/loadavg ?



  • Anyway, after using code suggested by sierdzo,

    mainwindow.cpp

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow) {
        ui->setupUi(this);
        QTimer *timer = new QTimer(this);
        timer->setInterval(3000);
        timer->setSingleShot(false);
        // Using a lambda, but you can (should) create your own slot for this, of course
        QObject::connect(timer, &QTimer::timeout, this, [this] {
          QProcess *process = new QProcess(this);
          process->start("cat /proc/loadavg");
          QObject::connect(process, &QProcess::finished, this, &MainWindow::updateCpu);
        });
        cores = getCpuCores();
    
    }
    
    void MainWindow::updateCpu(int exitCode, QProcess::ExitStatus exitStatus) {
    
        QProcess *process = qobject_cast<QProcess *>(sender());
        QString result = process->readAllStandardOutput().trimmed();
        result.truncate(4);
        ui->lcdNumber->display(result);
        process->deleteLater();
    }
    

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMouseEvent>
    #include <QMainWindow>
    #include <QProcess>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        int getCpuCores();
        void updateCpu(int exitCode, QProcess::ExitStatus exitStatus);
        void updateRAM(QString value);
        void updateNetwork(QString value);
        void updateFanSpeed(QString value);
        int cores;
        ~MainWindow();
    
    private slots:
        void on_actionExit_triggered();
    
    private:
        void mousePressEvent(QMouseEvent *event);
        void mouseMoveEvent(QMouseEvent *event);
        int m_nMouseClick_X_Coordinate;
        int m_nMouseClick_Y_Coordinate;
        Ui::MainWindow *ui;
        bool isMouseDown = false;
    
    };
    
    #endif // MAINWINDOW_H
    

    Qtcreator shows me this:

    No matching function call for call to "connect"


  • Moderators

    Hint from @SGaist is far better :-)

    No matching function call for call to "connect"

    Which connect? QtC will show you exactly the line where compilation failed.



  • This part:

     QObject::connect(process, &QProcess::finished, this, &MainWindow::updateCpu);
    

  • Lifetime Qt Champion

    What is the exact error ?



  • /home/bot/Hardware_mon/mainwindow.cpp:19: error: no matching function for call to ‘MainWindow::connect(QProcess*&, <unresolved overloaded function type>, MainWindow*, void (MainWindow::*)(int, QProcess::ExitStatus))’ QObject::connect(process, &QProcess::finished, this, &MainWindow::updateCpu); ^


  • Moderators

    The documentation has the proper code for this connect.



  • So something like this?

    connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
        [=](int exitCode, QProcess::ExitStatus exitStatus){ 
         QProcess *process = new QProcess(this);
          process->start("cat /proc/loadavg");
          QObject::connect(process, &QProcess::finished, this, &MainWindow::updateCpu);
          
     });
    

    that connect looks even more confusing, i'm even more lost now


  • Lifetime Qt Champion

    Hi @Fuchsiaff: Then try this:

    QByteArray avg;
    QFile f("/proc/loadavg");
    if (f.open(QIODevice::ReadOnly)) {
      avg = f.readAll();
      f.close();
    }
    // avg now contains the content of /proc/loadavg
    

  • Lifetime Qt Champion

    QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished)

    reads as: use the overloaded version of finished having the matching signature: QProcess::finished(int, QProcess::ExitStatus)



  • I got it to compile with this code

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow) {
        ui->setupUi(this);
        QTimer *timer = new QTimer(this);
        timer->setInterval(3000);
        timer->setSingleShot(false);
        // Using a lambda, but you can (should) create your own slot for this, of course
        QObject::connect(timer, &QTimer::timeout, this, [this] {
          QProcess *process = new QProcess(this);
          process->start("cat /proc/loadavg");
          qDebug() << "hi";
          QObject::connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(updateCpu(int, QProcess::ExitStatus)));
        });
        cores = getCpuCores();
    
    }
    
    

    But now, looking at the application output section, the string "hi" isn't printed so i'm quessing the timer never reached the timeout signal, but why?


  • Lifetime Qt Champion

    From the code you posted, you don't start timer.


Log in to reply