I am currently attempting to get a QTimer to work and emit the appropriate signals when running in a separate Thread. I'm not sure what I should do in order to get the timeout() signal to be emitted.
When I try using QFuture and QFutureWatcher they do not ever throw their finished() signal as this thread never really ends, it just keeps looping.
I've looked at a number of other questions 1, 2, 3, 4, 5 to no avail.
This is what I currently have. Any advice would be greatly appreciated!
Foo.cpp
Foo::Foo() {
...
mcTimerForSpoilers = new QTimer(this);
connect(mcTimerForSpoilers, SIGNAL(timeout()), this, SLOT(queueDownloadOfSpoilerFile()), Qt::QueuedConnection);
QtConcurrent::run(this, &Foo::manageSpoilerTimer);
}
void Foo::manageSpoilerTimer() {
...
mcTimerForSpoilers->setInterval(5000); // 5 seconds
mcTimerForSpoilers->start();
}
void Foo::queueDownloadOfSpoilerFile() {
...
std::cerr << "Queue Download" << std::endl;
manageSpoilerTimer(); // Restart the timer
}
Your design is wrong. You call QtConcurrent::run(this, &Foo::manageSpoilerTimer);but a timer can only be started from its own thread.
Furthermore, you don't specify an interval, so the timer will keep firing continuously, but then you also keep on starting the timer every time it times out.
It is not clear what you really want to do. It looks like you don't know what you are doing.
I don't think QtConcurrent is a good candidate for downloads to begin with. It doesn't support progress tracking, pausing or cancelling which are all features that downloading stuff should use. You should instead go for a threaded QObject based worker, as described in this answer.
Related
I have the exact same problem as described QThread won't stop / does not process a signal but with a twist. I cannot get it to work even with QCoreApplication::processEvents() to my while loop. I followed this blog post and ended up like as follows:
MediaController.cpp
void MediaController::buttonPlayClicked(bool checked)
{
if (checked)
{
m_loopThread = new QThread;
m_playPauseworker = new PlayPauseWorker;
m_playPauseworker->moveToThread(m_loopThread);
connect(m_playPauseworker, &PlayPauseWorker::dataReady, this, &MediaController::onDataReady);
connect(m_loopThread, &QThread::started, m_playPauseworker, &PlayPauseWorker::process);
connect(m_playPauseworker, &PlayPauseWorker::finished, m_loopThread, &QThread::quit); // <-- never works
connect(m_playPauseworker, &PlayPauseWorker::finished, m_playPauseworker, &PlayPauseWorker::deleteLater);
connect(m_loopThread, &QThread::finished, m_loopThread, &QThread::deleteLater);
m_loopThread->start();
}
else
{
m_loopThread->requestInterruption();
}
}
The above slot is called every time play/pause checkable button is clicked. Thread and worker are created on the main thread.
PlayPauseWorker.cpp
void PlayPauseWorker::process()
{
while (!QThread::currentThread()->isInterruptionRequested())
{
// heavy computations
emit dataReady(std::tuple<QImage, QImage, QImage>>); // <-- works!
QCoreApplication::processEvents(); // <-- doesn't help with quit()
}
emit finished(); // <-- emits the signal but quit() not called
}
On further processing, when i access wait() to see if my thread has exited, it never returns.
In addition to quit() slot never called, i noticed my GUI becomes very choppy when the thread is running. I sometimes can/cannot click on other buttons or play with UI. I went through a lot of SO posts but couldn't figure out a way to cleanly exit the thread whenever i need to. Not sure where am i going wrong.
After trying out a lot of suggestions i still couldn't figure out why emit finished() doesn't quit the thread. So, as a last resort, i used this->thread->quit() to achieve the same.
But thanks to all the commenters here, i narrowed down on laggy/choppy gui issue.
My emit data(std::tuple<QImage, QImage, QImage>>) was setting pixmap on QGraphicsScene on a loop without removing the already set pixmap on the scene leading to huge memory leak and ultimately crash.
I wrote a thread that executes a worker object. Everything works fine. Also the resulting signals are emitted as they should. Of course I took care of the usual mistakes regarding thread/object affinity.
Today I wrote an automated module test for those workers/threads. I created a QSignalSpy to wait for a signal that is emitted by the worker object (which was moved to the thread) like this:
QSignalSpy spy(worker, SIGNAL(Success()));
thread.ExecuteWorker();
QVERIFY(spy.wait()); // Error in this line
I am getting a well known error in the marked line:
QObject::killTimer: timers cannot be stopped from another thread
First I exepected an error on my side because some code in wait() was executed in the wrong thread. Then I found the following code in the implementation of QSignalSpy:
if (!QMetaObject::connect(obj, sigIndex, this, memberOffset, Qt::DirectConnection, 0))
{
qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
This obviously means QSignalSpy uses DirectConnection all time and can not be used to monitor signals of objects living in different threads.
Why did they program it that way in Qt5.3? Is that a mistake or is it intended behaviour? How can I work around this limitation?
This is unfortunately a long-standing issue, more than six years to be fair:
QSignalSpy crashes if signal is emitted from worker thread
I met Jason at the Qt Contributor Summit a couple of years ago, but then he left Nokia not much after that as Nokia closed the Brisbane office where he was working. After that, there was not much contribution going on in this test module of Qt, sadly.
There was recently more discussion about it on the mailing list, too:
Why is QSignalSpy using Qt::DirectConnection?
The solution proposed by Roland was this that the maintainer, Thiago, also accepted:
if (thread() != QThread::currentThread())
{
QMetaObject::invokeMethod(this, "exitLoop", Qt::QueuedConnection);
return;
}
It is a bit of shame really that this did not go in before 5.4. Having said that, this will be fixed for Qt 5.4 as the change was merged:
Make QTestEventLoop::exitLoop() thread-safe
In order to make QSignalSpy work reliably across threads I use the following approach: I move the spy to the worker thread and I re-implement the wait function as follows:
#include <QSignalSpy>
#include <QTime>
struct ThreadsafeQSignalSpy : QSignalSpy
{
template <typename Func>
ThreadsafeQSignalSpy(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func signal0)
: QSignalSpy(obj, signal0)
{}
bool wait(int timeout)
{
auto origCount(count());
QTime timer;
timer.start();
while (count() <= origCount && timer.elapsed() < timeout)
QCoreApplication::instance()->processEvents(QEventLoop::AllEvents, timeout/10);
return count() > origCount;
}
};
void TestSuite::testFunction()
{
QThread thread;
...
ThreadsafeQSignalSpy spy;
spy.moveToThread(thread);
/// now wait should work
...
QVERIFY(spy.wait(1000));
}
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.
I have been learning Qt for a while and a few days ago I decided to go on multi-threading by QThread. I have been following this person. Since he says that subclassing QThread is not the appropriate way but by using QObject is. I am following his method. But he has created another class which inherits from QObject. I decided to make a few changes:
class myclass
{
private:
dowork();
private slots:
slota();
slotb();
slotc();
}
myclass::dowork()
{
QThread lett;
QThread latt;
QThread pltt;
QObject lethread;
connect(&lett, SIGNAL(started()), this, SLOT(slota()));
lethread.moveToThread(&lett);
QObject lathread;
connect(&latt, SIGNAL(started()), this, SLOT(slotb()));
lathread.moveToThread(&latt);
QObject plthread;
connect(&pltt, SIGNAL(started()), this, SLOT(slotc()));
plthread.moveToThread(&pltt);
lett.start();
latt.start();
pltt.start();/*
lett.wait();
latt.wait();
pltt.wait();*/
while(lett.isRunning())
{
//qDebug() << "Still Running!";
}
lett.quit();
}
If I run the program it keeps running while loop infinitely! It doesn't come out of that at all.
I used qdebug inside the implementation of the 3 slots to check whether it is entering them or not, and surprisingly none of the slots gets called. Why is that so?
If I remove the comments for .wait then the program pauses.
What is wrong in this method? I want to avoid that extra class as my program is very small.
while(lett.isRunning())
{
//qDebug() << "Still Running!";
}
lett.quit();
Terribly wrong. The current thread is busy waiting and will be eating all the time. But this is not your problem. Your problem is certainly that the main event loop has probably not started yet, ie you didn't do QApplication.exec() yet.
Qt uses events to start threads, and when you do use movetoThread, start, specific events are queued in the current thread (ie the thread executing these methods). If the main event loop has already started, then will be processed as soon as they can. But in your case I bet that you main function is :
int main(){
QApplication app();
myclass onlyclassIwanttomake;
onlyclassIwanttomake.dowork();
app.exec(); <----HERE!!!
}
The thread will be started when the main thread executes app.exec();. When you are busy waiting, you are preventing the main thread to process the events required to start your
events.
EDIT: Well, another issue is that
lett.start();
latt.start();
pltt.start();
start 3 threads which are started, and stay idle forever. They are not doing anything, but they are running (even if you remove your loop).
This is a entry written about Qt threading. Please take the time to read it.
I have a case where a signal is being lost and I don't understand why -- normally signals sent prior to the event loop starting just get queued up and sent then.
This is what the code looks like (for a QThread object):
void OffloadHandler::run()
{
cout << "Start" << endl;
connect( this, SIGNAL(loopStarted()), SLOT(onLoopStarted()), Qt::QueuedConnection );
emit loopStarted();
exec();
}
void OffloadHandler::onLoopStarted()
{
cout << "Here!" << endl;
}
The thread is started elsewhere and Start is written to the console but Here1 never is -- the signal is not received. I use the same pattern in my main message loop and it works, but in this threaded message loop it appears not to work.
Is there anything clearly wrong in my code here?
Your code is valid and it should run. Are you sure you have an event loop running in the thread that oh is created in?
Cause the emit loopStarted() should send an event to oh's event loop, which will be processed and will call onLoopStarted(). I've tested your code out, and it works for me.
Btw, It's generally recommended that you do not add slots to your QThread, and avoid using moveToThread( this );
Unfortunately, I don't really understand your use case, so I can't give a better solution. But here is some amazing documentation which has nice DOs and DONTs regarding QThreads.
Okay, I've figured it out, I've been bitten by the QThread ownership oddity. One has to be really careful when connecting to the QThread object itself since that Object is not owned by the thread by default.
So at the point where the thread is created I must move the thread to the thread:
OffloadHandler * oh = new OffloadHandler();
oh->moveToThread( oh ); //MOVE TO SELF!
oh->start();
Once I do this the signals work as expected.