Qt C++: how to add a simple countdown timer? - c++

I'm new to Qt C++ and from the few resources I found online I wasn't able to extract only the bit I need to add a countdown timer to a form. I'm not trying to add any buttons or other functionality. Only need to have a timer starting at 1:00 and then decreasing until 0:00 is reached, at which point I need to show some sort of message indicating the user the time is up. I thought maybe adding a label to display the timer would be a simple way to do it (but now sure if I'm right on this).
So far I created a new Qt Application project, added a label to my main form and added some timer code to mainwindow.cpp from what I got at http://doc.qt.io/archives/qt-4.8/timers.html:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//Initialize "countdown" label text
ui->countdown->setText("1:00");
//Connect timer to slot so it gets updated
timer = new QTimer();
connect(timer, SIGNAL(timeout()), this, SLOT(updateCountdown()));
//It is started with a value of 1000 milliseconds, indicating that it will time out every second.
timer->start(1000);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::updateCountdown()
{
//do something along the lines of ui->countdown->setText(....);
}
In mainwindow.h I added a QTimer *timer; as a public attribute and also void updateCountdown(); as a private slot.
But I'm not exactly sure about how to go on from here. I think the next step is to decrease the timer each second and show that on the "countdown" label (which would be done on the updateCountdown slot) but I can't find out how.
I'm also not sure how to trigger a message (maybe on a QFrame) when the countdown gets to 0:00.

From QTimer documentation, the function updateCountdown() is called every 1 second in your configuration. So you should decrease one second from your timer every time this function is called and update in the UI. Currently you are not storing your time anywhere, so I suggest you add it as a global for now, like QTime time(0, 1, 0) QTime Documentation.
Then inside updateCountdown(), call time.addSecs(-1); and then ui->countdown->setText(time.toString("m:ss"));. Then is easy to check if it is "0:00" and do something else.
I hope this helps

Related

Using QTimer for displaying timer

I created a class called aTimer which inherits from QTimer. I want to be able to store the time elapsed into a variable called TimeElapsed of type int. I then want to have the timer automatically started when the main window opens and for it to display the time elapsed therein.
I think I'm using the wrong timer, and I am quite confused as to which tools to be using within Qt because there are different ways of handling time. Suffice it to say that I want a stop-watch kind of module that allows me to start and stop time manually without a cap (or an interval with the case of Timer). How shall I proceed? So far, attempts to use QTimer are fruitless.
You do not really need a derived class for this task. I'd probably use a QTimer and a QElapsedTimer.
Create them in your main window constructor and set the QTimers interval according to how often the time should be updated. Also connect its timeout() signal to a function updating the displayed value. In this function you can get the elapsed time from the QElapsedTimer and update the display.
// *.h
QTimer* timer;
QElapsedTimer *eltimer;
// *.cpp
constructor(){
this->timer = new QTimer(this);
this->timer->setInterval(1000);
connect(this->timer, SIGNAL(timeout()), this, SLOT(update_ui()));
this->timer->start();
this->eltimer = new QElapsedTimer(this);
this->eltimer->start();
}
SLOT update_ui(){
qint64 msecs_elapsed = this->eltimer->elapsed();
// Insert value into ui object
}
Of course you can create some buttons to start() and stop() the QTimer

QT GUI freezes even though Im running in separate thread

I have a small chat application where I use a SQLite database to store all the conversations. I've noticed that the app freezes randomly, and I then have to minimize and maximize it to make it work again. I thought that the problem might be the SQLite selects / inserts that were causing the gui to freeze. I decided to try and move all the SQLite methods into a separate thread.
After doing so the app still freezes.
Some things that might be worth knowing:
I use QTcpSocket directly in my MainWindow but it seems that there is no use in running the QTcpSocket in a separate thread?
I have separated the SQLite methods into a new thread (see implementation below)
I use 3 WebViews for displaying my chat messages, the entire application GUI is build with these WebViews
Does my code below really run in a separate thread? GUI still freezes.
My header file:
class dbThread : public QObject
{
Q_OBJECT
public:
dbThread(QObject* parent);
public slots:
bool openDB(QString agentID);
signals:
void clearPreviousHistory();
private:
QSqlDatabase db;
QHash<QString, QString> countries;
};
My cpp file:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);
dbtrad->openDB(userID);
connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
thread->start();
}
dbThread::dbThread(QObject * parent): QObject(parent) {
}
bool dbThread::openDB(QString agentID) {
qDebug() << "OPEN DB FROM THREAD ";
// Find QSLite driver
db = QSqlDatabase::addDatabase("QSQLITE");
// ......
}
This is how I call dbThread methods from my MainWindow:
dbtrad->getHistory(channelId);
Edit
New code:
// Start database thread
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);
connect(this, SIGNAL(requestOpenDB(QString)), dbtrad, SLOT(openDB(QString)));
thread->start();
emit requestOpenDB(userID);
dbtrad->openDB(userID); will execute like any normal function (Why should it?), in the GUI thread.
moveToThread allow you to execute slots called using signals in a separate thread.
If you want to execute openDB in the thread you can trigger its execution using
connect (thread, SIGNAL(started()), dbtrad, SLOT(openDBWithUIDAlreadySet()))
or
connect (this, SIGNAL(requestOpenDB(int)), dbtrad, SLOT(openDB(int)))
You need to use existing or additional signals. Qthread::start() emit the signal started(). You can also define
MainWindow{
signals:
void requestOpenDB(int);
void queryHistory(int channelid);
}
and emit the signals manually using
emit requestOpenDB(userID); //for openDB
emit queryHistory(channelId); // for getHistory
the responses from the dbThread object also need to be given using a signal which is connected to a slot. Like a notification.
QTcpSocketdoes indeed not need to be in a separated thread.
as long as all the database access is done from that thread where the database was created it should also be no problem
And now to the fun part: i think you create the database in the main thread ... by calling dbtrad->openDB(userId)
Yes so qt moveToThread() does not do what you are expecting it to do. The function that you are calling from your main thread will get executed in your main thread only. That database access is causing GUI freezes.
moveToThread only moves "event processing" in a seperate thread. Which means any slots of dbThread which are connected using Qt::QueuedConnectionwill get executed in new thread.
Following way will execute getHistory() method in your main ui thread only. You need to create a signal in main thread and make getHistory() a slot of dbThread class. Then connect both.
Reading documentation AND logs is essential!!!
In log you have a warning that YOU CAN"T MOVE TO THREAD IF OBJECT HAVE A PARENT.
Also documentation clearly says that:
Changes the thread affinity for this object and its children. The
object cannot be moved if it has a parent. Event processing will
continue in the targetThread.
Proper way to fix it:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
thread = new QThread(this);
dbtrad = new dbThread(); // NO PARENT
dbtrad->moveToThread(thread);
// run object method in thread assigned to this object:
QMetaObject::invokeMethod(dbtrad, "openDB", Qt::QueuedConnection, Q_ARG(QString, userID));
connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
thread->start();
}
MainWindow::~MainWindow()
{
dbtrad->deleteLater();
thread->quit();
thread->wait(5000); // wait max 5 seconds to terminate thread
}

Qt/C++: Checkable button and infinite loop

I'm coding a Qt Gui and I'm trying to implement a new feature, I now the precise result I want to arrive at but not how to code it.
I'm trying to add a checkable button that when checked would run a function that would only stop when the button is unchecked, but every second a PaintArea I have on the window would be updated (letting me see how the multiple executions of my function are changing my data). It seem that I'll need to use some QThread objects, but just the part dealing with the button is already counter intuitive to me, I've been trying to play with the autoRepeatDelay and autoRepeatInterval without getting my hand on what they do and how they could be useful to me.
I guess that what I'm trying to code is not really original, would have an idea of the steps to implement it, or an example of a code?
Edit:
According to the first answers (thank you for them by the way) my question may not be clear. Putting on the side the thread thing, I'd like to implement an infinite loop that only starts when a pressbutton goes to pressed position (it's a checkable button) and stops only when leaving it. The first version I tried to do (with a while(button->isChecked() loop) would completely freeze as the application would be running the loop, the gui would freeze and the button couldn't be turned off (hence the idea of running it in a separate thread). Voila! I hope it's a clearer formulation. Thank you in advance.
Here's a simple skeleton of something that might work. Without knowing your exact requirements, it may or may not be right for your problem. Hopefully it will give you a few hints that do actually help.
void Ui::buttonPressedSlot(bool checked){
if (checked){
Processor *processor = new Processor;
connect(this, SIGNAL(abortCalculations()), processor, SLOT(abort()), Qt::QueuedConnection);
connect(processor, SIGNAL(updateNeeded()), this, SLOT(updateGui()), Qt::QueuedConnection);
QThreadPool::globalInstance()->start(processor);
} else {
emit abortCalculations(); // this is a signal in your UI class
}
}
You can then use the following for your calculations.
class Processor : public QObject, public QRunnable{ // QObject must always be first in multiple inheritance
Q_OBJECT
public:
~Processor();
void run();
public slots:
void abort();
void doCalculations();
signals:
void updateNeeded(); // connect this to the GUI to tell it to refresh
private:
QScopedPointer<QEventLoop> loop;
};
Processor::~Processor(){
abort();
}
void Processor::run() {
loop.reset(new QEventLoop);
QTimer timer;
connect(&timer, SIGNAL(timeout()), this, SLOT(doCalculations()));
timer.setInterval(1000);
timer.start();
loop->exec();
}
void Processor::abort(){
if (!loop.isNull()){
loop->quit();
}
}
void Processor::doCalculations(){
// do whatever needs to be done
emit updateNeeded();
}
I don't know if I really understand what you want to do, but I will try to answer.
First, you want a Button that send a start & stop info to control a thread. You can use a checkbox to begin. This check box send a signal when its state changes. Connect this signal to a slot that perform start thread and stop according to the boolean sent.
Second, in you thread you need to launch the events loop. After, set a timer that call you repaint after every timeout.
Hope it helped.
PS: take care of execution context with you thread and Qt's objects.

Creating signals and slots qt4 gui builder

Hi im brand new to c++ and trying to get my head around the concepts.
I am creating a very simple app to get going with the help of the tutorials, so im trying to do my own first try.
I'm having problems with the file.h and file.cpp the one besides main.cpp
I would like to click the button in the button box "ok" and have text come up in the text box.
Here is MainWindow.h first
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "ui_MainWindow.h"
class MainWindow: public QMainWindow, private Ui::MainWindow
{
Q_OBJECT
public:
MainWindow(QMainWindow *parent = 0);
private slots:
//here is where im tyring to add a slot.
void on_buttonbox_buttonClicked ( QAbstractButton * );
// void on_inputSpinBox2_valueChanged(int value);
private:
Ui::MainWindow ui;
};
#endif
Next is the MainWindow.cpp
#include <QtGui>
#include "MainWindow.h"
MainWindow::MainWindow(QMainWindow *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
}
//This is where i would like to catch the clicked signal from the ok button and add the text to the text box.
void MainWindow::on_buttonbox_buttonClicked ( QAbstractButton * ){
ui.textEdit->setText(QString::number(16));
}
Im trying to be as simple as posible to just to get it going, it will compile but i can't get the signals and slots to talk, where am i going wrong.... remember brand new.
You have to connect your Slots to the Signals, add this into your constructor:
this->connect(this->ui.yourButton, SIGNAL(clicked()), this, SLOT(on_buttonbox_buttonClicked(QAbstractButton*)));
// ^
// |
// The name of your Button here ...
Also please see here: http://qt-project.org/doc/qt-4.8/signalsandslots.html
Edit:
MainWindow.cpp
MainWindow::MainWindow(QMainWindow *parent) : QMainWindow(parent)
{
ui.setupUi(this);
// Connect Signals-Slots
this->connect(this->ui.yourButton, SIGNAL(clicked()), this, SLOT(on_buttonbox_buttonClicked(QAbstractButton*)));
}
// ...
But don't forget to change yourButton to whatever you've named yours.
Alright, you have to connect the signal to the slot somewhere. You should do this on the constructor,
connect(button, SIGNAL(clicked()), this, SLOT(on_buttonbox_buttonClicked(QAbstractButton *)));
Just remember that the slot only will be called if there is a signal connect to it. Otherwise your button won´t be able to know where to go.
The mechanism of SIGNAL and SLOT is very simple and is used to register a widget(buttons, spinbox etc..) to an event. For example "when I click that button a new window will show up."
That being said, let's see how can we register our SLOT(what to do after receiving the signal) to his SIGNAL(an event: a click, a selection, an edit of a form etc..)
QObject::connect( button , SIGNAL( click()), this , SLOT( openWindow() ))
button is the widget that will throw the signal.
SIGNAL( click()) you are telling that clicking(press and release) button an action will be performed
this is the object that declare the slot
SLOT( openWindow() ) is the method(slot) will be called clicking button
Signals and slots must have same parameters!!
So to answer your question you have to declare a slot with same parameters as the signal. click() has no parameter so you have to declare your slot as:
void on_buttonbox_buttonClicked ()
PS: as I remember there is an issue naming a slot with the prefix on. But I have to do a little search because I don't remember very well.
update: I made a little test and naming the slot with the prefix on_ gives an error message at run time QMetaObject::connectSlotsByName: No matching signal for on_ClickChangeBack() but the slot execute.

QTimer - Repetitive Timer

I'm trying to have an QTimer object count in intervals, continuously to call a function. I followed an example and I have set the intervals but it doesn't appear to start counting ever again.
This is the piece of code I'm working with
QTimer *timer = new QTimer(this);
timer->setInterval(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(MyFunction()));
timer->start();
Is your main loop stil running?
Does the object you reference with "this" is stil existent?
Could you check if the timer is set to single shot?
sorry didn't have the function set to a slot in the header file that was the problem
private slot:
void MyFunction();