在调用函数时如何防止 GUI 冻结? (PyQT4, Python3)
How do I keep GUI from freezing, while calling a function? (PyQT4, Python3)
问题背景:
我是 PyQT4 的新手。我正在用它开发一个程序,我正在网络抓取以将数据导入我的程序。在下载信息时,我的 GUI 锁定了。我想在一个单独的后台线程中调用此函数,也许使用 QThread,但我很难理解 QThread、一般的 Qt 和 slot/signal 通信方式。
我已经阅读了有关制作通用工作线程的信息,该工作线程将调用传递给它的任何函数。我不知道如何在我的主文件中实现它,以便我可以 运行 我的功能作为后台进程。如果可以显示任何示例代码,请详细解释每一行,因为我不了解该过程。
问题:
- 如何防止我的 GUI 在函数 运行ning 时冻结?
- 如何使用后台线程从我的 class 中执行 运行 函数?
代码:
我的 ui 是从 Qt 4 Designer 创建的外部文件中加载的。
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_())
从示例代码中不清楚为什么 connections()
会 阻塞(鉴于它包含的代码),或者为什么 login()
shouldn't block(假设这是登录对话框通常做的)。但是无论如何,您示例中的工人 class 可以像这样转换为 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()
有许多其他方法可以实现相同的目的 - 但哪种方法最合适,以及实际如何实现,取决于您的应用程序的工作方式的详细信息。
有了这个更新后的代码,我终于可以在后台启动我的功能了。现在开始学习如何让我的后台线程与主 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()
问题背景:
我是 PyQT4 的新手。我正在用它开发一个程序,我正在网络抓取以将数据导入我的程序。在下载信息时,我的 GUI 锁定了。我想在一个单独的后台线程中调用此函数,也许使用 QThread,但我很难理解 QThread、一般的 Qt 和 slot/signal 通信方式。
我已经阅读了有关制作通用工作线程的信息,该工作线程将调用传递给它的任何函数。我不知道如何在我的主文件中实现它,以便我可以 运行 我的功能作为后台进程。如果可以显示任何示例代码,请详细解释每一行,因为我不了解该过程。
问题:
- 如何防止我的 GUI 在函数 运行ning 时冻结?
- 如何使用后台线程从我的 class 中执行 运行 函数?
代码:
我的 ui 是从 Qt 4 Designer 创建的外部文件中加载的。
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_())
从示例代码中不清楚为什么 connections()
会 阻塞(鉴于它包含的代码),或者为什么 login()
shouldn't block(假设这是登录对话框通常做的)。但是无论如何,您示例中的工人 class 可以像这样转换为 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()
有许多其他方法可以实现相同的目的 - 但哪种方法最合适,以及实际如何实现,取决于您的应用程序的工作方式的详细信息。
有了这个更新后的代码,我终于可以在后台启动我的功能了。现在开始学习如何让我的后台线程与主 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()