简体   繁体   中英

Why is my PyQt UI not frozen while I don't have a loop running?

I'm fairly new to Qt. I've built a few things in python with Gtk3 with introspection and the Glade UI designer, including building my code to be run as functions that are run when an event happens. In GTK, if the main loop is not currently looping, you UI freezes.

Now, I'm trying to learn PyQt. The thing that seems weird is that I tell the Window to show, and the UI appears, and the Python prompt returns. But the GUI is not frozen as I would expect with GTK (since I don't have the main loop running).

This is really nice as far as testing UI stuff from the python interpreter. But how is this possible? Is Qt making another thread that is looping or something?

Here's the code:

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class Ui_multippp(object):
    def setupUi(self, multippp):
        multippp.setObjectName(_fromUtf8("multippp"))
        multippp.resize(371, 43)
        self.verticalLayout = QtGui.QVBoxLayout(multippp)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.label = QtGui.QLabel(multippp)
        self.label.setObjectName(_fromUtf8("label"))
        self.verticalLayout.addWidget(self.label)
        self.verticalLayout_2 = QtGui.QVBoxLayout()
        self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
        self.verticalLayout.addLayout(self.verticalLayout_2)

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

    def retranslateUi(self, multippp):
        multippp.setWindowTitle(QtGui.QApplication.translate("multippp", "Multiple PPP Accounts", None, QtGui.QApplication.UnicodeUTF8))
        self.label.setText(QtGui.QApplication.translate("multippp", "More than one PPP account found, please select one:", None, QtGui.QApplication.UnicodeUTF8))



import sys
app = QtGui.QApplication(sys.argv)
multippp = QtGui.QDialog()
ui = Ui_multippp()
ui.setupUi(multippp)
multippp.show()
# At this point the python prompt returns, but the UI is interactive. I can add/remove buttons at the python prompt.

This might be helpful: http://qt-project.org/doc/qt-5.0/qtcore/threads-qobject.html

" Each thread can have its own event loop. The initial thread starts its event loops using QCoreApplication::exec(); other threads can start an event loop using QThread::exec(). Like QCoreApplication, QThread provides an exit(int) function and a quit() slot. "

If you think about it, the interpreter prompt is running a main loop waiting for your input.

Not only does PyQt have a main loop that is set off by running QCoreApplication._exec() , but it also hooks itself into the interpreter prompt's mainloop (and for that matter, pdb's main loop). While the interpreter prompt is spinning in circles waiting for input, the Qt Events are being handled.

For example, using the above code as a starting point, you can see how the interface will freeze if you make the interpreter main loop take a while to return:

from PyQt4 import QtCore, QtGui
import time

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class Ui_multippp(object):
    def setupUi(self, multippp):
        multippp.setObjectName(_fromUtf8("multippp"))
        multippp.resize(371, 43)
        self.verticalLayout = QtGui.QVBoxLayout(multippp)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.label = QtGui.QLabel(multippp)
        self.label.setObjectName(_fromUtf8("label"))
        self.verticalLayout.addWidget(self.label)
        self.verticalLayout_2 = QtGui.QVBoxLayout()
        self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
        self.verticalLayout.addLayout(self.verticalLayout_2)

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

    def retranslateUi(self, multippp):
        multippp.setWindowTitle(QtGui.QApplication.translate("multippp", "Multiple PPP Accounts", None, QtGui.QApplication.UnicodeUTF8))
        self.label.setText(QtGui.QApplication.translate("multippp", "More than one PPP account found, please select one:", None, QtGui.QApplication.UnicodeUTF8))



import sys
app = QtGui.QApplication(sys.argv)
multippp = QtGui.QDialog()
ui = Ui_multippp()
ui.setupUi(multippp)
multippp.show()
# UI is visible and interactive at this point
# But after the next command, the UI will freeze for 5 seconds while the interpreter loop is 'busy' waiting for the command to return
time.sleep(5)

A practical side problem comes up when you are trying to debug using pdb: the Qt main loop is hooked into the pdb prompt, which makes it so that you are trying to re-enter the main loop when you do something like pdb.set_trace() . You will get an error like "QCoreApplication::exec: The event loop is already running". To be able to use pdb, you can wrap it in QtCore.pyqtRemoveInputHook() and QtCore.pyqtRestoreInputHook() , which will let you disconnect Qt from the input loop, freezing it and letting you debug.

See also http://www.riverbankcomputing.com/pipermail/pyqt/2007-September/017134.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM