61602


PyQt5で計算を行っている最中、別ウィンドウを開きgif画像を表示したいです。

Calculatingボタンを押すと、関数calcを実行している間、別ウィンドウが開きgif画像が表示されるように書いたつもりです。しかし、実際には表示された別ウィンドウではgifが全く動きません。ちなみに、Openボタンを押すと同じ別ウィンドウが開きますが、此方ではgifはしっかりと動きます。Closeボタンでは別ウィンドウが閉じます。

どうすればCalcuatingボタンを押したときでもgif画像が動くようになるのでしょうか。

* * *

**表示されるGUI**

[![画像](https://i.stack.imgur.com/tkJKJ.png)](https://i.stack.imgur.com/tkJKJ.png)

**ソースコード**

[code]

    import sys
    from PyQt5 import*
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    from PyQt5 import QtGui,QtCore, QtWidgets
    import time
    from time import sleep
    
    class Second(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(Second, self).__init__(parent)
            #Setting a title, locating and sizing the window
            self.title = 'My Second Window'
            self.left = 200
            self.top = 200
            self.width = 500
            self.height = 500
            self.setWindowTitle(self.title)
            self.setGeometry(self.left, self.top, self.width, self.height)
            lbl = QtWidgets.QLabel("計算中")
    
            self._gif = QtWidgets.QLabel()
            movie = QtGui.QMovie("calculating.gif")
            self._gif.setMovie(movie)
            movie.start()
    
            layout1=QVBoxLayout()
            layout1.addWidget(lbl)
            layout1.addWidget(self._gif)
            self.setLayout(layout1)
            #print("a")
    
    class First(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(First, self).__init__(parent)
            self.title = 'My First Window'
            self.left = 100
            self.top = 100
            self.width = 500
            self.height = 500
            self.setWindowTitle(self.title)
            self.setGeometry(self.left, self.top, self.width, self.height)
            self.pushButton = QtWidgets.QPushButton("Calculation", self)
            self.pushButton.clicked.connect(self.on_pushButton_clicked)
            self.openButton = QtWidgets.QPushButton("Open", self)
            self.openButton.clicked.connect(self.open_newWindow)
            self.closeButton = QtWidgets.QPushButton("Close", self)
            self.closeButton.clicked.connect(self.close_newWindow)        
    
            self.newWindow = Second()
    
            layout=QVBoxLayout()
            layout.addWidget(self.pushButton)
            layout.addWidget(self.openButton)
            layout.addWidget(self.closeButton)
    
            self.setLayout(layout)
    
        def on_pushButton_clicked(self):
            self.newWindow.show()
            QtWidgets.QApplication.processEvents()
            j = self.calc()
            QtWidgets.QApplication.processEvents()
            self.newWindow.close()
            print("the result of calculation is",j)
    
        def calc(self):
            j = 0        
            for i in range(100000000):                
                j += i
            return j   
    
        def open_newWindow(self):
            self.newWindow.show()
    
        def close_newWindow(self):
            self.newWindow.close()
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        main = First()
        main.show()
        sys.exit(app.exec_())
    
[/code]


CC BY-SA 4.0
2019-12-19T22:50:25.983
61599
2019-12-20T06:30:55.527
2019-12-20T01:40:37.743
3060
26529
question
python pyqt5
PyQt5で他のプロセス中に別ウィンドウでgif画像を表示したい


こんな感じで計算を別スレッドにすれば動きます。

  * `import threading`して`calc()`メソッドを別スレッドで実行する
  * 単純な戻り値は取得出来ないので、戻り値を受け取る変数をパラメータで渡し、値を入れる  
関連記事[Return value from thread](https://stackoverflow.com/q/1886090/9014308)
の承認付きの[Queue.Queue](https://docs.python.org/3/library/queue.html#Queue.Queue)
経由でもよいが、こちらの[回答](https://stackoverflow.com/a/34132917/9014308) が簡潔で良さそう

  * スレッド実行中かどうかチェックし、実行中は`QtWidgets.QApplication.processEvents()`を呼ぶ  
完了待ちは`timeout`を指定してチェックループ内で行う(ループ後の無限待ちでも良いが)

import部分

[code]

    import threading  # 追加
    
[/code]

処理の変更部分を抽出してコメント

[code]

    def on_pushButton_clicked(self):
        self.newWindow.show()
        QtWidgets.QApplication.processEvents()
        result = {'retVal': -1}    # ここから計算処理関連の変更
        tcal = threading.Thread(target=self.calc, args=[result])
        tcal.start()
        while tcal.is_alive():
            QtWidgets.QApplication.processEvents()
            tcal.join(timeout=0.001) # ここまで変更箇所
    
        self.newWindow.close()
        print("the result of calculation is", result['retVal']) # 戻り値の表示
    
    def calc(self, result): # 戻り値パラメータ追加
        j = 0
        for i in range(100000000):
            j += i
    
        result['retVal'] = j # 戻り値の設定
        return               # return では値を返さない
    
[/code]


CC BY-SA 4.0
2019-12-20T01:12:25.950
61602
2019-12-20T01:22:55.013
2019-12-20T01:22:55.013
26370
26370
61599
answer


`PyQt5.QtCore.QThread` を使う方法もあります。以下に改変したコードを記載しますが、`First` クラス内に `QThread`
クラスを継承する `CalcThread` クラスを追加しています。計算終了後、`My Second Window`
を閉じます(実際には非表示(`hide()`)にしています)。

[code]

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtCore import QThread
    from PyQt5.QtGui import QMovie
    from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout 
    
    class Second(QWidget):
        def __init__(self, parent=None):
            super(Second, self).__init__(parent)
            #Setting a title, locating and sizing the window
            self.title = 'My Second Window'
            self.left = 200
            self.top = 200
            self.width = 500
            self.height = 500
            self.setWindowTitle(self.title)
            self.setGeometry(self.left, self.top, self.width, self.height)
            lbl = QtWidgets.QLabel("計算中")
    
            self._gif = QtWidgets.QLabel()
            movie = QMovie("calculating.gif")
            self._gif.setMovie(movie)
            movie.start()
    
            layout1=QVBoxLayout()
            layout1.addWidget(lbl)
            layout1.addWidget(self._gif)
            self.setLayout(layout1)
    
    class First(QWidget):
        def __init__(self, parent=None):
            super(First, self).__init__(parent)
            self.title = 'My First Window'
            self.left = 100
            self.top = 100
            self.width = 500
            self.height = 500
            self.setWindowTitle(self.title)
            self.setGeometry(self.left, self.top, self.width, self.height)
            self.pushButton = QPushButton("Calculation", self)
            self.pushButton.clicked.connect(self.on_pushButton_clicked)
            self.openButton = QPushButton("Open", self)
            self.openButton.clicked.connect(self.open_newWindow)
            self.closeButton = QPushButton("Close", self)
            self.closeButton.clicked.connect(self.close_newWindow)        
    
            self.newWindow = Second()
    
            layout=QVBoxLayout()
            layout.addWidget(self.pushButton)
            layout.addWidget(self.openButton)
            layout.addWidget(self.closeButton)
    
            self.setLayout(layout)
    
            ## Calculation thread
            self.calc_th = self.CalcThread(self.calc, self.calc_done)
    
        def on_pushButton_clicked(self):
            self.newWindow.show()
            self.calc_th.start()
    
        def calc(self):
            j = 0        
            for i in range(100000000):                
                j += i
            self.calc_result = j   
    
        def calc_done(self):
            self.newWindow.hide()
            print('The result of calculation is ', self.calc_result)
    
        def open_newWindow(self):
            self.newWindow.show()
    
        def close_newWindow(self):
            self.newWindow.close()
    
        class CalcThread(QThread):
            def __init__(self, task, task_done):
                QThread.__init__(self)
                self.task = task
                self.task_done = task_done
    
            def run(self):
                self.task()
                self.task_done()
                self.quit()
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        main = First()
        main.show()
        sys.exit(app.exec_())
    
[/code]


CC BY-SA 4.0
2019-12-20T04:43:23.847
61609
2019-12-20T06:30:55.527
2019-12-20T06:30:55.527
61599
answer