vadim vaduxa vadim vaduxa - 7 months ago 27
Python Question

QMessageBox in other thread

import time
import sys, threading
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import QApplication


class Global:
def __init__(self):
for c in MyClass, MainWindow:
cl = c()
setattr(self, c.__name__, cl)
setattr(cl, 'global_', self)


class MyClass:
def work(self):
for r in range(100):
if r == 2:
self.global_.MainWindow.SignalBox.emit('MyClass NO PAUSE') # need pause !!!
else:
print(r)
time.sleep(1)


class MainWindow(QtGui.QWidget):
Signal = QtCore.pyqtSignal(str)
SignalBox = QtCore.pyqtSignal(str)

def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(300, 300)

self.lab = QtGui.QLabel()
lay = QtGui.QGridLayout()
lay.addWidget(self.lab)
self.setLayout(lay)

self.msgBox = lambda txt: getattr(QtGui.QMessageBox(), 'information')(self, txt, txt)
self.Signal.connect(self.lab.setText)
self.SignalBox.connect(self.msgBox)

def thread_no_wait(self):
self.global_.MainWindow.SignalBox.emit('MyClass PAUSE OK')
threading.Thread(target=self.global_.MyClass.work).start()

def thread_main(self):
def my_work():
for r in range(100):
self.Signal.emit(str(r))
time.sleep(1)
threading.Thread(target=my_work).start()


if __name__ == '__main__':
app = QApplication(sys.argv)
g = Global()
g.MainWindow.show()
g.MainWindow.thread_main()
g.MainWindow.thread_no_wait()
app.exec_()


how to run a QMessageBox from another process?

MyClass.work performed in a separate thread without join is necessary to MyClass suspended if it causes QMessageBox If we define QMessageBox is the MainWindow , the error will be called

QObject :: startTimer: timers can not be started from another thread
QApplication: Object event filter can not be in a different thread.
And in this call QMessageBox invoked in a separate thread , and the work is not suspended

Answer

It sounds like you want to pause execution of a thread until certain operations in the main thread complete. There are a few ways you can do this. A simple way is to use a shared global variable.

PAUSE = False

class MyClass:
    def work(self):
        global PAUSE
        for r in range(100):
            while PAUSE:
                time.sleep(1)
            if r == 2:
                self.global_.MainWindow.SignalBox.emit('MyClass NO PAUSE')
                PAUSE = True
            ...

class MainWindow(...)

    def msgBox(self, txt):
        QtGui.QMessageBox.information(self, txt, txt)
        global PAUSE
        PAUSE = False

If you use QThreads instead of python threads, you can actually call functions in another thread and block until that function completes using QMetaObject.invokeMethod

@QtCore.pyqtSlot(str)
def msgBox(self, txt):
    ...

...

if r == 2:
    # This call will block until msgBox() returns
    QMetaObject.invokeMethod(
        self.global_.MainWindow, 
        'msgBox', 
        QtCore.Qt.BlockingQueuedConnection, 
        QtCore.Q_ARG(str, 'Text to msgBox')
    )
Comments