0

So I have a simple class below that uses a QTimer to add a number to the total every 1 second:


// score.h

#include <QObject>
#include <QTimer>

class Score : public QObject
{
    Q_OBJECT
public:
    explicit Score(QObject *parent = nullptr);
    void start();
    void stop();
    QTimer *timer;
    int getScore();

private slots:
    void update();

private:
    int score;

};

// score.cpp

#include "score.h"

Score::Score(QObject *parent) : QObject(parent)
{
    score = 0;
    timer = new QTimer(this);
}

void Score::start()
{
    timer->start(1000);
}

void Score::stop()
{
    timer->stop();
}

int Score::getScore()
{
    return score;
}

void Score::update()
{
    score += 10;
    qDebug() << score;
}

// main.cpp

#include <QCoreApplication>
#include "score.h"
#include <QtDebug>
#include <iostream>

int main(int argc, char *argv[])

{
    QCoreApplication a(argc, argv);

    Score score{};
    std::string input{""};

    while(input!="1") {
        std::cout << "1. to stop" << std::endl;
        std::cout << "2. to start" << std::endl;
        std::cin >> input;

        if(input=="1") {
            score.stop();
        }
        else if(input=="2") {
            score.start();
        }
    }
    return a.exec();
}

During the loop, if I press if my input was 2, nothing is happening. The slot doesn't seem to fire as I don't see anything that is outputting to the console. I am pretty new to QT in general so I may have done something incorrect. Another thought I had that it could be due to some threading issues. But I haven't had much experience with threading before.

dtt
  • 248
  • 3
  • 14
Tim
  • 57
  • 2
  • 6
  • The timer cannot fire before you start `a.exec()`. It contains the event-loop which manages the timer (among other things). You have to find out how to handle console input via `QCoreApplication` events. If I choose Qt I do so to develop GUI applications (where input is just reaction on widget events). If I want to do console applications, I wouldn't choose Qt. FYI: [I/O in concurrent program](https://stackoverflow.com/a/48097134/7478597) (With multi-threading, you can achieve processing of user input and a timer at the same time though you still have to use non-standard non-blocking input.) – Scheff's Cat Nov 09 '20 at 06:53

1 Answers1

1

Your implementation has 2 crucial errors:

  • The eventloop is never started since the while loop does not allow it, and if the eventloop is not started then asynchronous elements such as QTimer, signals, etc. will not work.

  • Even so, the eventloop will work. You have not made the connection between the timeout signal with the updata slot.

Considering the above you have to implement the reading of stdin without using a loop and the implementation of the reading will depend on the OS, so to simplify the solution we can use the QConsole library that implements the reading of stdin through signals:

├── 3rdParty
│   └── QConsole
│       ├── Demo
│       │   ├── Demo.pro
│       │   └── main.cpp
│       ├── LICENSE
│       ├── qconsole.cpp
│       ├── qconsole.h
│       ├── qconsole.pri
│       ├── README.md
│       ├── readthread_win.cpp
│       └── readthread_win.h
├── 64746801.pro
├── main.cpp
├── score.cpp
└── score.h

*.pro

QT -= gui

CONFIG += c++11 console
CONFIG -= app_bundle

SOURCES += \
        main.cpp \
        score.cpp

HEADERS += \
    score.h

include(3rdParty/QConsole/qconsole.pri)

main.cpp

#include <QCoreApplication>
#include <QFile>
#include <QDebug>

#include <qconsole.h>

#include "score.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Score score;

    QConsole console;
    QObject::connect(&console, &QConsole::readyRead, [&](){
        auto data = console.read(console.bytesAvailable());
        if(data == QStringLiteral("1\n")){
            score.stop();
        }
        else if(data == QStringLiteral("2\n")){
            score.start();
        }
    });
    if(!console.open()) {
        qCritical() << console.errorString();
        return EXIT_FAILURE;
    }
    return a.exec();
}

score.h

#ifndef SCORE_H
#define SCORE_H

#include <QObject>

class QTimer;

class Score : public QObject
{
    Q_OBJECT
public:
    explicit Score(QObject *parent = nullptr);
    void start();
    void stop();
    int getScore();

private slots:
    void update();

private:
    int score;
    QTimer *timer;
};

#endif // SCORE_H

score.cpp

#include "score.h"

#include <QTimer>
#include <QDebug>

Score::Score(QObject *parent) : QObject(parent)
{
    score = 0;
    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &Score::update);
}

void Score::start()
{
    timer->start(1000);
}

void Score::stop()
{
    timer->stop();
}

int Score::getScore()
{
    return score;
}

void Score::update()
{
    score += 10;
    qDebug() << score;
}

The full example is here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • I've never seen a Qt console application nor have I ever intended to write one. So, I really enjoy this answer with sample to see how it might look like. – Scheff's Cat Nov 10 '20 at 07:31