2 回答

TA貢獻(xiàn)1779條經(jīng)驗(yàn) 獲得超6個(gè)贊
除了不應(yīng)在 Qt 主線程之外訪問(wèn)或創(chuàng)建任何 UI 元素這一事實(shí)之外,這對(duì)于使用在其他線程中創(chuàng)建的對(duì)象的 UI 元素也有效。
在您的具體情況下,這不僅意味著您無(wú)法在單獨(dú)的線程中設(shè)置影片,而且也無(wú)法在那里創(chuàng)建 QMovie。
在下面的示例中,我打開(kāi)一個(gè)本地文件,并使用信號(hào)將數(shù)據(jù)發(fā)送到主線程。從那里,我創(chuàng)建一個(gè) QBuffer 將數(shù)據(jù)存儲(chǔ)在 QMovie 可以使用的 IO 設(shè)備中。請(qǐng)注意,緩沖區(qū)和電影都必須有持久引用,否則函數(shù)返回后它們將被垃圾回收。
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()
請(qǐng)注意,上面的示例僅用于解釋目的,因?yàn)橄螺d過(guò)程可以使用 QtNetwork 模塊完成,該模塊異步工作并提供簡(jiǎn)單的信號(hào)和插槽來(lái)下載遠(yuǎn)程數(shù)據(jù):
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貢獻(xiàn)1816條經(jīng)驗(yàn) 獲得超4個(gè)贊
使用 Qt 的主要規(guī)則是只有一個(gè)主線程負(fù)責(zé)操作 UI 小部件。它通常被稱為GUI thread
。您永遠(yuǎn)不應(yīng)該嘗試從其他線程訪問(wèn)小部件。例如,Qt 計(jì)時(shí)器不會(huì)從另一個(gè)線程開(kāi)始激活,并且 Qt 會(huì)在運(yùn)行時(shí)控制臺(tái)中打印警告。在你的情況下 - 如果你把 QMovie 的所有操作都放在 GUI 線程中,很可能一切都會(huì)按預(yù)期工作。
該怎么辦?使用信號(hào)和槽 - 它們也被設(shè)計(jì)為在線程之間工作。
你的代碼應(yīng)該做什么:
從主線程顯示加載程序。
激活從網(wǎng)絡(luò)下載 gif 的線程。
下載準(zhǔn)備就緒后 -
GUI thread'. Remember to use
在連接信號(hào)和插槽時(shí)發(fā)出信號(hào)并在主 Qt::QueuedConnection` 中捕獲它,盡管在某些情況下會(huì)自動(dòng)使用它。在接收槽中,將主窗口中的默認(rèn) gif 替換為下載的 gif,并顯示它并隱藏加載程序。
您必須使用某種同步機(jī)制來(lái)避免數(shù)據(jù)競(jìng)爭(zhēng)。一個(gè)互斥體就足夠了?;蛘吣梢詫?shù)據(jù)作為信號(hào)槽參數(shù)傳遞,但如果是 gif,它可能會(huì)太大。
添加回答
舉報(bào)