How to properly connect to QObject's destroyed signal from Qt Script?
When I connect to it like to your average signal, it does not work. I did test that I removed the object and that other QObjects did get the signal, but the script function I connected to it is not invoked.
Below is the sample I'm using for testing. The most important part is the line:
_engine.evaluate("obj.destroyed.connect(function() { debug(\"obj destroyed\") })");
I'd expect it to invoke that function when obj is destroyed. The code below ensures that object is deleted when even loop already started and it also tests that the object did send destroyed signal to another QObject. What I want to fix is for the script to invoke the script function with debug("obj destroyed") after destroyed signal is emitted.
ScriptTester.h:
#ifndef SCRIPTTESTER_H
#define SCRIPTTESTER_H
#include <QtScript>
#include <QtCore>
class ScriptTester: public QObject {
Q_OBJECT
public:
explicit ScriptTester(QObject *parent = 0);
private slots:
void signalTest();
void boo();
private:
QScriptEngine _engine;
};
#endif // SCRIPTTESTER_H
ScriptTester.cpp:
#include "ScriptTester.h"
static QScriptValue scriptDebug(QScriptContext *context, QScriptEngine *engine) {
Q_UNUSED(engine);
if (context->argumentCount() >= 1) {
QString msg = context->argument(0).toString();
for (int i = 1; i < context->argumentCount(); ++i) {
msg = msg.arg(context->argument(i).toString());
}
qDebug() << msg;
}
return QScriptValue();
}
ScriptTester::ScriptTester(QObject *parent) :
QObject(parent)
{
QTimer::singleShot(0, Qt::CoarseTimer, this, SLOT(signalTest()));
_engine.globalObject().setProperty("debug", _engine.newFunction(scriptDebug, 1));
}
void ScriptTester::signalTest() {
QObject *obj = new QObject(this);
_engine.globalObject().setProperty("obj", _engine.newQObject(obj));
_engine.evaluate("obj.destroyed.connect(function() { debug(\"obj destroyed\") })");
if (_engine.hasUncaughtException()) {
qDebug() << "Exception:" << _engine.uncaughtException().toString();
}
connect(obj, SIGNAL(destroyed()), this, SLOT(boo()));
QTimer *timer = new QTimer;
_engine.globalObject().setProperty("timer", _engine.newQObject(timer));
_engine.evaluate("timer.timeout.connect(function() { debug(\"timer timeout\"); obj.deleteLater(); })");
if (_engine.hasUncaughtException()) {
qDebug() << "Exception:" << _engine.uncaughtException().toString();
}
timer->setSingleShot(true);
timer->start(100);
}
void ScriptTester::boo() {
qDebug() << "was destroyed!";
}
Note that I don't want a hack like passing destroyed first to C++ code and then manually or by a signal informing script of that. I'm searching for an implementation that's done fully in the script.
Related
I have a bigger project, with a GUI and I want to manage some files in the background. I've implemented a new thread for this task and on runtime, everything works great. But as soon as I quit the application visual-leak-detector finds 3-7 memory leaks.
I separated my threading code and created a new project to check this with a minimal code example, but I'm still not able to fix my issue.
I think it has something to do with the event loop of the main program. Maybe the loop doesn't process the last events to delete my thread class and the thread itself. Because I stop and quit the thread in a destructor. But I'm not sure on this one.
Here is my minimal code:
threadclass.hpp:
#include <QObject>
#include <QDebug>
class ThreadClass : public QObject {
Q_OBJECT
public:
explicit ThreadClass() {}
virtual ~ThreadClass(){
qDebug() << "ThreadClass Destructor";
}
signals:
// emit finished for event loop
void finished();
public slots:
// scan and index all files in lib folder
void scanAll(){
for(long i = 0; i < 10000; i++){
for (long k = 0; k < 1000000; k++);
if(i%500 == 0)
qDebug() << "thread: " << i;
}
}
// finish event loop and terminate
void stop(){
// will be processed after scanall is finished
qDebug() << "STOP SIGNAL --> EMIT FINSIHED";
emit finished();
}
};
threadhandler.hpp:
#include <QObject>
#include <QThread>
#include "threadclass.hpp"
class ThreadHandler : public QObject {
Q_OBJECT
public:
explicit ThreadHandler(QObject *parent = 0) : parent(parent), my_thread(Q_NULLPTR) {}
virtual ~ThreadHandler() {
// TODO Check!
// I think I don't have to delete the threads, because delete later
// on finish signal. Maybe I just have to wait, but then how do I
// check, if thread is not finished? Do I need to make a bool var again?
if (my_thread != Q_NULLPTR && my_thread->isRunning())
{
emit stopThread();
//my_thread->quit();
my_thread->wait();
//delete my_thread;
}
qDebug() << "ThreadHandler Destructor";
my_thread->dumpObjectInfo();
}
void startThread(){
if (my_thread == Q_NULLPTR)
{
my_thread = new QThread;
ThreadClass *my_threaded_class = new ThreadClass();
my_threaded_class->moveToThread(my_thread);
// start and finish
QObject::connect(my_thread, &QThread::started, my_threaded_class, &ThreadClass::scanAll);
QObject::connect(this, &ThreadHandler::stopThread, my_threaded_class, &ThreadClass::stop);
// finish cascade
// https://stackoverflow.com/a/21597042/6411540
QObject::connect(my_threaded_class, &ThreadClass::finished, my_threaded_class, &ThreadClass::deleteLater);
QObject::connect(my_threaded_class, &ThreadClass::destroyed, my_thread, &QThread::quit);
QObject::connect(my_thread, &QThread::finished, my_thread, &QThread::deleteLater);
my_thread->start();
}
}
signals:
void stopThread();
private:
QObject *parent;
QThread* my_thread;
};
The main.cpp is crappy but seems to simulate the behavior of my main program well enough:
#include <QCoreApplication>
#include <QTime>
#include <QDebug>
#include "threadhandler.hpp"
#include <vld.h>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
ThreadHandler *th = new ThreadHandler();
th->startThread();
// wait (main gui programm)
QTime dieTime= QTime::currentTime().addSecs(5);
while (QTime::currentTime() < dieTime) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
qDebug() << "DELETE TH";
delete th;
qDebug() << "FINISH ALL EVENTS";
QCoreApplication::processEvents(QEventLoop::AllEvents, 500);
qDebug() << "QUIT";
QCoreApplication::quit();
qDebug() << "CLOSE APP";
// pause console
getchar();
// return a.exec();
}
Here is the output from VLD:
WARNING: Visual Leak Detector detected memory leaks!
...
turns out this is very boring and uninteresting
...
Visual Leak Detector detected 3 memory leaks (228 bytes).
Largest number used: 608 bytes.
Total allocations: 608 bytes.
Visual Leak Detector is now exiting.
The program '[8708] SOMinimalExampleThreads.exe' has exited with code 0 (0x0).
Update 1: I added qDebug() << "ThreadClass Destructor"; to the destructor of the ThreadClass and my new output looks like this:
...
thread: 9996
thread: 9997
thread: 9998
thread: 9999
ThreadHandler Destructor
FINISH ALL EVENTS
CLOSE APP
Now it is clear that the destructor of my threaded class is never called and therefore lost in the void. But then why is this not working?
QObject::connect(my_threaded_class, &ThreadClass::finished, my_threaded_class, &ThreadClass::deleteLater);
Update 2: I found one problem in ThreadHandler:
emit stopThread();
my_thread->quit(); // <-- stops the event loop and therefore no deletelater
my_thread->wait();
I removed my_thread->quit() and now the destructor of ThreadClass is called, but my_thread->wait() never finishes.
Problem description:
When the destructor of ThreadHandler emits stopThread from the main thread, Qt invokes the connected slot (&ThreadClass::stop) by posting an event into the worker thread's event loop (aka, a queued connection). This means that the event loop of the worker's needs to be ready for receiving new events when this signal is emitted (since you are relying on it to perform proper clean-up). However, as you've already spotted, the call to thread->quit() might cause the event loop to quit earlier than desired (before the worker thread makes its way to call ThreadClass::stop, and hence the signal ThreadClass::finished is not emitted). You might want to examine the output of this minimal example that reproduces the behavior I am talking about:
#include <QtCore>
/// lives in a background thread, has a slot that receives an integer on which
/// some work needs to be done
struct WorkerObject : QObject {
Q_OBJECT
public:
using QObject::QObject;
Q_SLOT void doWork(int x) {
// heavy work in background thread
QThread::msleep(100);
qDebug() << "working with " << x << " ...";
}
};
/// lives in the main thread, has a signal that should be connected to the
/// worker's doWork slot; to offload some work to the background thread
struct ControllerObject : QObject {
Q_OBJECT
public:
using QObject::QObject;
Q_SIGNAL void sendWork(int x);
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QThread thread;
WorkerObject workerObj;
workerObj.moveToThread(&thread);
// quit application when worker thread finishes
QObject::connect(&thread, &QThread::finished, &a, &QCoreApplication::quit);
thread.start();
ControllerObject controllerObj;
QObject::connect(&controllerObj, &ControllerObject::sendWork, &workerObj,
&WorkerObject::doWork);
for (int i = 0; i < 100; i++) {
QThread::msleep(1);
// call thread.quit() when i is equal to 10
if (i == 10) {
thread.quit();
}
controllerObj.sendWork(i);
}
return a.exec();
}
#include "main.moc"
On my machine, Here is a possible output:
working with 0 ...
working with 1 ...
working with 2 ...
working with 3 ...
Notice that, despite the fact that thread.quit() is called on the tenth iteration from the main thread, the worker thread might not process all messages received before the exit call (and we get the value 3 as the last value processed by the worker).*
Solution:
Actually, Qt provides a canonical to way to quit a worker thread and perform necessary clean-up, since the signal QThread::finished is handled in a special way:
When this signal is emitted, the event loop has already stopped running. No more events will be processed in the thread, except for deferred deletion events. This signal can be connected to QObject::deleteLater(), to free objects in that thread.
This means that you can use thread->quit() (the same way you were doing), but you just need to add:
connect(my_thread, &QThread::finished, my_threaded_class, &ThreadClass::stop);
to your startThread and remove the unnecessary emit stopThread(); from the destructor.
* I couldn't find any page in the documentation that explains this behavior in detail, so I provided this minimal example to explain what I am talking about.
I found out that the my_thread->wait() function, blocks the event loop and therefore the quit and deleteLater cascade is never finished. I fixed this with another method of waiting for the finished thread:
removed
Here is the newly implemented solution from Mike. It was pretty easy to implement, I only had to change the connection to my_threaded_class::stop in the threadhandler class.
#include <QObject>
#include <QThread>
#include "threadclass.hpp"
class ThreadHandler : public QObject {
Q_OBJECT
public:
explicit ThreadHandler(QObject *parent = 0) : parent(parent), my_thread(Q_NULLPTR) {}
virtual ~ThreadHandler() {
if (my_thread != Q_NULLPTR && my_thread->isRunning())
{
my_thread->quit();
my_thread->wait();
}
qDebug() << "ThreadHandler Destructor";
}
void startThread(){
if (my_thread == Q_NULLPTR)
{
my_thread = new QThread;
ThreadClass *my_threaded_class = new ThreadClass();
my_threaded_class->moveToThread(my_thread);
// start and finish
QObject::connect(my_thread, &QThread::started, my_threaded_class, &ThreadClass::scanAll);
// https://stackoverflow.com/questions/53468408
QObject::connect(my_thread, &QThread::finished, my_threaded_class, &ThreadClass::stop);
// finish cascade
// https://stackoverflow.com/a/21597042/6411540
QObject::connect(my_threaded_class, &ThreadClass::finished, my_threaded_class, &ThreadClass::deleteLater);
QObject::connect(my_threaded_class, &ThreadClass::destroyed, my_thread, &QThread::quit);
QObject::connect(my_thread, &QThread::finished, my_thread, &QThread::deleteLater);
my_thread->start();
}
}
signals:
void stopThread();
private:
QObject *parent;
QThread* my_thread;
};
I have the following method in one of my C++ classes (using QtWebEngine):
QString get()
{
QString result;
view->page()->runJavaScript("test();", [this](const QVariant &v)
{
result = v.toString();
});
return result;
}
It is to execute test() JS function and return the result of this invocation.
Unfortunately, the callback is asynchronous and the program crashes. How can I make it work?
The callback is asynchronous because the JavaScript execution occurs not only in another thread but in another process. So there is no way to make it fully synchronous.
The best possible solution would be to migrate your C++ code to work asynchronously. If you can't do that, the only feasible solution is to use QEventLoop, somewhat like this:
void ranJavaScript()
{
emit notifyRanJavaScript();
}
QString get()
{
QString result;
QEventLoop loop;
QObject::connect(this, SIGNAL(notifyRanJavaScript()), &loop, SLOT(quit()));
view->page()->runJavaScript("test();", [this](const QVariant &v)
{
result = v.toString();
this.ranJavaScript();
});
loop.exec();
return result;
}
However, note that this example is oversimplified for a real-world usage: you need to ensure the JavaScript was not ran before the event loop is started. The most proper way to do that would involve implementing a proper slot instead of a lambda + factoring out the call to view->page()->runJavaScript() into another slot which would be called asynchronously after starting the event loop. It is a lot of glue code for such a seemingly simple task but that's what it takes. Here's an example:
MainWindow.h
#ifndef TMP_MAIN_WINDOW_H
#define TMP_MAIN_WINDOW_H
#include <QMainWindow>
#include <QVariant>
class QWebEngineView;
class QPushButton;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget * parent = 0);
QString get();
void onScriptEnded(const QVariant & data);
Q_SIGNALS:
void notifyRanJavaScript();
private Q_SLOTS:
void onButtonPressed();
void startScript();
private:
QWebEngineView * m_view;
QPushButton * m_button;
QString m_scriptResult;
};
#endif // TMP_MAIN_WINDOW_H
MainWindow.cpp
#include "MainWindow.h"
#include <QWebEngineView>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMessageBox>
#include <QEventLoop>
#include <QDebug>
#include <QTimer>
MainWindow::MainWindow(QWidget * parent) :
QMainWindow(parent)
{
m_view = new QWebEngineView;
QWebEnginePage * page = new QWebEnginePage(m_view);
m_view->setPage(page);
QString html = QStringLiteral("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""
"\"http://www.w3.org/TR/html4/strict.dtd\"><html>"
"<head><h3>head</h3>\n</head>"
"<script type=\"text/javascript\">function test() { return \"A!\"; }</script>"
"<body>text\n</body></html>");
m_view->page()->setHtml(html);
m_button = new QPushButton;
m_button->setMinimumWidth(35);
m_button->setText(QStringLiteral("Test"));
QObject::connect(m_button, SIGNAL(pressed()), this, SLOT(onButtonPressed()));
QHBoxLayout * buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(m_button);
buttonLayout->addStretch();
QVBoxLayout * viewLayout = new QVBoxLayout;
viewLayout->addLayout(buttonLayout);
viewLayout->addWidget(m_view);
QWidget * widget = new QWidget(this);
widget->setLayout(viewLayout);
setCentralWidget(widget);
}
QString MainWindow::get()
{
QEventLoop loop;
QObject::connect(this, SIGNAL(notifyRanJavaScript()), &loop, SLOT(quit()));
// Schedule the slot to run in 0 seconds but not right now
QTimer::singleShot(0, this, SLOT(startScript()));
// The event loop would block until onScriptEnded slot is executed
loop.exec();
// If we got here, the script has been executed and the result was saved in m_scriptResult
return m_scriptResult;
}
void MainWindow::onScriptEnded(const QVariant & data)
{
qDebug() << QStringLiteral("Script ended: ") << data;
m_scriptResult = data.toString();
emit notifyRanJavaScript();
}
void MainWindow::onButtonPressed()
{
QString str = get();
QMessageBox::information(this, QStringLiteral("Script result"), str,
QMessageBox::StandardButton::Ok);
}
struct Functor
{
Functor(MainWindow & window) : m_window(window) {}
void operator()(const QVariant & data)
{
m_window.onScriptEnded(data);
}
MainWindow & m_window;
};
void MainWindow::startScript()
{
qDebug() << QStringLiteral("Start script");
m_view->page()->runJavaScript(QStringLiteral("test();"), Functor(*this));
}
Dmitry's solution only works partially, unfortunately. In his example the code is invoked upon pressing a button. However, if the same code is moved to execute while the window is loading, the slot for the finished script (onScriptEnded) is never called. To fix this, I had to wrap the call to the evaluation of JS itself (called evaluateJavaScript in my project) in another QTimer::singleShot:
QTimer::singleShot(0, this, [&] { evaluateJavaScript(); });
Unfortunately, in my real project I have many calls, often one evaluation waiting for another before it to finish. It's practically impossible to use this every single time so I still look for a solution.
In the kchmviewer project, I found a very easy way to wait for the result of the runJavaScript function. The following code is part of the ViewWindow class which inherits from QWebEngineView.
int ViewWindow::getScrollbarPosition()
{
QAtomicInt value = -1;
page()->runJavaScript("document.body.scrollTop", [&value](const QVariant &v)
{
qDebug( "value retrieved: %d\n", v.toInt());
value = v.toInt();
});
while (value == -1)
{
QApplication::processEvents();
}
qDebug( "scroll value %d", value.load() );
return value;
}
I have some problems with threading inside QT.
#include <QCoreApplication>
#include "handler.hpp"
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
Handler* handler = new Handler();
handler->StartThread();
return a.exec();
}
What I expect is that with handler->StartThread() statement the function within my thread start to write debug messages and once the internal timer within handler finishes I get the nice line [Press ...] and then a return code of 0. However this is not happening. What I get is:
I WORK... ( 0x1540 )
Worker has finished. ( 0x6f4 )
I WORK... ( 0x1540 )
Worker has finished. ( 0x6f4 )
I WORK... ( 0x1540 )
Worker has finished. ( 0x6f4 )
I WORK... ( 0x1540 )
Worker has finished. ( 0x6f4 )
Thread stopped.
And of course when I stop the execution of the application, the return code is: -1073741510. Obviously not that nice of a zero.
Anyway here's the rest of the application code:
Handler.hpp
#ifndef HANDLER_HPP
#define HANDLER_HPP
#include <QObject>
#include <QThread>
#include <QDebug>
#include <QTimer>
#include "testclass.hpp"
class Handler : public QObject
{
Q_OBJECT
public:
Handler();
~Handler();
void StartThread();
public slots:
void functionFinished();
void threadTerminated();
private:
QTimer* shutdown;
QTimer* timer;
QThread* thread;
MyClass* worker;
};
#endif // HANDLER_HPP
Handler.cpp
#include "handler.hpp"
Handler::Handler() {
shutdown = new QTimer();
thread = new QThread();
timer = new QTimer();
worker = new MyClass();
worker->moveToThread(thread);
QObject::connect(thread, SIGNAL(started()), worker, SLOT(runAgain()));
QObject::connect(timer, SIGNAL(timeout()), worker, SLOT(runAgain()));
QObject::connect(worker, SIGNAL(iFinished()), this, SLOT(functionFinished()));
QObject::connect(shutdown, SIGNAL(timeout()), thread, SLOT(quit()));
QObject::connect(thread, SIGNAL(finished()), this, SLOT(threadTerminated()));
shutdown->start(20000);
}
Handler::~Handler() {
QObject::disconnect(thread, SIGNAL(started()), worker, SLOT(runAgain()));
QObject::disconnect(timer, SIGNAL(timeout()), worker, SLOT(runAgain()));
QObject::disconnect(worker, SIGNAL(iFinished()), this, SLOT(functionFinished()));
QObject::disconnect(shutdown, SIGNAL(timeout()), thread, SLOT(quit()));
QObject::disconnect(thread, SIGNAL(finished()), this, SLOT(threadTerminated()));
if (shutdown != 0) {
delete shutdown;
shutdown = 0;
}
if (timer != 0) {
delete timer;
timer = 0;
}
if (thread != 0) {
delete thread;
thread = 0;
}
if (worker != 0) {
delete worker;
worker = 0;
}
}
void Handler::functionFinished() {
qDebug() << "Worker has finished. (" << QThread::currentThreadId() << ")";
timer->start(5000);
}
void Handler::threadTerminated() {
qDebug() << "Thread stopped.";
}
void Handler::StartThread() {
thread->start();
}
MyClass (header - testclass.hpp)
#ifndef TESTCLASS_HPP
#define TESTCLASS_HPP
#include <QTimer>
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass();
public slots:
void runAgain();
signals:
void iFinished();
private:
void doWork();
};
#endif // TESTCLASS_HPP
MyClass Source - testclass.cpp
#include "testclass.hpp"
#include <QThread>
#include <QDebug>
MyClass::MyClass() {
}
void MyClass::runAgain() {
doWork();
}
void MyClass::doWork() {
qDebug() << "I WORK...\t(" << QThread::currentThreadId() << ")";
emit iFinished();
}
I've previosly read that it's not a good idea to inherit the classes to be ran inside a thread directly from QThread and so I came up with this solution but it still gets fishy though it's pretty nice. I'm open to any recommendation, this is my first time with QT so better to learn now than feel sorry later.
Oh my bad, I forgot the concrete actual question. Why doesn't the execution ends with a nice exit code of 0?
What I expect is that with handler->StartThread() statement the function within my thread start to write debug messages and once the internal timer within handler finishes I get the nice line [Press ...] and then a return code of 0. However this is not happening.
The reason you're not getting a finished command prompt of "[Press ...]" is due to the QCoreApplication and its exec() call. As the Qt docs state: -
Enters the main event loop and waits until exit() is called
So, you've created a second thread, set it to do some work and finish, but the main thread is still running. You need to exit the main thread.
And of course when I stop the execution of the application, the return code is: -1073741510
It sounds like you're killing the main thread with something like "Ctrl+C". Calling QCoreApplication::exit() when your 2nd thread finishes and has cleaned up, should help here.
I try to implement this: when app is started I need to create multiple threads that would use the same QDialog window to get messages from user. When thread is started, it asks user for input and if button OK pressed, it prints the message to console. I can't figure out why but I get dialog window only once and after that it prints my one message to console and application finishes.
Here's how I describe dialog window:
#include <QtWidgets>
class MyDialog : public QDialog
{
Q_OBJECT
public:
QWaitCondition* condition;
explicit MyDialog(QWidget *parent = 0);
signals:
void got_message(QString);
public slots:
void show_message_input();
void show_message();
private:
QLabel* message_label;
QVBoxLayout* vbox;
QHBoxLayout* hbox;
QLineEdit* message_input;
QDialogButtonBox* dialog_buttons;
};
MyDialog::MyDialog(QWidget *parent) : QDialog(parent)
{
setModal(true);
message_label = new QLabel("Message");
message_input = new QLineEdit();
dialog_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
hbox = new QHBoxLayout();
hbox->addWidget(message_label);
hbox->addWidget(message_input);
vbox = new QVBoxLayout();
vbox->addLayout(hbox);
vbox->addWidget(dialog_buttons);
setLayout(vbox);
connect(dialog_buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(dialog_buttons, SIGNAL(rejected()), this, SLOT(reject()));
condition = new QWaitCondition();
}
void MyDialog::show_message_input()
{
int result = this->exec();
if (result == QDialog::Accepted)
{
emit got_message(message_input->text());
condition->wakeAll();
}
}
Here's MyThread class:
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(int id, MyDialog* window, QObject *parent = 0);
signals:
void show_input();
public slots:
void print_message(QString);
private:
static QMutex mutex;
static QMutex mutex2;
MyDialog* window;
int id;
void run();
void get_captcha_value();
};
QMutex MyThread::mutex;
QMutex MyThread::mutex2;
MyThread::MyThread(int id, MyDialog* window, QObject *parent) :
QThread(parent)
{
this->id = id;
this->window = window;
connect(this, SIGNAL(show_input()), this->window, SLOT(show_message_input()));
}
void MyThread::get_captcha_value()
{
QMutexLocker lock(&mutex);
connect(this->window, SIGNAL(got_message(QString)), SLOT(print_message(QString)));
emit show_input();
mutex2.lock();
window->condition->wait(&mutex2);
mutex2.unlock();
}
void MyThread::run()
{
mutex.lock();
qDebug() << "Starting thread " << id;
mutex.unlock();
get_captcha_value();
mutex.lock();
qDebug() << "Finishing thread " << id;
mutex.unlock();
}
void MyThread::print_message(QString message)
{
qDebug() << message;
QObject::disconnect(this, SLOT(print_message(QString)));
}
And main function:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyDialog* window = new MyDialog();
QList<MyThread*> threads;
for(int i = 0; i < 5; i++)
{
MyThread* thread = new MyThread(i, window);
threads << thread;
thread->start();
}
return a.exec();
}
The first problem you have is that you're inheriting from QThread. Unless you're wanting to re-write how Qt handles threading, you're doing it wrong!.
What you need to do is have a class that inherits from QObject and move the instances to a new thread. The main problem from inheriting QThread is that it can cause confusion about thread affinity (which thread an object is running on).
Also, creating more threads than processor cores is just a waste of resources.
I suggest you read this article on how to use Qt threading and stop inheriting from QThread.
Finally, the use of QMutex is to protect multiple threads accessing the same data simultaneously. You should be able to remove all of them in the code you've shown. Emitting signals with data from one thread to be received by a slot on another thread is the preferred method in Qt.
I have a tool button in one .cpp (i.e. ExerciseControl.cpp). When the button is clicked, it will trigger two timers in another .cpp (i.e. StatusBar.cpp).
However, the following codes does not work. The timers do not start when I click the button in ExerciseControl.cpp to trigger startExercise().
ExerciseControl.cpp:
ExerciseControl::ExerciseControl(void)
{
myStatusBar = new StatusBar;
}
void ExerciseControl::startExercise()
{
myStatusBar ->simulationTimer->start(1000);
myStatusBar ->elapsedTimer->start(1000);
}
StatusBar.cpp:
StatusBar::StatusBar()
{
simulationTimer = new QTimer;
QObject::connect(simulationTimer, SIGNAL(timeout()), this, SLOT(tickSimulation()));
elapsedTimer = new QTimer;
QObject::connect(elapsedTimer, SIGNAL(timeout()), this, SLOT(tickSimulation()));
createButtons();
};
void StatusBar::createButtons()
{
...
}
void StatusBar::tickSimulation()
{
...
}
However, if I declare the following in my constructor, the timers can start automatically, which is not I wanted.
StatusBar::StatusBar()
{
simulationTimer = new QTimer;
simulationTimer->start(1000);
QObject::connect(simulationTimer, SIGNAL(timeout()), this, SLOT(tickSimulation()));
elapsedTimer = new QTimer;
elapsedTimer->start(1000);
QObject::connect(elapsedTimer, SIGNAL(timeout()), this, SLOT(tickSimulation()));
}
I need to trigger the timers through my button in ExerciseControl.cpp.
Please help.
Thanks.
On the example below, ExerciseControl is a specialization of QMainWindow that does 2 things:
Display a QTooButton inside the window;
Allocate a StatusBar object.
Clicking the button invokes the slot _timers_must_start() of the class. This slot is responsible to call start_timers() from the StatusBar object, which in turn starts the timers of that object.
I made each timer call a slot that prints something to the screen when the timeout() is triggered.
Enjoy.
main.cpp:
#include "exercise_control.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
ExerciseControl ec;
ec.show();
return app.exec();
}
exercise_control.h:
#pragma once
#include "status_bar.h"
#include <QMainWindow>
#include <QToolButton>
class ExerciseControl : public QMainWindow
{
Q_OBJECT
public:
ExerciseControl()
{
_button = new QToolButton(this);
_button->setText("Press Me!");
QObject::connect(_button, SIGNAL(clicked()), this, SLOT(_timers_must_start()));
_statusbar = new StatusBar;
// Set the size of the window to the size of the button
setFixedSize(_button->width() ,_button->height());
}
private slots:
/* This slot is called by clicking the button */
void _timers_must_start()
{
qDebug() << "ExerciseControl::_timers_must_start: button clicked! Trying to start timers...";
_statusbar->start_timers();
}
private:
QToolButton* _button;
StatusBar* _statusbar;
};
status_bar.h:
#pragma once
#include <QObject>
#include <QTimer>
#include <QDebug>
class StatusBar : public QObject
{
Q_OBJECT
public:
StatusBar()
{
_timer1 = new QTimer;
QObject::connect(_timer1, SIGNAL(timeout()), this, SLOT(_timer1_task()));
_timer2 = new QTimer;
QObject::connect(_timer2, SIGNAL(timeout()), this, SLOT(_timer2_task()));
}
~StatusBar()
{
_timer1->stop();
delete _timer1;
_timer2->stop();
delete _timer2;
}
void start_timers()
{
if (!_timer1->isActive() && !_timer2->isActive())
{
qDebug() << "StatusBar::start_timers: OK!";
_timer1->start(1000); // 1 second interval
_timer2->start(5000); // 5 seconds interval
return;
}
qDebug() << "StatusBar::start_timers: timers already started, nothing to be done here!";
}
private:
QTimer* _timer1;
QTimer* _timer2;
private slots:
/* These slots are called by the timers of this class, hence PRIVATE SLOTS */
void _timer1_task()
{
qDebug() << "StatusBar::_timer1_task: 1 second has elapsed";
}
void _timer2_task()
{
qDebug() << "StatusBar::_timer2_task: 5 seconds has elapsed";
}
};
test.pro:
QT += widgets
SOURCES += \
main.cpp
HEADERS += \
exercise_control.h \
status_bar.h