0

I want to show a widget for displaying an animated loading gif while another function (pthread) computes a task.

I have tried to subclass my widget with a QThread class, and implemented the method run() where I call the method show(). However, my widget GUI froze.

How can I launch a widget where the GUI is processed separately?

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
user2543127
  • 385
  • 10
  • 21
  • Please post some minimal code sample that reproduces problem – Kamil Klimek Apr 10 '14 at 13:48
  • You can take a look at Qt `QProgressDialog` class. If you need that the widget do not block the main GUI, you can run it in non modal mode? – vahancho Apr 10 '14 at 13:50
  • 1
    I think that everyone is somewhat confused by the question's poor wording. Here's how I see it: the asker tries to do something in a worker thread while showing some progress in the gui thread. He has experimentally and correctly determined that the naive approach with direct calls to gui objects won't work. The question essentially reduces to: how can I "do gui things" from another thread. – Kuba hasn't forgotten Monica Apr 10 '14 at 13:56

2 Answers2

5

You can't have widgets running on anything except the main thread.

Also, unless you're wanting to change how Qt handles threads, you should not be inheriting from QThread.

Instead, create a worker object that inherits from QObject and move that to the new thread. You can read how to really use QThread here.

Your worker object can then be moved to another thread, do its computation and communicate to the Gui widgets, on the main thread, via signals and slots.

For example, here's a brief outline of a worker class: -

class Worker : public QObject
{
    Q_OBJECT        

    signals:
        void finished();

        void displayWidget();

    private slots:
        void run();
}

QThread pThread = new QThread;
Worker pObject = new Worker;

// move the pObject to the thread
pObject->moveToThread(pThread);  

You can then control the thread with signals and slots.

// assuming you've added a run slot function to the Consumer class
connect(pThread, SIGNAL(started()), pObject, SLOT(run()));
connect(pObject, SIGNAL(finished()), pThread, SLOT(quit()));
connect(pObject, SIGNAL(finished()), pObject, SLOT(deleteLater()));

// Note the thread cleans itself up here, but if the app is quitting,
// waiting on the thread to finish may be required instead
connect(pThread, SIGNAL(finished()), pThread, SLOT(deleteLater()));

And start the thread: -

pThread->start();

Used this way, it also enables multiple objects to be moved to a single new thread, rather than creating a new thread per object instance.

So now, for example, if you wanted to display a widget at some point during the processing in the worker object, you'd emit its displayWidget() signal, having previously connected it to the Widget's show() slot.

QWidget* pWidget = new QWidget(parent); // assumes parent is initialized
// using Qt 5 connect call
connect(pWorker, &Worker::displayWidget, pWidget, &Widget::show);
TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • didn't you notice that user2543127 subclassed his widget with QThread which is root of the evil he tried? UI can't be accessed/created from outside of UI thread (main thread). – Kamil Klimek Apr 10 '14 at 13:48
  • Yes, that's why I suggest he creates a worker object inherited from QObject. – TheDarkKnight Apr 10 '14 at 13:49
  • But you didn't explain to much with this answer – Kamil Klimek Apr 10 '14 at 13:49
  • The OP didn't show any example code to work with. Rather than regurgitate the information from an excellent article, I linked to it instead. All the information is there. – TheDarkKnight Apr 10 '14 at 13:51
  • @Merlin069 None of this addresses the asker's problem: how to invoke `widget->show()` from a non-GUI thread. The mention of "signals and slots" seems to go in this direction, but then abruptly stops. The recommendation on how to properly use `QThread` is, while valid, somewhat off-topic here. Pedagogy dictates not to invoke too many new things all at once. – Kuba hasn't forgotten Monica Apr 10 '14 at 14:00
  • @KubaOber, I was in the process of revising the answer, but see you've already answered it. – TheDarkKnight Apr 10 '14 at 14:03
  • You can of course always post an SSCCE example, I don't have the time to write one out now :) In fact, I won't write one if you or someone else comes up with it within 7 days from now :) – Kuba hasn't forgotten Monica Apr 10 '14 at 14:04
  • @KubaOber, that's what I was doing ;O) – TheDarkKnight Apr 10 '14 at 14:04
1

You can't use QWidget (nor any derived classes) directly from threads other than the GUI thread. All you can do directly is to use a QImage owned by the worker thread and paint on it directly from that thread. Here, directly means that you are simply calling methods of objects.

What you need is a way to execute show() not directly in the invoking thread, but indirectly within the GUI thread's context. This is quite simple since QWidget::show() is a slot. Thus, from your computation thread, simply do:

QMetaObject::invokeMethod(widget, "show");

That's all. The implementation of invokeMethod will determine that widget lives in a different thread, and will automatically choose the QueuedConnection method of call delivery. Internally, it will post a QMetaCallEvent to widget. The widget's QObject::event method will act on the event and place the call to the show method. This will happen in the GUI thread.

You can use the same approach to set QProgressBar, for example:

int value = ...;
QMetaObject::invokeMethod(progressBar, "setValue", Q_ARG(int, value));
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Thanks for your hel, i have try to instantiate my QObject: LoadingWindow* load_window = new LoadingWindow() and call the function QMetaObject::invokeMethod(load_window,"slot_openWindow"); where my "slot_openWindow" call method show of a qlabel, but the GUI freeze – user2543127 Apr 10 '14 at 14:13
  • @user2543127 You can't instantiate that window in the non-GUI thread. You must have an instance already existing. – Kuba hasn't forgotten Monica Apr 10 '14 at 16:20
  • I didn't solve my problem! :( You can provide me a simple full example to show me how a widget can displayed keeping his gui responsive while a task function work for long time? Thanks – user2543127 Apr 11 '14 at 08:38
  • I wrote [a photographic mosaic example](http://stackoverflow.com/a/22674903/1329652) that is does all computations and image I/O in worker threads. The GUI is responsive at all times. I have many other examples, feel free to search for my answers with the keyword QThread. – Kuba hasn't forgotten Monica Apr 11 '14 at 13:23