start QTimer from another class - c++

I have the following classes:
class MainWindow : public QMainWindow
{
public:
void StartTimer()
{
timer = new QTimer(this);
timer.start(100);
}
private:
QTimer *timer;
};
class AnotherClass
{
public:
MainWindow *window;
void runTimer()
{
window->StartTimer();
}
};
Assuming the window pointer is correctly pointing to the mainwindow, if I try to call runTimer() , I receive this error:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is MainWindow(0x7fff51ffe9f0), parent's thread is QThread(0x7fd1c8d001d0), current thread is QThread(0x7fd1c8f870c0)
QObject::startTimer: Timers can only be used with threads started with QThread
My guess for this error was that since runTimer was being called from a different thread it was also trying to initialize in that same thread? instead of the mainwindow thread?
If I initialize the timer in the default constructor of the main window I receive
QObject::startTimer: Timers cannot be started from another thread
How can I tell a QTimer to start from another class?

You can use signals and slots.
class AnotherClass : public QObject
{
Q_OBJECT
public:
MainWindow * window;
AnotherClass() : window( new MainWindow )
{
// Connect signal to slot (or just normal function, in this case )
connect( this, &AnotherClass::signalStartTimer,
window, &MainWindow::StartTimer,
// This ensures thread safety, usually the default behavior, but it doesn't hurt to be explicit
Qt::QueuedConnection );
runTimer();
}
void runTimer()
{
emit signalStartTimer();
}
signals:
void signalStartTimer();
};

Related

Qt GUI hanging with worker in a QThread

I have a worker which needs to complete an arbitrary blocking task
class Worker : public QObject
{
Q_OBJECT;
public:
using QObject::QObject;
public slots:
void start()
{
for (int i = 0; i < 10; i++)
{
qDebug("I'm doing work...");
Sleep(1000);
}
}
};
In my MainWindow constructor, I start the task like so:
class MainWindow : public QWidget
{
Q_OBJECT;
public:
explicit MainWindow(QWidget* parent = nullptr) : QWidget(parent)
{
QThread* t = new QThread(this);
Worker* w = new Worker(this);
w->moveToThread(t);
this->connect(
t, &QThread::started,
w, &Worker::start
);
this->connect(
t, &QThread::finished,
t, &QThread::deleteLater
);
t->start();
}
};
And my entry point:
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MainWindow mainWindow{};
mainWindow.show();
return app.exec();
}
When I run the program, I just get a spinning mouse circle, and the window contents don't load until 10 seconds later (when the worker is complete). Why is this so? What do I need to do so that Worker::start is run in the background without affecting the GUI?
P.S. I am using Qt 6.2.2 and Windows 11, but I highly doubt that has anything to do with this issue.
The problem is that the worker is actually not moved to thread (Isn't a warning written to console? I bet it is.), because its parent, i.e. MainWindow instance is still in the GUI thread.
When moving to thread, you can only move the whole hierarchy of objects by moving the very top parent. You cannot have parent in different thread than its children.
You should create the worker without parent.
Worker* w = new Worker();
Of course you should also add some finished() signal to your Worker class.
class Worker : public QObject
{
Q_OBJECT;
public:
using QObject::QObject;
void start()
{
for (int i = 0; i < 10; i++)
{
qDebug("I'm doing work...");
Sleep(1000);
}
emit finished();
}
signals:
void finished();
};
and add these connections:
this->connect(
w, &Worker::finished,
t, &QThread::quit
);
this->connect(
t, &QThread::finished,
w, &Worker::deleteLater
);
otherwise your thread's even loop will not end and the worker will not be deleted.

Custom function interrupt

Is it possible to implement function interrupt in Qt (5.x).
For example if I have a button and want something to execute on the thread (which is running infinite loop) when this button is clicked, I could say something like this:
in thread...
forever
{
if(button_is_pressed_flag)
{
do something...
}
}
is there a better way?
The infinite loop should be an event loop, and then it can automatically process cross-thread slot calls without you worrying about the details.
The idiom to run code "continuously" on an event loop is the zero-duration timer.
Let's say you start with code that looks like this:
class MyThread : public QThread {
bool button_is_clicked_flag = false;
void run() override {
forever{
if (button_is_clicked_flag) {
onButtonClick();
button_is_clicked_flag = false;
}
doWork();
}
}
void onButtonClick();
void doWork();
public:
using QThread::QThread;
void setButtonClickedFlag();
}
int main(int argc, char **argv) {
...
MyThread t;
t.start();
...
}
It is required for doWork() not to take too long - nor too short. If it took ~5ms on modern hardware, it'd be just about a right tradeoff between overhead and latency for a general-purpose application. If you need lower latency reaction in the worker thread, then doWork() must do less work. It probably doesn't make much sense for doWork() to take much less than 1ms.
And whenever doWork() doesn't have anything to do, e.g. if it's done with the computation it was supposed to perform, it should stop the timer that keeps it alive.
You should transform it to look as follows:
class MyWorker : public QObject {
Q_OBJECT
QBasicTimer m_timer;
void doWork();
void timerEvent(QTimerEvent *event) {
if (event->timerId() == m_timer.timerId())
doWork();
}
public:
explicit MyWorker(QObject *parent = nullptr) : QObject(parent) {
m_timer.start(0, this);
}
Q_SLOT void onButtonClick() {
// Ensure we're invoked correctly
Q_ASSERT(QThread::currentThread() == thread());
...
}
}
class Window : public QWidget {
Ui::Window ui;
public:
Q_SIGNAL void buttonClicked();
explicit Window(QWidget *parent = nullptr) : QWidget(parent) {
ui.setupUi(this);
connect(ui.button, &QPushButton::clicked, this, &Window::buttonClicked);
}
};
class SafeThread : public QThread {
Q_OBJECT
using QThread::run; // final method
public:
~SafeThread() { quit(); wait(); } // we're safe to destroy - always
};
int main(int argc, char **argv) {
...
MyWorker worker;
SafeThread thread;
Window window;
// onButtonClick will be executed in worker->thread()
connect(&window, &Window::buttonClicked, &worker, &MyWorker::onButtonClick);
worker.moveToThread(&thread);
thread.start();
window.show();
return app.exec();
}
The event loop that runs in QThread::run will continuously invoke doWork via the timer event handler. But whenever a cross-thread slot call needs to be made to an object living in that thread, the event loop will deliver the internal QMetaCallEvent representing the slot call to QObject::event, which will then execute the call.
Thus, when you set a breakpoint in onButtonClick, there will be QObject::event nearby on the call stack.
You could start a thread and then immediately wait on a std::condition_variable, then when the button is clicked (the event being called on the main thread), notify the condition variable and the thread would awake.
However, this is a bit strange. What are you trying to do? call an asynchronous task upon a button click? In that case, perhaps it would be better just to start one from the button click event with std::packaged_task or std::async.

Qt signal slot with threads

I have a problem with signal/slots in a QThread class. My design looks like this:
class Manager : public QObject {
Q_OBJECT
public:
Manager(QObject* parent) : QObject(parent) {
Thread thread* = new Thread(this);
connect(this, SIGNAL(testsignal()), thread, SLOT(test()));
thread->start();
...
emit testsignal();
}
signals:
void testsignal();
};
class Thread : public QThread {
Q_OBJECT
public slots:
void test() {
qDebug() << "TEST";
}
private:
void run() {}
};
The signal never reaches my test() method. Can someone help? Thanks.
The problem is that sending signals across threads results in queuing the signal into the target thread's event queue (a queued connection). If that thread never processes events, it'll never get the signal.
Also, according to the QThread::run documentation:
Returning from this method will end the execution of the thread.
In other words, having an empty run method results in instant termination of the thread, so you're sending a signal to a dead thread.
Signals sent to a QThread object will go to the thread of the parent object. In this case to the same thread that created it.
To have a object live on another thread you should move it to that thread:
class Manager : public QObject {
Q_OBJECT
public:
Manager(QObject* parent) : QObject(parent) {
Thread thread* = new QThread(this);
Receiver* rec = new Receiver(); //no parent
connect(this, SIGNAL(testsignal()), rec, SLOT(test()));
connect(thread, SIGNAL(finished()), rec, SLOT(deleteLater()));
rec->moveToThread(thread);
thread->start();
...
emit testsignal();
}
signals:
void testsignal();
};
class Receiver: public QObject {
Q_OBJECT
public slots:
void test() {
qDebug() << "TEST";
}
};

Qt Threads: Signals/Slots 2 Objects (1 Singleton) created in Main Thread and called in another Thread

I have a Problem:
I've got 2 objects wich are both created and connected in the main thread. One of the objects is a Singleton.
Now I want to call a method from the singleton in another thread and emit a signal in it.
The Problem is, that the connected slot is not called after the signal was emitted. Am I doing something wrong or is connect() not capable of it?
The Second Thread should process some folders and call a method from the singleton object, which is emitting the signal, with the result of the process.
The Code looks something like this (not working):
void main() {
Singleton* singleton = Singleton::getInstance();
ShowResult* show = new Showesult();
connect(singleton, SIGNAL(itemsset(Items)), show, SLOT(showresult(Items)));
Process* p = new Process();
QThread thread;
p->moveToThread(thread);
connect(&thread, SIGNAL(started()), p, SLOT(process()));
thread.start();
}
class Process : public QObject {
public:
Process(){}
public slots:
void process() {
// Do some work
Singleton::getInstance()->setItems(someitems);
}
};
class Singleton : public QObject {
public:
Singleton(){}
Singleton* getInstance() {
return instance;
}
void setitems(Items) {
//Set items
emit itemsset();
}
signals:
void itemsset(Items);
};
class ShowResult : public QObject {
public:
ShowResult(){}
public slots:
void showresult(Items) {
//THIS Slot is not called...
}
}
Thanks for your help...

Qt Object::connect: No such slot Signal to Thread Slot

i try to invoke Slot in thread object when threas started but getting this error:
Object::connect: No such slot Worker::doWork(pFoo)
the thread executing code:
// main class
m_WorkerThread = new QThread();
FooStack* pfooStack = InternalStorageManager::getInstance()->getStack();
m_Worker = new Worker();
bool done = connect(m_WorkerThread,
SIGNAL(started()),
m_Worker,
SLOT(doWork(pfooStack)));
m_Worker->moveToThread(m_WorkerThread);
m_WorkerThread->start();
// class Worker
// cpp imple
void Worker::doWork(FooStack *& rp_urlsStack)
{
}
// header
class Worker : public QObject
{
Q_OBJECT
public :
Worker();
~Worker();
public slots:
void doWork(FooStack *&);
};
Object::connect: No such slot Worker::doWork(pFoo)
You can't pass objects in connection declarations.
Can't you pass pfooStack into the Worker constructor?
EDIT:
class Main : ...
{
...
void startThread(); // The method in your example.
private slots:
void startWork();
...
};
void Main::startThread()
{
m_WorkerThread = new QThread();
m_Worker = new Worker();
bool done = connect(m_WorkerThread, SIGNAL(started()),
this, SLOT(startWork()));
m_Worker->moveToThread(m_WorkerThread);
m_WorkerThread->start();
}
void Main::startWork()
{
m_Worker->doWork(InternalStorageManager::getInstance()->getStack());
}
I have not compiled the code on my computer, but it should imply what you need:
m_WorkerThread = new QThread();
FooStack* pfooStack = InternalStorageManager::getInstance()->getStack();
m_Worker = new Worker(pfooStack);
bool done = connect(m_WorkerThread,
SIGNAL(started()),
m_Worker,
SLOT(doWork()));
m_Worker->moveToThread(m_WorkerThread);
m_WorkerThread->start();
void Worker::doWork()
{
//use stack here
}
class Worker : public QObject
{
Q_OBJECT
public :
Worker(FooStack *& rp_urlsStack):stack(rp_urlsStack);
~Worker();
public slots:
void doWork();
private:
FooStack*& stack;
};
You can't do it that way, you can't pass current variables as slot method parameters in connect, and slot can't have more parameters than the signal. In addition to other answers, you can achieve this with QSignalMapper, but if you have just one connection to the slot, that seems like an overkill.
If you can use Qt5 and C++11, then you can connect signal to lambda functions, not just slots, but I'm not absolutely sure if that supports creating a closure (that is, using the local variable in the lambda function, which you would need here).
I think you need to change the signal and slot signatures. From the QT Documenation:
The rule about whether to include arguments or not in the SIGNAL() and SLOT() macros, if the arguments have default values, is that the signature passed to the SIGNAL() macro must not have fewer arguments than the signature passed to the SLOT() macro.