How to Create a Window in Different Qt Threads

How do I create a Window in different QT threads?

If you need to create QWidget(or some other gui component(s)) in different(non-main) thread(s) you can implement it in such way:

  • Create simple wrapper which holds gui component:

    // gui component holder which will be moved to main thread
    class gui_launcher : public QObject
    {
    QWidget *w;
    // other components
    //..
    public:
    virtual bool event( QEvent *ev )
    {
    if( ev->type() == QEvent::User )
    {
    w = new QWidget;
    w->show();
    return true;
    }
    return false;
    }
    };
  • create QApplication object in main thread

  • another thread body:

    ..
    // create holder
    gui_launcher gl;
    // move it to main thread
    gl.moveToThread( QApplication::instance()->thread() );
    // send it event which will be posted from main thread
    QCoreApplication::postEvent( &gl, new QEvent( QEvent::User ) );
    ..
  • be happy, :)

Create QMainWindow from different thread

Qt will only let you create widgets from the main GUI thread, this is explicitly mentioned in the docs (emphasis mine):

As mentioned, each program has one thread when it is started. This
thread is called the "main thread" (also known as the "GUI thread" in
Qt applications). The Qt GUI must run in this thread. All widgets and
several related classes, for example QPixmap, don't work in secondary
threads.
A secondary thread is commonly referred to as a "worker
thread" because it is used to offload processing work from the main
thread.

How to create QtQuick window outside the main thread

If you want a QQuickView to live outside the main() thread, then you must:

  1. Create a new std::thread (NOT a QThread, because it is a QObject so it mustn't be created before your QGuiApplication)
  2. Run an init() function in that thread. Let it instantiate your QGuiApplication and start the event loop.
  3. Create your QQuickView in that thread too.
  4. Ensure that all of your GUI objects are accessed from that thread only.
  5. Ensure that your main() thread doesn't create any QObjects until after your QGuiApplication has been created in your other thread.

See How to avoid Qt app.exec() blocking main thread for more details.

How can I start a GUI window from a QThread?

Yes, you are going about this in the wrong way. GUIs, due to platform limits, are single threaded systems. You cannot create, change and manage GUI objects on different threads - everything must be done on one thread (normally, the GUI thread).

Qt has two mechanisms for dealing with worker threads and the GUI: queued signals and slots, and the QCoreApplication::postEvent() handler.

More details are in the comprehensive Qt threading document: http://doc.qt.io/qt-5/thread-basics.html

Qt - updating main window with second thread

but the problem is that, i cannot reach the
ana->ui->horizontalLayout_4->addWidget(label);

Put your UI modifications in a slot in your main window, and connect a thread signal to that slot, chances are it will work. I think only the main thread has access to the UI in Qt. Thus if you want GUI functionality, it must be there, and can be only signaled from other threads.

OK, here is a simple example. BTW, your scenario doesn't really require to extend QThread - so you are better off not doing it, unless you really have to. That is why in this example I will use a normal QThread with a QObject based worker instead, but the concept is the same if you subclass QThread:

The main UI:

class MainUI : public QWidget
{
Q_OBJECT

public:
explicit MainUI(QWidget *parent = 0): QWidget(parent) {
layout = new QHBoxLayout(this);
setLayout(layout);
QThread *thread = new QThread(this);
GUIUpdater *updater = new GUIUpdater();
updater->moveToThread(thread);
connect(updater, SIGNAL(requestNewLabel(QString)), this, SLOT(createLabel(QString)));
connect(thread, SIGNAL(destroyed()), updater, SLOT(deleteLater()));

updater->newLabel("h:/test.png");
}

public slots:
void createLabel(const QString &imgSource) {
QPixmap i1(imgSource);
QLabel *label = new QLabel(this);
label->setPixmap(i1);
layout->addWidget(label);
}

private:
QHBoxLayout *layout;
};

... and the worker object:

class GUIUpdater : public QObject {
Q_OBJECT

public:
explicit GUIUpdater(QObject *parent = 0) : QObject(parent) {}
void newLabel(const QString &image) { emit requestNewLabel(image); }

signals:
void requestNewLabel(const QString &);
};

The worker object is created and moved to another thread, then connected to the slot that creates the labels, then its newLabel method is invoked, which is just a wrapper to emit the requestNewLabel signal and pass the path to the image. The signal is then passed from the worker object/thread to the main UI slot along with the image path parameter and a new label is added to the layout.

Since the worker object is created without parent in order to be able to move it to another thread, we also connect the thread destroyed signal to the worker deleteLater() slot.

QtCreator C++ create thread in main window

As discussed in the comments, in this use-case it's preferable to not use threading, but exploit the Qt event loop and signal-slot mechanism. Here is the skeleton of the MainWindow and the SerialReciver classes, and how they are wired together in main.cpp. For simplicity, the SerialReceiver class just emits the signal every second with the current time, which will be appended to the editfield's content in the main window.

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPlainTextEdit>

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void onSerialMessage(const QString &msg);
private:
QPlainTextEdit mTextField;

};

mainwindow.cpp:

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
mTextField.setReadOnly(true);
setCentralWidget(&mTextField);
}

MainWindow::~MainWindow()
{

}
void
MainWindow::onSerialMessage(const QString &msg)
{
mTextField.appendPlainText(msg);
}
#endif // MAINWINDOW_H

serialreceiver.h:

#ifndef SERIALRECEIVER_H
#define SERIALRECEIVER_H

#include <QObject>
#include <QTimer>

class SerialReceiver : public QObject
{
Q_OBJECT
public:
explicit SerialReceiver(QObject *parent = nullptr);

signals:
void newMsg(const QString &msg);

public slots:
void onSerialReceived();
private:
QTimer mTimer;
};

#endif // SERIALRECEIVER_H

serialreceiver.cpp:

#include "serialreceiver.h"
#include <QDateTime>

SerialReceiver::SerialReceiver(QObject *parent) : QObject(parent)
{
mTimer.setInterval(1000);
mTimer.setSingleShot(false);
connect(&mTimer, &QTimer::timeout,this,&SerialReceiver::onSerialReceived);
mTimer.start();
}
void
SerialReceiver::onSerialReceived()
{
QDateTime now = QDateTime::currentDateTime();
emit newMsg(now.toString());
}

and main.cpp:

#include "mainwindow.h"
#include "serialreceiver.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SerialReceiver receiver;
MainWindow w;
QObject::connect(&receiver, &SerialReceiver::newMsg,
&w,&MainWindow::onSerialMessage);
w.show();
return a.exec();
}

Does creating a new Qt Window with no parent also create a new thread?

  1. No new thread is created for mainWindow
  2. The mainWindow event loop is executed in scope of a.exec() - it blocks until application exits (for example - last top-level window is closed).

So mainWindow does not go out of scope, because it is main that executes everything.

Check it using code like:

std::cout << "starting application event loop" << std::endl;
const int ret = a.exec();
std::cout << "after exec" << std::endl; // or any other code here
return ret;

From QApplication doc:

Enters the main event loop and waits until exit() is called, then returns the value that was set to exit() (which is 0 if exit() is called via quit()).



Related Topics



Leave a reply



Submit