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,并顯示它并隱藏加載程序。
您必須使用某種同步機制來避免數據競爭。一個互斥體就足夠了?;蛘吣梢詫祿鳛樾盘柌蹍祩鬟f,但如果是 gif,它可能會太大。
添加回答
舉報