0

I created an interface in pyqt4 designer, there is only a button and a status bar. I want to show a message in the status bar when I click the button. For some reason I have to put this action in a new thread.

But when I click the button, nothing happened. The message didn't appear until I drag the edge of the window. Sometimes the click action even cause the program to crash:

pythonw.exe has stopped working.

This happens frequently, especially when I click too fast.

Here is my code:

from PyQt4 import QtCore, QtGui
import threading

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_MainWindow(object):

    def cal(self):
        self.statusbar.showMessage('something')   

    def onbutton(self):

        thread = threading.Thread(target=self.cal)
        thread.start()



    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(510, 409)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.pushButton = QtGui.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(170, 100, 171, 91))
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        self.pushButton.clicked.connect(self.onbutton)

        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
        self.pushButton.setText(_translate("MainWindow", "PushButton", None))


if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
shyuu
  • 123
  • 1
  • 1
  • 6
  • Most Qt objects cannot be updated from a worker thread, only the main thread. Based on your description I think QStatusBar is such an object. Therefore you must arrange for the main thread to be notified when the worker thread wants to display a message. How to do this depends on your application, but you should look at QCoreApplication.postEvent and QThread. – Paul Cornelius May 12 '15 at 01:52

1 Answers1

4

The crash happens because you are interacting with the Qt GUI from a secondary thread. You are only allowed to interact with the GUI from the main thread (see the Qt documentation).

You have two options:

  1. Switch to a PyQt QThread, and emit a signal from the thread. If the signal is connected to a slot in the main thread, then you can do the GUI interaction there. This question/answer demonstrates approximately how to do this.

  2. Use QApplication.postEvent() to send an event from the Python thread to the main thread, which then causes a method to be run which interacts with the GUI. Note that some people have concerns that this is not OK (see this SO Answer), however I've had no issue with it. I've wrapped this up in an freely available library called qtutils (documentation here).

Community
  • 1
  • 1
three_pineapples
  • 11,579
  • 5
  • 38
  • 75