qt5: how to create and display custom qdialog from static function within a qthread - qt

Let's say that you've created a new thread that then calls a static function after it has been started. Within that static function you need to create and display a custom qdialog. How can you create it so that it has no parent and is located in the proper thread?
The constructor sets the parent to 0 but it still reports an error about being unable to create children for a parent in a different thread. Since it's a static function I can't use the "this" object and without "this" I can't retrieve the current thread or thread id. I thought I might be able to call myCustomDialog->moveToThread() but I have no idea how to determine the correct thread from a static function.
If I use one of the QMessageBox static functions everything works fine. For example, calling QMessageBox::information(0, tr("Title"), tr("Message")) doesn't report any errors. How can I code my custom qdialog to function similar to the qmessagebox static function?
Is there any way to retrieve a list of all running threads from the qApp object? Any other suggestions?
static int myFunction();
int myObject::myFunction()
{
myCustomDialog *mcd = new myCustomDialog();
// as soon as I call exec() it reports an error and crashes
mcd->exec();
// QObject: Cannot create children for a parent that is in a different thread.
// can't call mcd->moveToThread() without knowing the current thread
// how can I determine the current thread from this static function?
// if parent = 0 then why is this error occurring?
return 0;
}
myCustomDialog(QWidget *parent = 0);
myCustomDialog::myCustomDialog(QWidget *parent) : QDialog(parent)
{
// create widgets and layout here
}

Don't. Creating QWidget objects from another thread is a bad idea. The Documentation states:
In GUI applications, the main thread is also called the GUI thread
because it's the only thread that is allowed to perform GUI-related
operations.
The way I normally get around this is to emit a signal from my object in the non-GUI thread that's connected to an object living in the main thread (often MainWindow for me). The receiving object then creates the dialog on the main thread.
As mentioned in the other answer, this connection can block your worker thread if needed by establishing the connection type as Qt::BlockingQueuedConnection.
For more information on invoking functions from other threads, see this post.

Sample... You may adapt it to make calls from static functions, but I don't think that it is necesary. It is good practice to work with threads in next way: You should create your dialog in GUI thread and connect it's exec() slot to your signal with Qt::BlockingQueuedConnection
Worker.h
#include <QObject>
class Worker
: public QObject
{
Q_OBJECT
signals:
void showDialog();
public:
Worker( QObject *parent = NULL );
~Worker();
public slots:
void doLongWork();
private:
};
Worker.cpp
#include <QThread>
#include <QMessageBox>
#include <QDebug>
#include <QCoreApplication>
#include <Windows.h>
Worker::Worker( QObject *parent )
: QObject( parent )
{
}
Worker::~Worker()
{
}
void Worker::doLongWork() // You may override QThread::run with same effect, but it is bad practice
{
qDebug() << "Worker thread id = " << QThread::currentThreadId();
::MessageBoxA( NULL, "Click to show dialog in 3 seconds", NULL, 0 );
::Sleep( 3000 );
emit showDialog(); // "Showing" dialog from non-GUI thread. And wait for close
::MessageBoxA( NULL, "Dialog closed!", NULL, 0 );
qApp->quit();
}
main.cpp
#include <QApplication>
#include <QDialog>
#include <QThread>
#include <QDebug>
#include "Worker.h"
int main(int argc, char *argv[])
{
QApplication a( argc, argv );
a.setQuitOnLastWindowClosed( false );
QDialog dlg;
QThread workerThread;
Worker worker;
qDebug() << "Main thread id = " << QThread::currentThreadId();
QObject::connect( &workerThread, SIGNAL( started() ), &worker, SLOT( doLongWork() ) );
QObject::connect( &worker, SIGNAL( showDialog() ), &dlg, SLOT( exec() ), Qt::BlockingQueuedConnection ); // !!!See connection type!!!
worker.moveToThread( &workerThread );
workerThread.start();
return a.exec();
}
Note: WinAPI MessageBoxes are used only to visualize that thread is really waiting for close of dialog.

Related

Qt: using QWidgets in non GUI threads

I'm trying to understand what is and isn't allowed when it comes to QWidget and Qt concurrency. I've created a Widget which has a slow_function and I'm considering three cases:
Run the slow_function on the GUI thread. This results in the expected behaviour; the GUI becomes unresponsive while waiting for the function to return.
Use QtConcurrent::run(this, &Widget::slow_function). I was surprised to see that this didn't block the GUI. I've confirmed that the thread affinity of my instance is still the GUI thread, nevertheless, the function seems to be executing on a separate thread. Is such an approach allowed and is this the expected behaviour (documentation link would be really helpful)? Is such an approach safe if I can guarantee that slow_function is thread-safe?
Create a subclass of QThread which holds a pointer to my widget. Override the run method to call slow_function. The behaviour is the same as Case 2. This is also surprising as the thread affinity is still the GUI thread (besides, we are not even allowed to use moveToThread on a QWidget). Why is this running on a separate thread? Is moveToThread meant to be useful only when we are interested in calling slots via signals sent from another thread?
Thank you for reading. Here is the relevant code starting with my the header file:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QDebug>
#include <QPushButton>
#include <QLayout>
#include <windows.h>
#include <QtConcurrent/QtConcurrent>
#include <QThread>
#include <QApplication>
class Widget;
class Thread: public QThread
{
public:
Thread(Widget* widget)
: m_widget(widget){}
protected:
void run() override;
private:
Widget* m_widget;
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr)
: QWidget(parent)
, m_thread(this){
auto layout = new QVBoxLayout(this);
auto button = new QPushButton("Case 1: Run on gui thread");
auto button2 = new QPushButton("Case 2: Run with qtconcurrent");
auto button3 = new QPushButton("Case 3: Run with qthread");
connect(button, &QPushButton::clicked, this, &Widget::slow_function);
connect(button2, &QPushButton::clicked, this, &Widget::use_concurrent);
connect(button3, &QPushButton::clicked, this, &Widget::use_qthread);
layout->addWidget(button);
layout->addWidget(button2);
layout->addWidget(button3);
}
~Widget()
{
m_thread.quit();
m_thread.wait();
}
public slots:
void slow_function()
{
qDebug() << "Starting";
auto gui_thread = QApplication::instance()->thread();
auto this_thread = thread();
qDebug() << "Thread affinity is" << (gui_thread == this_thread ? "gui_thread" : "non_gui_thread");
Sleep(5000);
qDebug() << "Finished";
}
void use_concurrent()
{
QtConcurrent::run(this, &Widget::slow_function);
}
void use_qthread()
{
m_thread.start();
}
private:
Thread m_thread;
};
#endif // WIDGET_H
and the main.cpp file:
#include "widget.h"
#include <QApplication>
void Thread::run()
{
m_widget->slow_function();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
You should not do any UI things in nonmain threads. The UI means
widget interaction
model
Run the slow_function on the GUI thread
That's not allowed, nothing should block the GUI thread.
Is such an approach safe if I can guarantee that slow_function is thread-safe?
It's okay to run slow function in a different thread. But...
what happens when the user closes the application, but the function still executes?
I have done this, but I prefer to encapsulate it in a separate class.
Create a subclass of QThread which holds a pointer to my widget.
You should only subclass QThread if the subclass is a thread. Like, subclassing an animal class will give you an animal, not a chair with four legs.
Is moveToThread meant to be useful only when we are interested in calling slots via signals sent from another thread?
moveToThread changes the affinity of the object. So, slots are always executed in the correct thread, when you call invokeMethod, the method is executed in the correct thread. When an event is delivered, the event handler is always called in the appropriate thread.
The whole purpose of QtConcurrent::run() is to make running heavy workloads in a thread easier. If your use-case allows for it, great!
Pair it with a QFutureWatcher to retrieve the result after your slow_function is finished.
Using QThread is another option, but it makes more sense when you have a long-lived object. Instead of subclassing, I find it easier to use the worker model: create a worker class with signals/slots, call moveToThread on it with a vanilla QThread object, connect/subscribe, start the thread
QWidget is a way to create GUI functionality in Qt. While it may be possible to use such objects in non-gui threads, it's best you separate your compute workloads from GUI (don't put your slow_function into widget classes).

Why is my mainwindow closing automatically when called from a differnet class?

I can't seem to figure out what went wrong so I'm here to ask you. I have made a simple class called BOBSNetworkSessionManager defined below. It is a simple class that inherits the QOBject so that I can use signals and slots but it does not have a dialog or any kind of window associated with it. It will eventually call a log in dialog and use the credentials to connect to a tcp server that I have created. This class serves as a layer to manage the connection state of the program because it will only run properly when connected to the server and when being used within 15 minutes without break due to p.c.i. compliance. If these conditions are not true this class will lock the window and force a new login. As of right now I just try to arbitrarily open the main window as though credentials had passed and i wasbconnected to the server. The problem is when I open the mainwindow it disapears right away. I cannot seem to figure out why it is diappearing. I have included all of my files.
BOBSDCNetworkSessionManager .h header file
#ifndef BOBSDCNETWORKSESSIONMANAGER_H
#define BOBSDCNETWORKSESSIONMANAGER_H
#include <QObject>
#include <QSettings>
class BOBSDCNetworkSessionManager : public QObject
{
Q_OBJECT
public:
explicit BOBSDCNetworkSessionManager(QObject *parent = 0);
protected:
void destroyed(QObject *);
signals:
public slots:
private:
void readSettings();
void writeSettings();
QSettings networkSettings;
};
#endif // BOBSDCNETWORKSESSIONMANAGER_H
BOBSDCNetworkSessionManager Implementation .cpp file
#include "bobsdcnetworksessionmanager.h"
#include "bobsmainwindow.h"
BOBSDCNetworkSessionManager::BOBSDCNetworkSessionManager(QObject *parent) :
QObject(parent)
{
BOBSMainWindow w;
w.show();
}
Main.cpp file
#include "bobsdcnetworksessionmanager.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setApplicationName("Enterprise Management Suite");
a.setApplicationVersion("Beta Version: 0.0.0.01");
a.setOrganizationName("Enigma Web Consulting");
a.setOrganizationDomain("http://www.EnigmaWebCo.com");
BOBSDCNetworkSessionManager netMgr;
return a.exec();
}
The problem is here:
{
BOBSMainWindow w;
w.show();
}
w.show() is not a blocking call. So you're creating a window, showing it, and then it immediately is destructed when it goes out of scope. You should either declare w as a member variable or construct it on the heap:
BOBSMainWindow *w = new BOBSMainWindow(this);

(Qt) Unusable window after one button click

I have a window with many buttons. Each one triggers a sub-program (written using the Opencv API). Each sub-program displays images and stuff on windows.
The problem is, when I close these windows (via the little red cross), all the buttons become unclickable. So if I want to launch another program, I'll have to exit the main window and run it again.
In other words, I want to be able to run all the sub-programs without having to start over every time.
Here's the GUI's code :
.cpp
#include "fenprincipale.h"
#include "ui_fenprincipale.h"
#include<highgui.h>
#include<cv.h>
#include <moyenetmedian.h>
#include<morpho.h>
#include<tracking.h>
#include<contour.h>
#include<QApplication>
FenPrincipale::FenPrincipale(QWidget *parent) :
QWidget(parent),
ui(new Ui::FenPrincipale)
{
ui->setupUi(this);
MoyenEtMedian *moyenEtMedian = new MoyenEtMedian;
morpho * mor = new morpho;
tracking * tra= new tracking;
contour * cont= new contour;
QObject::connect(ui->bMoyMed, SIGNAL( clicked() ), moyenEtMedian, SLOT( exec() ), Qt::AutoConnection );
QObject::connect(ui->bMorph, SIGNAL( clicked() ), mor, SLOT( exec() ), Qt::AutoConnection );
QObject::connect(ui->bTrack, SIGNAL( clicked() ), tra, SLOT( exec() ), Qt::AutoConnection );
QObject::connect(ui->bCont, SIGNAL( clicked() ), cont, SLOT( exec() ), Qt::AutoConnection );
}
FenPrincipale::~FenPrincipale()
{
delete ui;
}
.h :
#ifndef FENPRINCIPALE_H
#define FENPRINCIPALE_H
#include <QWidget>
#include <QApplication>
namespace Ui {
class FenPrincipale;
}
class FenPrincipale : public QWidget
{
Q_OBJECT
public:
explicit FenPrincipale(QWidget *parent = 0);
void switch_callback(int);
void execMoyMed (void);
~FenPrincipale();
private:
Ui::FenPrincipale *ui;
};
#endif // FENPRINCIPALE_H
the main class :
#include <QCoreApplication>
#include <QApplication>
#include <QtGui>
#include <QWidget>
#include "fenprincipale.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
FenPrincipale fenetre;
fenetre.show();
return a.exec();
}
Slot implementation for "moyenetmedian" :
void MoyenEtMedian::exec(void)
{
const char* name = "Filtres";
IplImage* img = cvLoadImage( "C:/Users/XELTINFO/ProjetVision/image.png" );
IplImage* out = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 3 );
cvNamedWindow( name, 1 );
cvShowImage(name, out);
// Create trackbar
cvCreateTrackbar2( "Filtre", name, &g_switch_value, 1, &MoyenEtMedian::switch_callback, this );
while( 1 ) {
switch( filterInt ){
case 0:
cvSmooth( img, out, CV_BLUR, 7, 7 );
break;
case 1:
cvSmooth( img, out, CV_MEDIAN, 7, 7 );
break;
}
if(filterInt != lastfilterInt){
cvShowImage(name, out);
lastfilterInt = filterInt;
}
if( cvWaitKey( 15 ) == 27 )
break;
}
cvReleaseImage( &img );
cvReleaseImage( &out );
cvDestroyWindow( name );
}
The class declaration :
#ifndef MOYENETMEDIAN_H
#define MOYENETMEDIAN_H
#include "ui_fenprincipale.h"
#include<QObject>
class MoyenEtMedian : public QObject
{
Q_OBJECT
public:
MoyenEtMedian();
static void switch_callback(int position, void*);
public slots :
void exec(void);
};
#endif // MOYENETMEDIAN_H
The class delcarations and slots implementations are very similar for all classes. I'll add the rest if this isn't enough.
You are blocking the event loop in your exec() slot, since it doesn't return immediately. You should instead subclass QWidget and override keyPressEvent() to get keyboard input from Qt's event loop instead of doing the busy-loop you currently have.
So when using Qt with OpenCV, I would setup the polling using Qt's timers instead of a while loop.
There is a pretty good tutorial of using QTimers to interact with OpenCV objects here:
http://www.youtube.com/watch?v=0ONxIy8itRA
Jump to 35 or 38 minutes into it to see how he writes his classes.
Basically, you let Qt do the waiting and timing, instead of having a while loop with a wait call doing the timing.
And if possible, let Qt create the windows, and nest the OpenCV windows into Qt's windows so that Qt can manage the events on the windows.
Hope that helps.

Qt connect two signals together using QueuedConnection

Qt documentation states that it is possible to connect two signals together:
It is even possible to connect a signal directly to another signal.
I tried:
connect(x, SIGNAL(S()), y, SIGNAL(func()));
and it works as mentioned, but Qt documentation continues:
(This will emit the second signal immediately whenever the first is emitted.)
Does this mean that QueuedConnection will not work correctly? Can I connect two signals across threads?
The reason I am asking this is because I solved a class of crashes on an application by avoiding this, but I am not sure if this was related to connecting signals together.
It shouldn't be a great deal different from a signal/slot connection. Let's take a look at underlying mechanism of signals/slots. There is an event queue in each thread which maintains signals (events) that have been emitted but not processed yet. So whenever the execution returns to the event loop the queue is processed. Event loop itself doesn't handle the events. Rather it delivers them to the objects so they can handle it. In this special case, I suppose that the object would emit another signal which would be inserted in the queue. When the execution returns to event loop the new signal is handled by the object again. Here is a test which proves the above argument.
If you run the codes attached, the output would be:
before signal()
after signal()
slot() called
which means defining a signal-signal connection type as queued between threads have the expected queued behaviour, that rejects the argument which it is always immediate. If you define it as direct, the output would be:
before signal()
slot() called
after signal()
as expected. it doesn't generate any errors or warnings, and program doesn't crash as well.Yet this simple example doesn't prove it works for a large and complex one as well.
main.cpp:
#include <QtGui/QApplication>
#include "dialog.h"
#include "testssconnection.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestSignalSignalConnection * t = new TestSignalSignalConnection();
t->start();
return a.exec();
}
testssconnection.h:
#ifndef TESTSSCONNECTION_H
#define TESTSSCONNECTION_H
#include <QObject>
#include <QThread>
class TestSignalSignalConnection : public QThread
{
Q_OBJECT
public:
explicit TestSignalSignalConnection(QObject *parent = 0);
void run();
signals:
void signal1();
void signal2();
public slots:
void slot();
};
#endif // TESTSSCONNECTION_H
testssconnection.cpp:
#include "testssconnection.h"
#include <QtCore>
TestSignalSignalConnection::TestSignalSignalConnection(QObject *parent) :
QThread(parent)
{
}
void TestSignalSignalConnection::run()
{
TestSignalSignalConnection *t = new TestSignalSignalConnection();
this->connect(this,SIGNAL(signal1()),t,SIGNAL(signal2()), Qt::QueuedConnection);
t->connect(t,SIGNAL(signal2()), t,SLOT(slot()), Qt::DirectConnection);
qDebug() << "before signal()";
emit signal1();
qDebug() << "after signal()";
exec();
}
void TestSignalSignalConnection::slot()
{
qDebug() << "slot() called";
}
Take a look at qt-project, its a great wiki page about Threads and signals.
Threads, Events and QObjects::Signals and slots across threads

QT + How to call slot from custom C++ code running in a different thread

I am new to QT and I am doing some learning.
I would like to trigger a slot that modify a GUI widget from a C++ thread(Currently a Qthread).
Unfortunatly I get a: ASSERTION failed at: Q_ASSERT(qApp && qApp->thread() == QThread::currentThread());
here is some code:
(MAIN + Thread class)
class mythread : public QThread
{
public:
mythread(mywindow* win){this->w = win;};
mywindow* w;
void run()
{
w->ui.textEdit->append("Hello"); //<--ASSERT FAIL
//I have also try to call a slots within mywindow which also fail.
};
};
int main(int argc, char *argv[])
{
QApplication* a = new QApplication(argc, argv);
mywindow* w = new mywindow();
w->show();
mythread* thr = new mythread(w);
thr->start();
return a->exec();
}
Window:
class mywindow : public QMainWindow
{
Q_OBJECT
public:
mywindow (QWidget *parent = 0, Qt::WFlags flags = 0);
~mywindow ();
Ui::mywindow ui;
private:
public slots:
void newLog(QString &log);
};
So I am curious on how to update the gui part by code in a different thread.
Thanks for helping
stribika got it almost right:
QMetaObject::invokeMethod( textEdit, "append", Qt::QueuedConnection,
Q_ARG( QString, myString ) );
cjhuitt's right, though: You usually want to declare a signal on the thread and connect it to the append() slot, to get object lifetime management for free (well, for the price of a minor interface change). On a sidenote, the additional argument:
Qt::QueuedConnection ); // <-- This option is important!
from cjhuitt's answer isn't necessary anymore (it was, in Qt <= 4.1), since connect() defaults to Qt::AutoConnection which now (Qt >= 4.2) does the right thing and switches between queued and direct connection mode based on QThread::currentThread() and the thread affinity of the receiver QObject at emit time (instead of sender and receiver affinity at connect time).
In addition to stribika's answer, I often find it easier to use a signal/slot connection. You can specify that it should be a queued connection when you connect it, to avoid problems with the thread's signals being in the context of its owning object.
class mythread : public QThread
{
signals:
void appendText( QString );
public:
mythread(mywindow* win){this->w = win;};
mywindow* w;
void run()
{
emit ( appendText( "Hello" ) );
};
};
int main(int argc, char *argv[])
{
QApplication* a = new QApplication(argc, argv);
mywindow* w = new mywindow();
w->show();
mythread* thr = new mythread(w);
(void)connect( thr, SIGNAL( appendText( QString ) ),
w->ui.textEdit, SLOT( append( QString ) ),
Qt::QueuedConnection ); // <-- This option is important!
thr->start();
return a->exec();
}
You need to use QMetaObject::invokeMethod. For example:
void MyThread::run() {
QMetaObject::invokeMethod(label, SLOT(setText(const QString &)), Q_ARG(QString, "Hello"));
}
(The above code comes from here: http://www.qtforum.org/article/26801/qt4-threads-and-widgets.html)
I don't think you are allowed to call directly things that results in paint events from any
other threads than the main thread. That will result in a crash.
I think you can use the event loop to call things asynchronously so that the main gui thread picks up and then does the updating from the main thread, which is what cjhuitt suggests.

Resources