I have a class that should run in a thread and needs an event loop for the slots, currently I run it nicely with moveToThread(), but I'd like to use QThreadPool and I have encountered a problem.
When run with QThreadPool the run() method of my runnable is called from a pooled thread (I check this with QThread::currentThread()), but my slots aren't running in the pooled thread, so I think the object isn't moved to a thread in the pool.
I think this because I know the slots are run in the receiver's thread, which is exactly the (correct) behaviour I get when using the moveToThread() method and a QThread.
How do I get my QRunnable (Foo in the example below) to be run entirely in the pooled threads?
Or is it something I'm doing wrong or understood wrong?
The following POC demonstrates the problem:
foo.h
#ifndef FOO_H
#define FOO_H
#include <QObject>
#include <QRunnable>
#include <QEventLoop>
class Foo : public QObject, public QRunnable
{
Q_OBJECT
public:
explicit Foo(int data, QObject *parent = 0);
void run();
signals:
void startWorking();
public slots:
void doWork();
private:
QEventLoop eventLoop;
int data;
};
#endif // FOO_H
foo.cpp
#include "foo.h"
#include <QThread>
#include <QDebug>
Foo::Foo(int d, QObject *parent) :
QObject(parent), eventLoop(this), data(d)
{
}
void Foo::run()
{
qDebug() << "run() in: " << QThread::currentThread();
connect(this, SIGNAL(startWorking()), this, SLOT(doWork()));
emit startWorking();
eventLoop.exec();
}
void Foo::doWork()
{
qDebug() << "doWork() in: " << QThread::currentThread();
}
main.cpp
#include <QCoreApplication>
#include <QThreadPool>
#include "foo.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Foo *foo = new Foo(42);
QThreadPool::globalInstance()->start(foo);
return a.exec();
}
Please note, however, that in my real code the signal won't be emitted right away, because it will be after I receive some data on the network.
PS: The POC can also be found here.
Maybe you could split your logic in class Foo into two:
the hoster QRunnable with a QEventLoop, and a worker QObject, which you create on the worker thread in run() before calling QEventLoop::exec method. Then you forward all the signals to the worker object.
So now your slots will be called on the pooled thread.
However, QThreadPool is designed for executing lots of short tasks without creating too many simultaneous threads. Some tasks are enqueued and are waiting for others to finish. If this is not your intention, you might want to go back to good old QThread and use it instead.
You can support both modes but it will require some coordination from the outside. My strategy is to emit a signal from inside QRunnable::run passing the current thread. When you plan to use it in a thread pool, use a Qt::BlockingQueuedConnection on this signal and do your moveToThread there. Otherwise, move it to the QThread and emit a signal to start working as usual.
TaskRunner.h
#pragma once
#include <QObject>
#include <QRunnable>
#include <QThread>
class TaskRunner : public QObject, public QRunnable
{
Q_OBJECT
public:
TaskRunner(int data, QObject* parent = nullptr);
void run() override;
Q_SIGNALS:
void start();
void starting(QThread*);
void stop();
private:
int data;
};
TaskRunner.cpp
#include "TaskRunner.h"
#include <QEventLoop>
#include <stdexcept>
TaskRunner::TaskRunner(int data, QObject* parent)
: QObject(parent), data(data)
{
// start should call run in the associated thread
QObject::connect(this, &TaskRunner::start, this, &TaskRunner::run);
}
void TaskRunner::run()
{
// in a thread pool, give a chance to move us to the current thread
Q_EMIT starting(QThread::currentThread());
if (thread() != QThread::currentThread())
throw std::logic_error("Not associated with proper thread.");
QEventLoop loop;
QObject::connect(this, &TaskRunner::stop, &loop, &QEventLoop::quit);
// other logic here perhaps
loop.exec();
}
main.cpp
#include <QCoreApplication>
#include <QThreadPool>
#include "TaskRunner.h"
// comment to switch
#define USE_QTHREAD
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
auto runner = new TaskRunner(42);
#ifdef USE_QTHREAD
// option 1: on a QThread
auto thread = new QThread(&a);
runner->moveToThread(thread);
QObject::connect(thread, &QThread::finished, runner, &QObject::deleteLater);
Q_EMIT runner->start();
// stop condition not shown
#else
// option 2: in a thread pool
QObject::connect(
runner, &TaskRunner::starting,
runner, &QObject::moveToThread,
Qt::BlockingQueuedConnection);
QThreadPool::globalInstance()->start(runner);
// stop condition not shown
#endif
return a.exec();
}
Since the your connect call
connect(this, SIGNAL(startWorking()), this, SLOT(doWork()));
used the default parameter for connection type, it will be a Qt::Autoconnection.
The signal is emitted from the pooled thread, and the slot still belongs to foo, which has a thread affinity to the main thread. The autoconnection will decide to put the slot in the event queue of the main thread.
There are two ways you can fix this:
1.
connect(this, SIGNAL(startWorking()), this, SLOT(doWork()), Qt::DirectConnection);
and remove the eventloop.exec();
2.
in the run method, move the foo object to the current thread before connecting the signal and slot.
Related
Hello Im trying to print my result from a thread to textBrower in qtwidget QT but I cant, either I get error or the program wont compile
is there another way ?? how can I change the textBrowser outside of the class's function??
PS BTW I need to keep looping inside the thread cuz actually im getting some data from uart in final program (in this code i just wanna print "lol" which eventually I wanna change it with some other code which take data ) so I cant come out of the loop
eventually i want use some process from another library and show the resault REAL TIME
bool stop;
out::out(QWidget *parent) :
QWidget(parent),
ui(new Ui::out)
{
ui->setupUi(this);
ui->textBrowser->setText("lol");
}
out::~out()
{
delete ui;
}
///////////////////////////////////////////////
class t : public out, public QThread
{
public:
void run()
{
while(1){
qDebug()<<"hi"<<i;
// ui->textBrowser->setText("lol"); I tried to use this but it didnt worked
if(stop==true){
QThread::exec();
}
}
}
};
void mamad1(void){ //this function get called from another cpp file and its for starting the thread
stop=false;
t *b = new t;
b->start();
}
void out::on_pushButton_clicked() // a push button to stop the thread
{
stop=true;
}
I tried make ui in out.h file a public property and use ui->textBrowser->setText("lol"); but it didn't worked the program freezed and i got this
error : (Parent is QTextDocument(0x208d812a510), parent's thread is QThread(0x208d6816de0), current thread is QThread(0x208dac94e10)
I tried to use connect() also and didn't worked or I didn't use it right
The concept of QT the GUI thread is called the master thread. This thread has an event queue. In general, this event queue is populated by internal and external events. Moreover, the queue between threads is an efficient approach for thread communication. The same is true for the worker threads as well. When you call the start method through the worker threads their signal queue is created and ready to be consumed by the corresponding thread.
Let's come back to your question. The solution is simple. From your worker threads, you can just send a signal to the master thread. The master thread will see this event that modifies the GUI item and forwards this signal/event to the slot that is responsible to take action accordingly. Just define a signal in your worker thread and connect your master thread to this signal with a given slot.
UPDATE FOR SOLUTION
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <MyWorkerThread.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void setWorkerThread(MyWorkerThread* thread);
void connectToSignals();
public slots:
void handleTextBoxSignal(const QString& text);
private:
Ui::MainWindow *ui;
MyWorkerThread* m_worker_thread{nullptr};
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
void MainWindow::setWorkerThread(MyWorkerThread* thread)
{
m_worker_thread = thread;
}
void MainWindow::connectToSignals()
{
QObject::connect(m_worker_thread,
SIGNAL(changeTextOnUI(QString)),
this,
SLOT(handleTextBoxSignal(QString)));
}
void MainWindow::handleTextBoxSignal(const QString& text)
{
qDebug() << "Text box change signal received with text: " << text;
auto text_box = findChild<QTextEdit*>("myTxtEdit");
if(text_box != nullptr)
{
text_box->setText(text);
}
}
MainWindow::~MainWindow()
{
delete ui;
}
MyWorkerThread.h
#ifndef MYWORKERTHREAD_H
#define MYWORKERTHREAD_H
#include <QObject>
#include <QThread>
class MyWorkerThread : public QThread
{
Q_OBJECT
public:
MyWorkerThread(QObject* parent = nullptr);
void stopThread();
signals:
void changeTextOnUI(const QString& text);
private:
void run() override;
bool m_exit{false};
};
#endif // MYWORKERTHREAD_H
MyWorkerThread.cpp
#ifndef MYWORKERTHREAD_H
#define MYWORKERTHREAD_H
#include <QObject>
#include <QThread>
class MyWorkerThread : public QThread
{
Q_OBJECT
public:
MyWorkerThread(QObject* parent = nullptr);
void stopThread();
signals:
void changeTextOnUI(const QString& text);
private:
void run() override;
bool m_exit{false};
};
#endif // MYWORKERTHREAD_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
MyWorkerThread* worker_thread = new MyWorkerThread();
w.setWorkerThread(worker_thread);
w.connectToSignals();
worker_thread->start();
w.show();
return a.exec();
}
In the above code, you see the full solution to your problem. In the worker thread, we increment a static counter and concatenate it with a string and send it to the master thread that manages the UI elements. We emit a signal and this signal is landed on the slot. This way the master thread updates the text box on the screen per 5 seconds. The worker thread sleeps for 5 seconds at each iteration.
Firstly, I tried to use setVisible() from thread
There is an event:
void MainWindow::OnShow(){
// Start OnShow actions
ui->LoadingBox->setVisible(true);
std::thread dThread(OnShow_threaded, ui, &(this->settingsMap));
dThread.join();
}
There is a function OnShow_threaded:
void OnShow_threaded(Ui::MainWindow *ui, std::unordered_map<QString,QString> *settingsMap){
// Connect to server
bool hasInternet = false;
// If app doesn't have Internet access -> show offline mode
if (!hasInternet) {
ui->SettingsLabel->setVisible(true);
}
}
The program crashes when compiling a static assembly with an error:
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to
objects owned by a different thread. Current thread 0x0x36c56540.
Receiver 'WarningMsg' (of type 'QGroupBox') was created in thread
0x0x341c2fa0", file kernel\qcoreapplication.cpp, line 558
On the line: ui->SettingsLabel->setVisible(true);
At the same time, there is no such error when linking dynamically.
You can find full project on GitHub
Secondly, I tried to use events.
There is a function OnShow_threaded:
void OnShow_threaded(MainWindow* mw, Ui::MainWindow *ui, std::unordered_map<QString,QString> *settingsMap){
// Connect to server
bool hasInternet = false;
// If app doesn't have Internet access -> show offline mode
if (!hasInternet) {
MyEvent* event = new MyEvent(EventTypes::InternetConnectionError);
QCoreApplication::postEvent(mw, event);
//delete event;
//delete receiver;
}
}
There is an event class:
#ifndef EVENTS_HPP
#define EVENTS_HPP
#include <QEvent>
#include <QString>
enum EventTypes {
InternetConnectionError,
Unknown
};
class MyEvent : public QEvent
{
public:
MyEvent(const EventTypes _type) : QEvent(QEvent::User) {_localType = _type;}
~MyEvent() {}
auto localType() const {return _localType;}
private:
int _localType;
};
#endif // EVENTS_HPP
There is an event handler:
void MainWindow::events(QEvent *event)
{
if (event->type() == QEvent::User)
{
MyEvent* postedEvent = static_cast<MyEvent*>(event);
if (postedEvent->localType() == EventTypes::InternetConnectionError){
ui->WarningMsg->setVisible(true);
ui->SettingsLabel->setVisible(true);
}
}
}
Passing parameters:
void MainWindow::OnShow(){
// Start OnShow actions
ui->LoadingBox->setVisible(true);
std::thread dThread(OnShow_threaded, this, ui, &(this->settingsMap));
dThread.detach();
}
There is a mainwindows hpp file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QMovie>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QObject>
#include <QMessageBox>
#include <QStandardPaths>
#include <QDir>
#include <QFile>
#include <QCoreApplication>
#include <QSaveFile>
#include <QProcess>
#include <thread>
#include <chrono>
#include <unordered_map>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include "settings.hpp"
#include "events.hpp"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void OnShow();
private slots:
void SettingsLabelPressed();
void on_CloseMsgButton_clicked();
void on_Settings_SaveButton_clicked();
void on_Settings_UseTranslation_stateChanged(int arg1);
protected:
void events(QEvent* event);
private:
Ui::MainWindow *ui;
std::unordered_map<QString,QString> settingsMap;
};
void OnShow_threaded(MainWindow* mw, Ui::MainWindow *ui, std::unordered_map<QString,QString> *settingsMap);
#endif // MAINWINDOW_H
But event doesn't execute.
What did I do wrong?
And how to properly change the GUI from another thread?
З.Ы. Sorry for my English, I'm from Russia....
As you asked for the demo with QThread, in the comments, then here it is.
As GUI, I have a mainwindow with two simple buttons and I want to show, hide the big buttons with a QThread (instead of just the slot clicked) I emit an intermediate signal from the clicked to hide/show the button.
The role of the QThreadis only to emit the signal to sigShowHide with argument trueor false.
The main UI thread treats this signal by showing or hiding the button by calling the slot onShowHideButtonThreaded which reacts to the signal sigShowHide
Here are the code files:
mainwindows.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void sigShowHide(bool);
public slots:
void onShowHideButtonThreaded(bool);
void onButton1Click();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindows.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QObject::connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::onButton1Click);
QObject::connect(this,&MainWindow::sigShowHide, this, &MainWindow::onShowHideButtonThreaded);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onShowHideButtonThreaded(bool a)
{
qDebug() << " the main thread id = " << QThread::currentThread() << "set the visibility ";
ui->pushButton_2->setVisible(a);
}
void MainWindow::onButton1Click()
{
qDebug()<< "clicked";
qDebug() << " the main thread id = " << QThread::currentThread();
QThread* l_thread = QThread::create([&]()
{
qDebug() << "Running Thread " << QThread::currentThreadId() << " to emit signal only ";
emit sigShowHide( !this->ui->pushButton_2->isVisible());
});
l_thread->start();
}
`
The main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
An example of execution is :
the main thread id = QThread(0x116ee18)
Running Thread 0x1ed8 to emit signal only
the main thread id = QThread(0x116ee18) set the visibility
In Qt, like in many other GUI frameworks, the GUI may be updated from the main thread only. This means if you want to update the GUI from another thread, you have to communicate that to the main thread, which in turn will update the GUI.
For more details refer to these articles:
Multithreading in Qt
Qt: Threading Basics
Qt: Multithreading Technologies
Additional resources in other languages: Правильная работа с потоками в Qt.
As stated in #rustyx's answer: In Qt, like in many other GUI frameworks, the GUI may be updated from the main thread only.
I was also stuck on this problem, and here are my two solutions:
Use QMetaObject::invokeMethod.
QMetaObject::invokeMethod(ui->SettingsLabel, "setVisible", Q_ARG(bool, true));
QMetaObject::invokeMethod is a thread-safe API, it has a Qt::ConnectionType type parameter, which has a default value Qt::AutoConnection.
Descriptions of some Qt::ConnectionType values:
Qt::AutoConnection:
(Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.
Qt::DirectConnection:
The slot is invoked immediately when the signal is emitted. The slot is executed in the signalling thread.
Qt::QueuedConnection:
The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
Qt::BlockingQueuedConnection:
Same as Qt::QueuedConnection, except that the signalling thread blocks until the slot returns. This connection must not be used if the receiver lives in the signalling thread, or else the application will deadlock.
It is clear from the description that the API will put your call request into a queue, and then the main thread will take it out from the queue to finally process your call request.
And if you need to get the return value of the target method, you need to explicitly pass Qt::BlockingQueuedConnection to the Qt::ConnectionType type parameter. Because the indirect default value Qt::QueuedConnection will not wait for the call to complete, you will likely get the wrong return value.
But this solution has a trouble that is it only supports signal and slot functions, NOT non-signal and non-slot functions.
Use connect.
// In "MainWindow" class declaration
//
class MainWindow : public QMainWindow
{
// ...
Q_SIGNALS:
void setSettingsLabelVisibleSafety(bool value);
// ...
};
// In "MainWindow" constructor
//
connect(this, &MainWindow::setSettingsLabelVisibleSafety, this,
[this](bool value) {
ui->SettingsLabel->setVisible(value);
}
);
// In other thread
//
setSettingsLabelVisibleSafety(value);
connect also has a Qt::ConnectionType type parameter, which has a default value Qt::AutoConnection if you pass the receiver parameter explicitly, otherwise the default value is Qt::DirectConnection. So you need to either explicitly pass the receiver parameter or explicitly pass Qt::AutoConnection value to the Qt::ConnectionType type parameter.
If the target method has a return value, Qt::BlockingQueuedConnection needs to be passed explicitly as well.
This solution supports signal and slot functions, as well as non-signal and non-slot functions.
Both of the above solutions require you to call qRegisterMetaType to register the user-defined types (if any).
qRegisterMetaType<YourType>("YourType");
Obviously, I prefer the second solution.
I use QTimer to send periodically 'Ping' packet to the server (MQTT client). But it timer is not absolutely accurate. After some time of working it has some delay and server broken connection.
I try to use different Qt::TimerType, but it does not help.
I need the most accurate timer. Do you have any ideas?
Thank you!
EDIT (Frederik solution)
I have done something this:
tthread.h
class TThread : public QThread
{
Q_OBJECT
void run();
public:
explicit TThread(QObject *parent = 0);
signals:
private slots:
void timerOut();
};
tthread.cpp
TThread::TThread(QObject *parent) : QThread(parent)
{
}
void TThread::run()
{
QTimer timer;
connect(&timer, SIGNAL(timeout()), this, SLOT(timerOut()), Qt::DirectConnection);
timer.start(1000);
timer.moveToThread(this);
exec();
}
void TThread::timerOut()
{
QTime time = QTime();
qDebug() << time.currentTime().toString();
}
main.cpp
TThread thread;
thread.start();
thread.setPriority(QThread::HighPriority);
Run a QTimer on a separate QThread which doesn't do anything else.
If you're running the timer on an already busy thread the timeout events may not come on time or not at all.
Make a worker class e.g. "PingPacketWorker", implement a slot that does pinging.
Make a QThread.
Connect your QTimer and PingPacketWorker signal/slots.
Start the timer.
Call moveToThread on the PingPacketWorker and the QTimer, the timer should restart because of moveToThread, note you can only start / stop it on the owner thread!
You could also increase your QThread's priority since you asked for "the most accurate" solution ...
Update:
Also, set QTimer::setTimerType(Qt::PreciseTimer)
The default Qt::CoarseTimer is less precise (5% of the interval)
#pragma once
#include <cstdint>
#include <QObject>
class PrecisePolling : public QObject
{
Q_OBJECT
private:
std::uint64_t previousPollingTime;
public:
PrecisePolling();
public slots:
void doPolling();
#include "precisepolling.h"
#include "QDateTime"
#include <QDebug>
PrecisePolling::PrecisePolling()
: previousPollingTime(QDateTime::currentMSecsSinceEpoch())
{}
void PrecisePolling::doPolling()
{
const std::uint64_t ms = QDateTime::currentMSecsSinceEpoch();
qDebug() << ms - previousPollingTime;
previousPollingTime = ms;
}
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include "precisepolling.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread thread;
QTimer timer;
PrecisePolling pp;
timer.setInterval(1000);
timer.setTimerType(Qt::PreciseTimer);
QObject::connect(&timer, &QTimer::timeout, &pp, &PrecisePolling::doPolling);
timer.start();
timer.moveToThread(&thread);
pp.moveToThread(&thread);
thread.start(QThread::Priority::TimeCriticalPriority);
return a.exec();
}
What I am trying to achieve is a cross-platform TCP socket library built on top of Qt QTcpServer/Socket. I faced an issue that signals, emitted from a non-Qt thread without Qt event loop, are not received by objects in QThread with the event loop.
I have found that emitting from a non-Qt thread worked before with Qt::QueuedConnection connection type set explicitly, according to this and this questions. Those questions are rather old and relate to Qt 4. So I wonder if this functionality is still supported in Qt 5.
I have explored the Qt 5 source code and found:
Emitting a signal is just a call to QMetaObject::activate
QMetaObject::activate, in turn, calls queued_activate, if connection type is set to Qt::QueuedConnection or the current thread (emitter thread) is different from the thread receiver lives in (in my case, Qt::QueuedConnection is set explicitly).
queued_activate creates an event object and calls QCoreApplication::postEvent
QCoreApplication::postEvent does proper locking and puts the event into the receiver event queue. Despite postEvent is a static QCoreApplication function that uses self - a pointer to current static QCoreApplication singleton, it should work properly even if there is no global QCoreApplication object (i.e. self == 0).
Given this, I suppose that for signal&slot mechanism to work properly, only the receiver thread has to have the Qt event loop that will dispatch the event from the queue, correct me if I am wrong.
Despite that, emitting a signal from a non-Qt thread does not work for me. I have created as simple demo app as possible that demonstrates the malfunctioning of the signal&slot.
MyThread component just inherits QThread and moves inside itself (moveToThread) QObject-derived ThreadWorker.
MyThread.h:
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include "ThreadWorker.h"
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
signals:
void mySignal();
private:
ThreadWorker m_worker;
};
#endif // MYTHREAD_H
MyThread.cpp:
#include "MyThread.h"
#include "ThreadWorker.h"
MyThread::MyThread()
: m_worker(*this)
{
m_worker.moveToThread(this);
}
Thread worker is needed to live in MyThread thread and to connect to MyThread`s mySignal() signal.
ThreadWorker.h:
#ifndef THREADWORKER_H
#define THREADWORKER_H
#include <QObject>
class MyThread;
class ThreadWorker : public QObject
{
Q_OBJECT
public:
explicit ThreadWorker(const MyThread& thread);
public slots:
void mySlot();
};
#endif // THREADWORKER_H
ThreadWorker.cpp:
#include "ThreadWorker.h"
#include <QDebug>
#include "MyThread.h"
ThreadWorker::ThreadWorker(const MyThread& thread)
: QObject(0)
{
connect(&thread, SIGNAL(mySignal()),
this, SLOT(mySlot()),
Qt::QueuedConnection);
}
void ThreadWorker::mySlot()
{
qDebug() << "mySlot called! It works!";
}
Finally, the main.cpp:
#include <QCoreApplication>
#include <QDebug>
#include "MyThread.h"
int main(int argc, char *argv[])
{
// QCoreApplication a(argc, argv);
MyThread my_thread;
my_thread.start();
emit my_thread.mySignal();
qDebug() << "mySignal emitted";
my_thread.wait();
// return a.exec();
}
Note, that if I uncomment QCoreApplication creation, I get the correct output:
mySignal emitted
mySlot called! It works!
but if I leave it as is, I get only
mySignal emitted
QEventLoop: Cannot be used without QApplication
So, what is the reason signal&slot mechanism does not work in this case? How to make it working?
The error message tells you exactly what you need to know: you can't use the event loop system without QCoreApplication existing. That's all. All of your exploration into the innards of Qt was educational, but a red herring. None if it matters at all.
only the receiver thread has to have the Qt event loop that will dispatch the event from the queue
That's correct.
Does it mean that if I create QCoreApplication inside QThread, this system should work?
You might create it on any thread (in contrast to QGuiApplication that can only live on the main thread). But make sure that you link statically with Qt. Otherwise, if you're linking with system Qt, you'll become binary incompatible with any process using the same Qt if you create a second instance of the application. Thus, if you use system Qt you can work around by inspecting whether an application instance exists, and only create one if it doesn't exist yet.
Furthermore, you shouldn't really need to create the application instance in a custom thread. Your library should accept an initialization call that should be performed in the main thread of the calling process. This initialization can create an application object if one doesn't exist.
// https://github.com/KubaO/stackoverflown/tree/master/questions/twothreads-41044526
#include <QtCore>
// see http://stackoverflow.com/questions/40382820
template <typename Fun> void safe(QObject * obj, Fun && fun) {
Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
if (Q_LIKELY(obj->thread() == QThread::currentThread()))
return fun();
struct Event : public QEvent {
using F = typename std::decay<Fun>::type;
F fun;
Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
Event(const F & fun) : QEvent(QEvent::None), fun(fun) {}
~Event() { fun(); }
};
QCoreApplication::postEvent(
obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));
}
class Worker : public QObject {
Q_OBJECT
QBasicTimer m_timer;
int n = 0;
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == m_timer.timerId())
emit hasData(n++);
}
public:
Q_SIGNAL void hasData(int);
Q_SLOT void onData(int d) { qDebug() << QThread::currentThread() << "got data" << d; }
void start() {
safe(this, [this]{ m_timer.start(50,this); });
}
void quit() {
safe(this, [this]{ m_timer.stop(); thread()->quit(); });
}
};
class Library {
QByteArray dummy{"dummy"};
int argc = 1;
char *argv[2] = {dummy.data(), nullptr};
QScopedPointer<QCoreApplication> app;
static Library *m_self;
struct {
Worker worker;
QThread thread;
} m_jobs[3];
public:
Library() {
Q_ASSERT(!instance());
m_self = this;
if (!qApp) app.reset(new QCoreApplication(argc, argv));
for (auto & job : m_jobs) {
job.worker.moveToThread(&job.thread);
job.thread.start();
job.worker.start();
QObject::connect(&job.worker, &Worker::hasData, &m_jobs[0].worker, &Worker::onData);
}
}
~Library() {
for (auto &job : m_jobs) {
job.worker.quit();
job.thread.wait();
}
}
static Library *instance() { return m_self; }
};
Library *Library::m_self;
// API
void initLib() {
new Library;
}
void finishLib() {
delete Library::instance();
}
int main()
{
initLib();
QThread::sleep(3);
finishLib();
}
#include "main.moc"
I am trying to start a QTimer in a specific thread. However, the timer does not seem to execute and nothing is printing out. Is it something to do with the timer, the slot or the thread?
main.cpp
#include "MyThread.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
MyThread t;
t.start();
while(1);
}
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QTimer>
#include <QThread>
#include <iostream>
class MyThread : public QThread {
Q_OBJECT
public:
MyThread();
public slots:
void doIt();
protected:
void run();
};
#endif /* MYTHREAD_H */
MyThread.cpp
#include "MyThread.h"
using namespace std;
MyThread::MyThread() {
moveToThread(this);
}
void MyThread::run() {
QTimer* timer = new QTimer(this);
timer->setInterval(1);
timer->connect(timer, SIGNAL(timeout()), this, SLOT(doIt()));
timer->start();
}
void MyThread::doIt(){
cout << "it works";
}
As I commented (further information in the link) you are doing it wrong :
You are mixing the object holding thread data with another object (responsible of doIt()). They should be separated.
There is no need to subclass QThread in your case. Worse, you are overriding the run method without any consideration of what it was doing.
This portion of code should be enough
QThread* somethread = new QThread(this);
QTimer* timer = new QTimer(0); //parent must be null
timer->setInterval(1);
timer->moveToThread(somethread);
//connect what you want
somethread->start();
Now (Qt version >= 4.7) by default QThread starts a event loop in his run() method. In order to run inside a thread, you just need to move the object. Read the doc...
m_thread = new QThread(this);
QTimer* timer = new QTimer(0); // _not_ this!
timer->setInterval(1);
timer->moveToThread(m_thread);
// Use a direct connection to whoever does the work in order
// to make sure that doIt() is called from m_thread.
worker->connect(timer, SIGNAL(timeout()), SLOT(doIt()), Qt::DirectConnection);
// Make sure the timer gets started from m_thread.
timer->connect(m_thread, SIGNAL(started()), SLOT(start()));
m_thread->start();
A QTimer only works in a thread that has an event loop.
http://qt-project.org/doc/qt-4.8/QTimer.html
In multithreaded applications, you can use QTimer in any thread that has an event loop. To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timer's thread affinity to determine which thread will emit the timeout() signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread.
You can use the emit signal and start the timer inside the emitted slot function
main.cpp
#include "MyThread.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
MyThread t;
t.start();
while(1);
}
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QTimer>
#include <QThread>
#include <iostream>
class MyThread : public QThread {
Q_OBJECT
public:
MyThread();
QTimer *mTimer;
signals:
start_timer();
public slots:
void doIt();
void slot_timer_start();
protected:
void run();
};
#endif /* MYTHREAD_H */
MyThread.cpp
#include "MyThread.h"
using namespace std;
MyThread::MyThread() {
mTimer = new QTimer(this);
connect(this,SIGNAL(start_timer()),this, SLOT(slot_timer_start()));
connect(mTimer,SIGNAL(timeout()),this,SLOT(doIt()));
}
void MyThread::run() {
emit(start_timer());
exec();
}
void MyThread::doIt(){
cout << "it works";
}
void MyThread::slot_timer_start(){
mTimer->start(1000);
}
You need an event loop to have timers. Here's how I solved the same problem with my code:
MyThread::MyThread() {
}
void MyThread::run() {
QTimer* timer = new QTimer(this);
timer->setInterval(1);
timer->connect(timer, SIGNAL(timeout()), this, SLOT(doIt()));
timer->start();
/* Here: */
exec(); // Starts Qt event loop and stays there
// Which means you can't have a while(true) loop inside doIt()
// which instead will get called every 1 ms by your init code above.
}
void MyThread::doIt(){
cout << "it works";
}
Here's the relevant piece of the documentation that none of the other posters mentioned:
int QCoreApplication::exec()
Enters the main event loop and waits until exit() is called. Returns
the value that was set to exit() (which is 0 if exit() is called via
quit()). It is necessary to call this function to start event
handling. The main event loop receives events from the window system
and dispatches these to the application widgets. To make your
application perform idle processing (i.e. executing a special function
whenever there are no pending events), use a QTimer with 0 timeout.
More advanced idle processing schemes can be achieved using
processEvents().
I have created an example that calls the timer within a lambda function:
#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QDebug>
#include <memory>
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
QThread* thread = new QThread(&app);
QObject::connect(thread, &QThread::started, [=]()
{
qInfo() << "Thread started";
QTimer* timer1 = new QTimer(thread);
timer1->setInterval(100);
QObject::connect(timer1, &QTimer::timeout, [=]()
{
qInfo() << "Timer1 " << QThread::currentThreadId();
});
timer1->start();
});
thread->start();
QTimer timer2(&app);
QObject::connect(&timer2, &QTimer::timeout, [=]()
{
qInfo() << "Timer2 " << QThread::currentThreadId();
});
timer2.setInterval(100);
timer2.start();
return app.exec();
}