亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如果從線程啟動,QMovie 不會啟動

如果從線程啟動,QMovie 不會啟動

藍山帝景 2023-08-22 15:51:42
各位開發者!我有一個關于 Qt 和多線程的問題。=== 簡短版本 =============================================Qt 可以做我想做的事嗎?即(1)顯示一個加載器;(2)在后臺下載gif;(3) 下載后在主窗口中顯示下載的gif?===長版==============================================我有一個想法,當我按下按鈕時,它:顯示裝載機;激活一個從網絡下載 gif 的線程;用下載的 gif 替換主窗口中隱藏的默認 gif 并顯示它隱藏裝載機;我遇到的問題是,當顯示下載的 gif 時,它被“凍結”或僅顯示第一幀。除此之外一切都很好。然而,它需要在隱藏加載器后將 gif 動畫化。這里提到的是所以Qt事件循環負責執行你的代碼以響應程序中發生的各種事情,但是當它執行你的代碼時,它不能做任何其他事情。我相信這是問題的核心。還建議建議在 Qt 中使用 QThread 而不是 Python 線程,但如果您不需要從函數與主線程通信,則 Python 線程可以正常工作。由于我的線程替換了默認 gif 的內容,我相信它確實進行了通信:(
查看完整描述

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()


查看完整回答
反對 回復 2023-08-22
?
繁華開滿天機

TA貢獻1816條經驗 獲得超4個贊

使用 Qt 的主要規則是只有一個主線程負責操作 UI 小部件。它通常被稱為GUI thread。您永遠不應該嘗試從其他線程訪問小部件。例如,Qt 計時器不會從另一個線程開始激活,并且 Qt 會在運行時控制臺中打印警告。在你的情況下 - 如果你把 QMovie 的所有操作都放在 GUI 線程中,很可能一切都會按預期工作。

該怎么辦?使用信號和槽 - 它們也被設計為在線程之間工作。

你的代碼應該做什么:

  1. 從主線程顯示加載程序。

  2. 激活從網絡下載 gif 的線程。

  3. 下載準備就緒后 -GUI thread'. Remember to use 在連接信號和插槽時發出信號并在主 Qt::QueuedConnection` 中捕獲它,盡管在某些情況下會自動使用它。

  4. 在接收槽中,將主窗口中的默認 gif 替換為下載的 gif,并顯示它并隱藏加載程序。

您必須使用某種同步機制來避免數據競爭。一個互斥體就足夠了?;蛘吣梢詫祿鳛樾盘柌蹍祩鬟f,但如果是 gif,它可能會太大。


查看完整回答
反對 回復 2023-08-22
  • 2 回答
  • 0 關注
  • 1718 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號