2

For example:

#!/usr/bin/env python3

import sys
from PySide import QtCore, QtGui

class Dialog(QtGui.QDialog):
    def __init__(self):
        QtGui.QDialog.__init__(self)
        button = QtGui.QPushButton("test")
        layout = QtGui.QVBoxLayout()
        layout.addWidget(button)
        self.setLayout(layout)

app = QtGui.QApplication(sys.argv)
toast = Dialog()
toast.show()
app.exec_()
print("App freezes the main process!")

The last print() function will not be executed until you close the dialog.

I am working on a script that only uses qt for displaying some content that does not require user interaction, so I would prefer the gui code runs in background.

qed
  • 22,298
  • 21
  • 125
  • 196
  • You must have something that will make the program *not* end. What do you expect to happen after the execution reaches the end of the script? Rather make the printing and other tasks you wish to perform simultaneously in a separate thread - but do consider, whether a separate thread is *really* needed. It's a common misconception when using event-driven frameworks. – BartoszKP Nov 12 '14 at 15:28
  • I would rather prefer create a thread for the gui part and make it run the the background, because in this case the main logic and interest is not in the gui. – qed Nov 12 '14 at 15:36
  • It's sort of like a notification bubble, you make it pop up, and set timer for it to disappear, and go back to the main work. – qed Nov 12 '14 at 15:38
  • Where the interest is is irrelevant. The fact on which thread the code is running doesn't make it less important or less exposed in your implementation. In fact, this part shouldn't even be aware of on which thread it's running - see my answer. – BartoszKP Nov 12 '14 at 15:49

2 Answers2

2

This is not possible. Qt documentation states:

Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread. As noted earlier, QCoreApplication::exec() must also be called from that thread.

(emphasis mine)

This answer suggests on the other hand that in reality this is not true :) However it seems that PySide sticks to the official version:

This can be verified by the following code sample:

import sys
import threading
from PySide import QtCore, QtGui

class Dialog(QtGui.QDialog):
    def __init__(self):
        QtGui.QDialog.__init__(self)
        button = QtGui.QPushButton("test")
        layout = QtGui.QVBoxLayout()
        layout.addWidget(button)
        self.setLayout(layout)

app = QtGui.QApplication(sys.argv)
toast = Dialog()
toast.show()

t = threading.Thread(target = lambda: app.exec_())
t.daemon = True
t.start()
print("App freezes the main process!")
input()

which produces the following output:

App freezes the main process!
QApplication::exec: Must be called from the main thread

(and a crash, on my machine). I have also verified the option with creating the app within the other thread - it works, but crashes on exit.


So the solution seems to let Qt have the main thread, and organize your processing in a separate thread. This shouldn't really be a problem: if you'll separate your concerns well it won't make a difference for your console-app part on which thread it's running.

Community
  • 1
  • 1
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
  • 1
    It crashed because you instantiated `QApplication` in one thread but called its method `exec()` in a different thread. You cannot do this because QApplication is not thread-safe. You must instantiate QApplication in your secondary thread, and then call `exec()` in the same thread. – JKSH Nov 15 '14 at 03:15
  • @JKSH I tried that also: "*I have also verified the option with creating the app within the other thread - it works, but crashes on exit.*" – BartoszKP Nov 15 '14 at 09:04
2

I'm not sure if the PySide imposes any restrictions, but here's how it's done in C++:

  1. Instantiate QApplication in a secondary thread.
  2. Create your dialog in that same thread.
  3. Call either QDialog::exec() OR {QApplication::exec() plus QDialog::show()} in that same thread.
  4. Make sure that your secondary thread has fully shut down before you quit your app.

Yes, the Qt documentation currently says that only the main thread is allowed. However, there is nothing in the Qt source code that forbids creating QApplication in a secondary thread and then using GUI classes in that thread (for Windows and Linux). The documentation should be changed.

Mac OS X is different though -- the Cocoa framework only allows GUI operations in the main thread.

JKSH
  • 2,658
  • 15
  • 33