PyQt crashes and thread safety

Hello StackExchange Community,

At first you all helped me a lot, thanks. First question:

I am currently writing a PyQt GUI application and I see that it crashes on Windows systems, it also gives me segfault on my machine at home when it works on the one that works (both linux mint 17). After some research, I realize that I probably created an insecure GUI because I have several objects that call each other's methods.

https://stackoverflow.com/questions/1614060/ ... : GUI widgets can only be accessed from the main thread, which means the thread that calls QApplication.exec (). Accessing GUI widgets from any other thread is what you do with your calls to self.parent () - this is undefined behavior, in your case it means a crash.

From Qt docs : Although a QObject is reentrant, GUI classes, especially 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 this thread.

So, in the end, I believe that for this I should use a system of signal slots.

  • Is it correct?
  • , - ? , , , . ?

. Qt , QObjects . , Qt ( ).

from PyQt4 import QtGui
import sys

class TestWidget(QtGui.QWidget):
    def __init__(self,string):
        super(TestWidget,self).__init__()
        self.button = QtGui.QPushButton(string,parent=self)
        self.button.clicked.connect(self.buttonClicked)

        # just to check, and yes, lives in it own thread
        print self.thread()

    def buttonClicked(self):
        # the seemingly problematic line
        self.parent().parent().statusBar().showMessage(self.button.text())
        pass
    pass

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(MainWindow,self).__init__()

        Layout = QtGui.QHBoxLayout()
        for string in ['foo','bar']:
            Layout.addWidget(TestWidget(string))

        CentralWidget = QtGui.QWidget(self)
        CentralWidget.setLayout(Layout)
        self.setCentralWidget(CentralWidget)
        self.statusBar()
        self.show()
        pass
    pass

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    M = MainWindow()
    sys.exit(app.exec_())

, Windows.

  1. ? , ?

, ...

+3
3

?

, q-. .

?

options, ...

Q:

options , options QObject.

class Options(QtCore.QObject):
    optionUpdated = QtCore.pyqtSignal(object)

    def __init__(self):

        self.__options = {
            'option_1': None
        }

    def get_option(self, option):
        return self.__options.get(option)

    def set_option(self, option, value):
        self.__options[option] = value
        self.optionUpdated.emit(self)

/, , , .

:

    options = Options()
    some_widget = SomeWidget()
    options.optionUpdated.connect(some_widget.options_updated)    // Is like you implement the observer pattern, right?

? , ?

thread-unsafe , " ", " " " , ".

pyqt API doc QObject.thread:

, .

ekumoro, , , ... !

QObject.thread QThread , QThread , , .

, , .

, , :

from PyQt4 import QtGui
import sys

class TestWidget(QtGui.QWidget):
    def __init__(self,string):
        super(TestWidget,self).__init__()
        # just to check, and yes, lives in it own thread
        print("TestWidget thread: {}".format(self.thread()))

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(MainWindow,self).__init__()
        print("Window thread: {}".format(self.thread()))
        Layout = QtGui.QHBoxLayout()
        for string in ['foo','bar']:
            Layout.addWidget(TestWidget(string))
        self.show()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    M = MainWindow()
    sys.exit(app.exec_())

, :

Window thread: <PyQt4.QtCore.QThread object at 0x00000000025C1048>
TestWidget thread: <PyQt4.QtCore.QThread object at 0x00000000025C4168>
TestWidget thread: <PyQt4.QtCore.QThread object at 0x00000000025C41F8>

.

"-", .

+2

:

  • GUI (, QApplication.exec_()). , Qt 4

  • , direct Qt , , thread-safe = >

  • ( ???), , QObject . , ?

+2

As a continuation of some comments, below is a test script that shows how to check which thread the code is running in:

from PyQt4 import QtCore, QtGui

class Worker(QtCore.QObject):
    threadInfo = QtCore.pyqtSignal(object, object)

    @QtCore.pyqtSlot()
    def emitInfo(self):
        self.threadInfo.emit(self.objectName(), QtCore.QThread.currentThreadId())

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.button = QtGui.QPushButton('Test', self)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.button)
        self.thread = QtCore.QThread(self)
        self.worker1 = Worker()
        self.worker1.setObjectName('Worker1')
        self.worker1.moveToThread(self.thread)
        self.worker1.threadInfo.connect(self.handleShowThreads)
        self.button.clicked.connect(self.worker1.emitInfo)
        self.worker2 = Worker()
        self.worker2.setObjectName('Worker2')
        self.worker2.threadInfo.connect(self.handleShowThreads)
        self.button.clicked.connect(self.worker2.emitInfo)
        self.thread.start()

    def handleShowThreads(self, name, identifier):
        print('Main: %s' % QtCore.QThread.currentThreadId())
        print('%s: %s\n' % (name, identifier))

    def closeEvent(self, event):
        self.thread.quit()
        self.thread.wait()

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
+1
source

Source: https://habr.com/ru/post/1614043/


All Articles