【问题标题】:Updating GUI elements in MultiThreaded PyQT更新多线程 PyQT 中的 GUI 元素
【发布时间】:2021-05-29 04:19:05
【问题描述】:

我研究了一段时间以查找信息如何使用 PyQT 进行多线程程序,更新 GUI 以显示结果

我习惯于通过示例学习,但我找不到(是的,我一直在寻找几周)任何使用多线程执行简单任务的程序示例,例如连接到 www 站点列表(5 个线程)和只是打印带有响应代码的处理过的网址。

任何人都可以分享代码或将我发送到解释此类程序的好教程吗?

【问题讨论】:

标签: python multithreading pyqt


【解决方案1】:

这里有一些非常基本的例子。

您可以将 GUI 元素的引用传递给线程,并在线程中更新它们。

import sys
import urllib2

from PyQt4 import QtCore, QtGui


class DownloadThread(QtCore.QThread):
    def __init__(self, url, list_widget):
        QtCore.QThread.__init__(self)
        self.url = url
        self.list_widget = list_widget

    def run(self):
        info = urllib2.urlopen(self.url).info()
        self.list_widget.addItem('%s\n%s' % (self.url, info))


class MainWindow(QtGui.QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.list_widget = QtGui.QListWidget()
        self.button = QtGui.QPushButton("Start")
        self.button.clicked.connect(self.start_download)
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.button)
        layout.addWidget(self.list_widget)
        self.setLayout(layout)

    def start_download(self):
        urls = ['http://google.com', 'http://twitter.com', 'http://yandex.ru',
                'http://stackoverflow.com/', 'http://www.youtube.com/']
        self.threads = []
        for url in urls:
            downloader = DownloadThread(url, self.list_widget)
            self.threads.append(downloader)
            downloader.start()

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MainWindow()
    window.resize(640, 480)
    window.show()
    sys.exit(app.exec_())

编者注: Qt 小部件不是线程安全的,不应从除主线程之外的任何线程访问(有关详细信息,请参阅Qt documentation)。如本答案的第二部分所示,使用线程的正确方法是通过信号/插槽。


此外,您可以使用信号和插槽来分离 gui 和网络逻辑。

import sys
import urllib2

from PyQt4 import QtCore, QtGui


class DownloadThread(QtCore.QThread):

    data_downloaded = QtCore.pyqtSignal(object)

    def __init__(self, url):
        QtCore.QThread.__init__(self)
        self.url = url

    def run(self):
        info = urllib2.urlopen(self.url).info()
        self.data_downloaded.emit('%s\n%s' % (self.url, info))


class MainWindow(QtGui.QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.list_widget = QtGui.QListWidget()
        self.button = QtGui.QPushButton("Start")
        self.button.clicked.connect(self.start_download)
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.button)
        layout.addWidget(self.list_widget)
        self.setLayout(layout)

    def start_download(self):
        urls = ['http://google.com', 'http://twitter.com', 'http://yandex.ru',
                'http://stackoverflow.com/', 'http://www.youtube.com/']
        self.threads = []
        for url in urls:
            downloader = DownloadThread(url)
            downloader.data_downloaded.connect(self.on_data_ready)
            self.threads.append(downloader)
            downloader.start()

    def on_data_ready(self, data):
        print data
        self.list_widget.addItem(unicode(data))


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MainWindow()
    window.resize(640, 480)
    window.show()
    sys.exit(app.exec_())

【讨论】:

  • +1 建议使用信号将线程与显示逻辑分开。
  • 但是禁止直接从非gui(主)线程与gui交互,不是吗?
  • 注意:第一个例子是错误的。你不能从一个线程调用QListWidget.addItem()。你必须使用信号。
  • 是的,这是一个糟糕的答案(抱歉)。如果你这样做,你的程序会随机崩溃。 Qt 小部件不是线程安全的...
  • @three_pineapples,感谢编辑!我同意小部件和线程安全。我猜该代码之所以有效,是因为特定版本的运气。
【解决方案2】:

虽然 reclosedev 的回答对我来说非常有效,但我收到了 QThread: Destroyed while thread is still running 错误。为了纠正这个问题,我将父类引用传递给 QThread 构造函数,如this question 所示。

import sys
import urllib2

from PyQt4 import QtCore, QtGui


class DownloadThread(QtCore.QThread):

    data_downloaded = QtCore.pyqtSignal(object)

    def __init__(self, parent, url):
        QtCore.QThread.__init__(self, parent)
        self.url = url

    def run(self):
        info = urllib2.urlopen(self.url).info()
        self.data_downloaded.emit('%s\n%s' % (self.url, info))


class MainWindow(QtGui.QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.list_widget = QtGui.QListWidget()
        self.button = QtGui.QPushButton("Start")
        self.button.clicked.connect(self.start_download)
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.button)
        layout.addWidget(self.list_widget)
        self.setLayout(layout)

    def start_download(self):
        urls = ['http://google.com', 'http://twitter.com', 'http://yandex.ru',
                'http://stackoverflow.com/', 'http://www.youtube.com/']
        self.threads = []
        for url in urls:
            downloader = DownloadThread(parent=self, url)
            downloader.data_downloaded.connect(self.on_data_ready)
            self.threads.append(downloader)
            downloader.start()

    def on_data_ready(self, data):
        print data
        self.list_widget.addItem(unicode(data))


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MainWindow()
    window.resize(640, 480)
    window.show()
    sys.exit(app.exec_())

【讨论】:

    猜你喜欢
    • 2018-10-10
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多