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

My slot function is not being recognised. "No such slot".



  • I have the following code:

    main.cpp

    #include "mainwindow.h"
    
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
    }
    
    

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include "ui_mainwindow.h"
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        void updateTimeLabel(std::string current_time);
        void updateTime(std::chrono::time_point<std::chrono::high_resolution_clock> start);
        ~MainWindow();
    
    
    
    private slots:
        void on_startButton_clicked();
        void on_stopButton_clicked();
    
    private:
        Ui::MainWindow *ui;
    };
    #endif // MAINWINDOW_H
    

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <iostream>
    #include <chrono>
    #include <iostream>
    #include <string>
    #include <fstream>
    #include <QDebug>
    #include <QStandardPaths>
    #include <QTimer>
    
    
    
    std::string readTimeFromFile() {
        std::fstream timefile;
        std::string filepath = "C://Users//Byte//Desktop//timelog.txt";
        timefile.open(filepath);
        if(!(timefile.is_open())) {
            qDebug() << "File not opening";
        }
    
        std::string thetime;
        std::getline(timefile, thetime);
        timefile.close();
        return thetime;
    
    }
    
    void MainWindow::updateTimeLabel(std::string current_time) {
    
        int seconds = stoi(current_time);
        std::string formattedTime;
        int minutes = seconds / 60;
        int hours = minutes / 60;
        seconds %= 60;
        minutes  %= 60;
        formattedTime += std::to_string(hours);
        formattedTime += ":";
        formattedTime += std::to_string(minutes);
        formattedTime += ":";
        formattedTime += std::to_string(seconds);
        QString timeToDisplay = QString::fromStdString(formattedTime);
        ui->timeLabel->setText(timeToDisplay);
    
    }
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
    
        ui->setupUi(this);
        auto current_time = readTimeFromFile();
        updateTimeLabel(current_time);
    
    }
    
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::updateTime(std::chrono::time_point<std::chrono::high_resolution_clock> start) {
        auto elapsed = std::chrono::system_clock::now() - start;
        auto secondsElapsed = std::chrono::duration_cast<std::chrono::seconds>(elapsed).count();
        std::string current_time = readTimeFromFile();
        int temp = stoi(current_time);
        auto new_time = secondsElapsed += temp;
        updateTimeLabel(std::to_string(new_time));
    }
    
    void MainWindow::on_startButton_clicked()
    {
        auto start = std::chrono::system_clock::now();
        // calculate time elapsed, add to file time, update time
        QTimer *myTimer = new QTimer(this);
        connect(myTimer, SIGNAL(timeout()), this, SLOT(updateTime(start))); // problem here, connect is not recognising my slot
        myTimer->start();
    
    }
    
    void MainWindow::on_stopButton_clicked()
    {
        // write to file, close file
    }
    
    

    For some reason I constantly get the error: "QObject::connect: No such slot MainWindow::updateTime(start) in ..\testProg\mainwindow.cpp:78
    QObject::connect: (receiver name: 'MainWindow')"

    The problem is with the QTimer object in the startButton_clicked() function. Part of me thinks that I am using the SIGNAL() part completely wrong as I'm passing a variable and some of the examples I've seen just pass a type? How exactly do I pass an argument to the function and how exactly can I get it to recognize my slot function? Thanks for any help.


  • Lifetime Qt Champion

    @bytebaron said in My slot function is not being recognised. "No such slot".:

    updateTime(std::chrono::time_pointstd::chrono::high_resolution_clock)

    This is your slot, not updateTime(start).
    Please use the new signal/slot syntax - then you a) don't have to specify the arguments (only if you've overloaded signals or slots) and b) will get a compiler instead a runtime warning when something doesn't match.

    /edit: and why are you using the std stuff instead QElapsedTimer + QString - we're a Qt forum :)



  • @bytebaron
    Once you have resolved from @Christian-Ehrlicher 's post:
    In on_startButton_clicked(), are you aware that your QTimer *myTimer = new QTimer(this); leaks?



  • @christian-ehrlicher said in My slot function is not being recognised. "No such slot".:

    @bytebaron said in My slot function is not being recognised. "No such slot".:

    updateTime(std::chrono::time_pointstd::chrono::high_resolution_clock)

    This is your slot, not updateTime(start).
    Please use the new signal/slot syntax - then you a) don't have to specify the arguments (only if you've overloaded signals or slots) and b) will get a compiler instead a runtime warning when something doesn't match.

    /edit: and why are you using the std stuff instead QElapsedTimer + QString - we're a Qt forum :)

    I'm not sure where the newest syntax is, I'm a qt/c++ newb. Is it something similar to this:

    connect(myTimer, &QTimer::timeout, updateTime());
    

    I'm looking to try and pass the start variable to the function if I can. Also I'm new to QT so I'm just getting used to the library bit by bit atm but I hope to do away with the std stuff :)



  • @jonb said in My slot function is not being recognised. "No such slot".:

    @bytebaron
    Once you have resolved from @Christian-Ehrlicher 's post:
    In on_startButton_clicked(), are you aware that your QTimer *myTimer = new QTimer(this); leaks?

    A memory leak? How so?


  • Moderators

    @bytebaron said in My slot function is not being recognised. "No such slot".:

    I'm not sure where the newest syntax is, I'm a qt/c++ newb. Is it something similar to this:

    connect(myTimer, &QTimer::timeout, updateTime());
    

    I'm looking to try and pass the start variable to the function if I can.

    See https://doc.qt.io/qt-5/signalsandslots-syntaxes.html, especially the "Making Connections to Lambda Expressions" section.

    A memory leak? How so?

    Every time you click the start button, you create a new QTimer object. The old one is left in memory, undeleted.



  • @jksh said in My slot function is not being recognised. "No such slot".:

    @bytebaron said in My slot function is not being recognised. "No such slot".:

    I'm not sure where the newest syntax is, I'm a qt/c++ newb. Is it something similar to this:

    connect(myTimer, &QTimer::timeout, updateTime());
    

    I'm looking to try and pass the start variable to the function if I can.

    See https://doc.qt.io/qt-5/signalsandslots-syntaxes.html, especially the "Making Connections to Lambda Expressions" section.

    A memory leak? How so?

    Every time you click the start button, you create a new QTimer object. The old one is left in memory, undeleted.

    I took your advice but I'm unable to get it working still. From what I've researched, the slot is attached to the QTimer object(???) so all of MainWindow's elements objects/widgets go out of scope(???) so I am unable to access the label again in my slot function updateLabel(). Also, to fix the leak issue, is it worth creating the QTimer once in the MainWindow constructor or perhaps making it a smart pointer? I made a seperate barebones project to try and understand things and not get cluttered. My code is like so right now:

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QTimer>
    #include <chrono>
    #include <QString>
    #include <QDebug>
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void updateLabel(std::chrono::time_point<std::chrono::high_resolution_clock> start) {
        auto elapsed = std::chrono::system_clock::now() - start;
        auto secondsElapsed = std::chrono::duration_cast<std::chrono::seconds>(elapsed).count();
        std::string theSeconds = std::to_string(secondsElapsed);
        QString timeToDisplay = QString::fromStdString(theSeconds);
        ui->testLabel->setText(timeToDisplay);
    }
    
    
    void MainWindow::on_pushButton_clicked()
    {
        auto start = std::chrono::system_clock::now();
        QTimer *myTimer = new QTimer(this);
        connect(myTimer, &QTimer::timeout, [=] { emit updateLabel(start);}); // problem here, connect is not recognising my slot
        myTimer->start();
    }
    
    

    main.cpp

    #include "mainwindow.h"
    
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
    }
    
    

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    signals:
        void updateLabel(std::chrono::time_point<std::chrono::high_resolution_clock> start);
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private slots:
        void on_pushButton_clicked();
    
    private:
        Ui::MainWindow *ui;
    };
    #endif // MAINWINDOW_H
    
    


  • @bytebaron

    Also, to fix the leak issue, is it worth creating the QTimer once in the MainWindow constructor

    Yes, or maybe the simplest all round is just to have a member variable QTimer myTimer in MainWindow. Since that's not a pointer and not using new it never needs deleteing.


  • Lifetime Qt Champion

    @bytebaron said in My slot function is not being recognised. "No such slot".:

    connect(myTimer, &QTimer::timeout, [=] {

    Try capturing "this"

    connect(myTimer, &QTimer::timeout, [=, this] {....

    and see if it then accepts your slot.


  • Lifetime Qt Champion

    Hi,

    While it's not strictly necessary with the new connection syntax, it's mandatory to declare your slots properly in your header for the old to work (as well as for QML). Note that even if not strictly necessary, it's good to properly separate standard functions and slots so it makes their intent clear.


  • Moderators

    @bytebaron said in My slot function is not being recognised. "No such slot".:

    From what I've researched, the slot is attached to the QTimer object(???)

    The QTimer object emits the QTimer::timeout() signal when the time is up.

    You want to implement a slot function that should be called when the time is up.

    You connect the signal to the slot. Then, when the time is up, the QTimer object emits the QTimer::timeout() signal; when Qt detects this signal, it runs your slot.

    so all of MainWindow's elements objects/widgets go out of scope(???) so I am unable to access the label again.

    The term "go out of scope" refers to what happens when you reach the end of a function block. For example:

    {
        auto elapsed = std::chrono::system_clock::now() - start;
        auto secondsElapsed = std::chrono::duration_cast<std::chrono::seconds>(elapsed).count();
        std::string theSeconds = std::to_string(secondsElapsed);
        QString timeToDisplay = QString::fromStdString(theSeconds);
        ui->testLabel->setText(timeToDisplay);
    
       // The end of block is here
    }
    

    Local variables get destroyed when they go out of scope. Member variables do not. This means the MainWindow's elements do not go out of scope.

    Also, to fix the leak issue, is it worth creating the QTimer once in the MainWindow constructor

    Yes.

    or perhaps making it a smart pointer?

    A smart pointer will not help you here.

    I made a seperate barebones project to try and understand things and not get cluttered.

    This is a good practice.

    signals:
        void updateLabel(std::chrono::time_point<std::chrono::high_resolution_clock> start);
    

    ...

    connect(myTimer, &QTimer::timeout, [=] { emit updateLabel(start);});

    You have now created an updateLabel signal. Your code says, "When the QTimer::timeout() signal gets emitted, emit the updateLabel(std::chrono::time_point<std::chrono::high_resolution_clock>) signal".

    However, this new signal is not connected to any slot, so nothing will happen when it is emitted.

    I presume you want this:

    // NOTE: You don't need a separate updateLabel() function!
    connect(myTimer, &QTimer::timeout, [=] {
        auto elapsed = std::chrono::system_clock::now() - start;
        auto secondsElapsed = std::chrono::duration_cast<std::chrono::seconds>(elapsed).count();
        std::string theSeconds = std::to_string(secondsElapsed);
        QString timeToDisplay = QString::fromStdString(theSeconds);
        ui->testLabel->setText(timeToDisplay);
    });
    

    But your life will be much simpler if you use QElapsedTimer instead of doing the manual calculations: https://doc.qt.io/qt-5/qelapsedtimer.html

    void updateLabel(std::chrono::time_point<std::chrono::high_resolution_clock> start) {
        auto elapsed = ...
    

    Note 1: Here, you have defined a standalone function. void updateLabel(...) is not the same as void MainWindow::updateLabel(...)! Since this is not a member function of MainWindow, it cannot access the MainWindow's internal variables.

    Note 2: In mainwindow.h, you defined MainWindow::updateLabel(...) as a signal. However, in mainwindow.cpp, you tried to implement it as a slot.


Log in to reply