在调用函数时如何防止GUI冻结? (PyQT4,Python3)

时间:2015-11-01 14:13:03

标签: python multithreading user-interface pyqt4 qthread

问题背景:

我是PyQT4的新手。我正在开发一个程序,我正在网上抓取数据到我的程序。在信息下载时,我的GUI锁定。我想在一个单独的后台线程中调用这个函数,也许使用QThread,但是我很难绕过QThread,Qt,以及插槽/信号通信方式。

我已经读过关于创建一个泛型工作线程,它将调用传递给它的任何函数。我不知道如何在我的主文件中实现它,以便我可以将我的功能作为后台进程运行。如果可以显示任何示例代码,请详细解释每一行,因为我不了解该过程。

问题:

  1. 如何在函数运行时阻止GUI冻结?
  2. 我如何使用后台线程来运行我的类中的函数?
  3. 代码:

    我的ui是从Qt 4 Designer创建的外部文件中加载的。

    Full files on Github

    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName(_fromUtf8("MainWindow"))
    

    main.py(主文件)

    def connections():
        # If button is clicked, call summary(), which web scrapes 
        # for data. This could take 5-30 seconds, this freezes UI.
        ui.btnRefreshSummary.clicked.connect(lambda: summary())
    
    # Refresh items in gui
    def refresh_ui():
        if summary_data != []:
            ui.valWatching.setText(summary_data[0])
            ui.valBidding.setText(summary_data[1])
            ui.valWon.setText(summary_data[2])
            ui.valNotWon.setText(summary_data[3])
            ui.valPurchases.setText(summary_data[4])
            ui.valInvoices.setText(summary_data[5])
    
    def login():
        # Scrape website and login while in background; 
        # This locks up GUI until it completes.
        # Pretend this sleep command is the time it takes to login
        time.sleep(5)  # <-This would lock it up for 5 seconds
    
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        MainWindow = QtGui.QMainWindow()
        ui = Ui_MainWindow()
        ui.setupUi(MainWindow)
        MainWindow.show()
        connections()
        # Load credentials from file.
        with open('login.txt') as f:
            credentials = f.readline().strip().split(':')
        f.closed
    
        # Login, download summary, then refresh the UI.
        b = Biddergy()
        b.login(credentials[0],credentials[1])
        summary_data = b.summary()
        b.logout()
    
        refresh_ui()
        sys.exit(app.exec_())
    

2 个答案:

答案 0 :(得分:5)

从示例代码中不清楚为什么connections() 阻止(给定它包含的代码),或者为什么login() 不应该块(假设这是登录对话框通常所做的)。但无论如何,您示例中的worker类可以转换为QThread,如下所示:

class Worker(QThread):
    intReady = pyqtSignal(int)

    def run(self):
        for i in range(1, 10):
            time.sleep(1)
            self.intReady.emit(i)

然后可以像这样使用:

    # connections()
    # login()
    def slot(arg='finished'): print(arg)
    thread = Worker()
    thread.intReady.connect(slot)
    thread.finished.connect(slot)
    thread.start()

还有许多其他方法可以实现相同的目标 - 但哪一种最合适,以及实际如何实施,取决于应用程序的工作方式。

答案 1 :(得分:2)

通过这个更新的代码,我终于可以在后台启动我的功能了。现在开始学习如何使我的后台线程与主UI线程进行通信。非常感谢@ekhumoro对我非常耐心。

#!/usr/bin/env python3
from PySide import QtGui, QtCore
from PySide.QtCore import QThread, QObject, Signal, Slot
from main_gui import Ui_MainWindow  # my UI from Qt4 Designer(pyside-uic)
from Scrapers import Biddergy       # My own class
import sys
import queue

class BiddergyWrapper(QThread):
    def __init__(self, q, loop_time=1.0/60):
        self.q = q
        self.timeout = loop_time
        super(BiddergyWrapper, self).__init__()

    def onThread(self, function, *args, **kwargs):
        self.q.put((function, args, kwargs))

    def run(self):
        while True:
            try:
                function, args, kwargs = self.q.get(timeout=self.timeout)
                function(*args, **kwargs)
            except queue.Empty:
                self.idle()

    def idle(self):
        pass

    def _summary(self):
        b.summary()

    def summary(self):
        self.onThread(self._summary)

    def _login(self):
        b.login()

    def login(self):
        self.onThread(self._login())

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    ui.btnRefreshSummary.clicked.connect(lambda: bw.summary())

    # Load credentials from file.
    with open('login.txt') as f:
        credentials = f.readline().strip().split(':')

    # Login, download summary, then refresh the UI.
    b = Biddergy(credentials[0], credentials[1])
    request_queue = queue.Queue()
    bw = BiddergyWrapper(request_queue)
    bw.start()

    # Run QApplication
    app.exec_()
    # Begin "Graceful stop?"
    bw.quit()
    b.logout()
    sys.exit()