EDIT 3: When I first asked this question I thought it was because the QTimer
was only firing once; however it turned out to be because I was observing it based on its remainingTime()
member. It turns out the real problem is that remainingTime()
counts down to 0
only once (and the timeout()
signal is indeed firing multiple times).
I have a single-threaded Qt application, which has a timer that needs to be repeatedly called. I also have a progress bar to display how much time is left in the main timer, and the progress bar is updated every 40ms by another timer.
Currently, the main timer is set to 15 seconds (15 x 1000 = 15000 ms), but the timer only fires its QTimer::timeout()
event once. After that, remainingTime()
always returns 0
, even though isActive()
returns true
. (isSingleShot()
returns false
, as well.)
Below is the relevant code:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
interval_capture(15.00),
timer_capture(new QTimer(this)),
timer_updateProgress(new QTimer(this)),
isRecording(false)
{
ui->setupUi(this);
QObject::connect(timer_updateProgress, &QTimer::timeout,
this, &MainWindow::update_progress);
// I had to add the following line to force the timer to be called repeatedly
//QObject::connect(timer_capture, &QTimer::timeout,
// timer_capture, static_cast<void (QTimer::*)()>(&QTimer::start));
timer_capture->setInterval(static_cast<int>(round(interval_capture * 1000)));
timer_capture->setSingleShot(false);
timer_updateProgress->setInterval(40);
timer_updateProgress->setSingleShot(false);
}
// -snip-
void MainWindow::update_progress()
{
// The math does work
double time_passed =
static_cast<double>(interval_capture) -
static_cast<double>(timer_capture->remainingTime())/1000.0;
double fraction_passed =
time_passed / static_cast<double>(interval_capture);
int percentage = static_cast<int>(round(100 * fraction_passed));
ui->progressBar_timer->setValue(percentage);
// I only get an output of "tick: 0" after the first timeout
if (timer_capture->isSingleShot() || !timer_capture->isActive()) {
ui->progressBar_timer->setFormat("it ded");
} else {
ui->progressBar_timer->setFormat("tick: " + QString::number(timer_capture->remainingTime()));
}
}
// -snip-
void MainWindow::on_button_start_clicked()
{
isRecording = !isRecording;
switch (isRecording) {
case true :
ui->button_start->setIcon(QIcon(":/icons/stop.png"));
timer_capture->start();
timer_updateProgress->start();
break;
case false :
ui->button_start->setIcon(QIcon(":/icons/record.png"));
timer_capture->stop();
timer_updateProgress->stop();
break;
}
}
The weird thing is, I know that timer_updateProgress
does work (because I can see the progress bar updating), and it's initialized in basically the same way...
EDIT: To clarify, I am sure that all my other logic is functioning correctly, because I can see in the debugger that:
timer_capture.isSingleShot()
isfalse
timer_capture.remainingTime()
is0
timer_capture.isActive()
istrue
- all results of calculations are correct (e.g.
time_passed
)
And I can also see that the countdown works once and then stops.
EDIT 2: I added the following code to the end of update_progress()
to further illustrate what is happening:
qDebug() << "Active: " << timer_capture->isActive();
qDebug() << "Single shot: " << timer_capture->isSingleShot();
qDebug() << "Remaining ticks:" << timer_capture->remainingTime() <<
" / " << timer_capture->interval() << "\n";
The output I'm getting is:
Active: true
Single shot: false
Remaining ticks: 1496 / 15000
Active: true
Single shot: false
Remaining ticks: 996 / 15000
Active: true
Single shot: false
Remaining ticks: 494 / 15000
Active: true
Single shot: false
Remaining ticks: 3 / 15000
Active: true
Single shot: false
Remaining ticks: 0 / 15000
Active: true
Single shot: false
Remaining ticks: 0 / 15000
(ad infinitum)
QTimer
doesn't actually do anything if there is no slot connected to it? If it starts working when you do connect a slot, it looks like that might be what is happening.&MainWindow::update_progress()
(uncommented) but it does work when it's connected to&QTimer::start()
(as expected). I tried connecting it toupdate_progress()
because that would make it basically identical totimer_updateProgress
.timer_updateProgress
? Just have it increment the progress bar one tick every timeout. Are you running in a debugger or just observing the UI changing? Running in a debugger would help a lot, so you could see the results of your calculations.remainingTime
is broken, but that doesn't affect how the timer fires. Funny thing is that I've always used a separateQElapsedTimer
to see how much time has really elapsed since the timer was started - my code had long life in pre-Qt5-times, so I've never run into that. But I thank you for having me notice theremainingTime()
member, it'll be useful once it works :)