2 回答
TA贡献1779条经验 获得超6个赞
除了不应在 Qt 主线程之外访问或创建任何 UI 元素这一事实之外,这对于使用在其他线程中创建的对象的 UI 元素也有效。
在您的具体情况下,这不仅意味着您无法在单独的线程中设置影片,而且也无法在那里创建 QMovie。
在下面的示例中,我打开一个本地文件,并使用信号将数据发送到主线程。从那里,我创建一个 QBuffer 将数据存储在 QMovie 可以使用的 IO 设备中。请注意,缓冲区和电影都必须有持久引用,否则函数返回后它们将被垃圾回收。
from PyQt5.QtCore import QThread, QByteArray, QBuffer
class ChangeGif(QThread):
dataLoaded = pyqtSignal(QByteArray)
def __init__(self, all_widgets):
QThread.__init__(self)
self.all = all_widgets
def run(self):
sleep(1)
f = QFile('new.gif')
f.open(f.ReadOnly)
self.dataLoaded.emit(f.readAll())
f.close()
class MainWindow(QWidget):
# ...
def change_gif(self):
self.loader_label.show()
self.worker = ChangeGif(self)
self.worker.dataLoaded.connect(self.applyNewGif)
self.worker.start()
def applyNewGif(self, data):
# a persistent reference must be kept for both the buffer and the movie,
# otherwise they will be garbage collected, causing the program to
# freeze or crash
self.buffer = QBuffer()
self.buffer.setData(data)
self.newGif = QMovie()
self.newGif.setCacheMode(self.newGif.CacheAll)
self.newGif.setDevice(self.buffer)
self.gif_label.setMovie(self.newGif)
self.newGif.start()
self.gif_label.show()
self.loader_label.hide()
请注意,上面的示例仅用于解释目的,因为下载过程可以使用 QtNetwork 模块完成,该模块异步工作并提供简单的信号和插槽来下载远程数据:
from PyQt5.QtCore import QUrl
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
class MainWindow(QWidget):
def __init__(self):
# ...
self.downloader = QNetworkAccessManager()
def change_gif(self):
self.loader_label.show()
url = QUrl('https://path.to/animation.gif')
self.device = self.downloader.get(QNetworkRequest(url))
self.device.finished.connect(self.applyNewGif)
def applyNewGif(self):
self.loader_label.hide()
self.newGif = QMovie()
self.newGif.setDevice(self.device)
self.gif_label.setMovie(self.newGif)
self.newGif.start()
self.gif_label.show()
TA贡献1816条经验 获得超4个赞
使用 Qt 的主要规则是只有一个主线程负责操作 UI 小部件。它通常被称为GUI thread
。您永远不应该尝试从其他线程访问小部件。例如,Qt 计时器不会从另一个线程开始激活,并且 Qt 会在运行时控制台中打印警告。在你的情况下 - 如果你把 QMovie 的所有操作都放在 GUI 线程中,很可能一切都会按预期工作。
该怎么办?使用信号和槽 - 它们也被设计为在线程之间工作。
你的代码应该做什么:
从主线程显示加载程序。
激活从网络下载 gif 的线程。
下载准备就绪后 -
GUI thread'. Remember to use
在连接信号和插槽时发出信号并在主 Qt::QueuedConnection` 中捕获它,尽管在某些情况下会自动使用它。在接收槽中,将主窗口中的默认 gif 替换为下载的 gif,并显示它并隐藏加载程序。
您必须使用某种同步机制来避免数据竞争。一个互斥体就足够了。或者您可以将数据作为信号槽参数传递,但如果是 gif,它可能会太大。
添加回答
举报