

表題の通り、QProgressDialogを別スレッドで実行したいのですが、安定せずに困っています。  
サードパーティ製の重い処理を実行させつつ、ダイアログが固まらないようにしたいです。  
やりたいことは、基本は次のようなスクリプトです。  
(サードパーティ製モジュールの入る部分はコメントアウトしてあります)

[code]

    # -*- coding: utf-8 -*-
    import sys
    from PySide import QtCore, QtGui
    import time
    # import third_party_module
    
    
    def start_third_party_module():
        item_num = 3
    
        d=QtGui.QProgressDialog()
        d.setRange(0, item_num)
        d.show()
    
        for i in range(item_num):
            # ここに、重い処理が入る
            # third_party_module.do(i)
            d.setValue(i)
            print i
            time.sleep(2)
    
    class MainWindow(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__()
            button = QtGui.QPushButton(self)
            button.setText("Click me")
            button.clicked.connect(self.pushBtn)
    
        def pushBtn(self):
            start_third_party_module()
    
    def main():
        qApp = QtGui.QApplication(sys.argv)
        win = MainWindow(qApp)
        win.show()
        sys.exit(qApp.exec_())
    
    main()
    
[/code]

上記スクリプトを実行させると、一応ダイアログは表示されますがcancelボタンが押せないなど都合が悪いです。  
サードパーティ製のモジュールをQThreadで実行することも試してみたのですが安定せず(頻繁に落ちる)、メインの中でサードパーティ製のモジュールを実行させつつ、プログレスダイアログにも触れるようにしたいと思っております。

どうかお力添えをお願いいたします。

追記：上記スクリプトが、前回は端折りすぎていたので、情報を追記しました。  
よろしくお願いいたします。


CC BY-SA 3.0
2017-09-15T05:22:17.227
37960
2018-03-22T04:46:04.110
2017-09-15T06:55:29.230
24908
24908
question
python
PysideのQProgressDialogを、別スレッドで実行させたいです。


重い処理のせいではなく、`QProgressDialog`の表示方法の問題と思います。  
具体的には、`MainWindow.pushBtn`以降で一連の処理をさせているのが原因と思います。

2か所変更すると、`QProgressDialog`の表示更新と操作が可能になると思います。(重い処理をさせつつ)

  1. `button`のconnect方法。  
以下のように変更する。

[code]         # 変更前

        button.clicked.connect(self.pushBtn)
        # 変更後
        button.clicked.connect(self.pushBtn, QtCore.Qt.QueuedConnection)
    
[/code]

  2. `dialog`の表示方法。  
`pushBtn`以降の処理で表示させるのではなく、その外で表示させるようにする。  
具体的には、`QProgressDialog`オブジェクトを`MainWindow`のメンバとし、`button.clicked`にconnectして表示させるようにする。

[code]         # 変更後(MainWindow.__init__内)

        self.dialog = QProgressDialog()
        button.clicked.connect(dialog.show)
    
[/code]

原因は、`button.clicked`のイベントで一連の処理(`MainWindow.pushBtn`以降の処理)を行っているため、イベントディスパッチャに処理が戻らないためです。(戻らないと他のイベントが処理されない)  
ですので、`button.clicked`のイベントをイベントキューに入れるようの動作させるために前者の変更を行います。  
同様に`QtGui.QProgressDialog`でイベントディスパッチできるよう、後者の変更を行います。

＃connectの第2引数については、[`Qt.ConnectionType`のドキュメント](https://srinikom.github.io/pyside-
docs/PySide/QtCore/Qt.html#PySide.QtCore.PySide.QtCore.Qt.ConnectionType)を参照ください。


CC BY-SA 3.0
2017-09-15T13:12:21.170
37973
2017-09-15T13:12:21.170
20098
37960
answer


スクリプトをできるだけシンプルにし、@user20098さんの処理も加えてみました。  
QtCore.Qt.QueuedConnectionがどのように作用するのかも確認したのですが、  
ここからどう組めば良いのかがわからなく。。  
(下記スクリプトでは、QProgressDialogが白く固まった状態で表示され、  
MainWindow.pushBtnの処理後にプログレスバーやキャンセルボタンが表示される)

[code]

    # -*- coding: utf-8 -*-
    
    import sys
    from PySide import QtCore, QtGui
    import time
    
    class MainWindow(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__()
    
            button = QtGui.QPushButton(self)
            button.setText("Click me")
            button.clicked.connect(self.pushBtn, QtCore.Qt.QueuedConnection)
    
            self.dialog = QtGui.QProgressDialog()
            button.clicked.connect(self.dialog.show)
    
        def pushBtn(self):    
            for i in range(3):
                print i
                time.sleep(2)
    
    
    def main():
        qApp = QtGui.QApplication(sys.argv)
        win = MainWindow(qApp)
        win.show()
        sys.exit(qApp.exec_())
    
    main()
    
[/code]

QThreadの処理と組み合わせるようにしていくのでしょうか?  
私の理解不足で申し訳ないのですが、よろしくお願いいたします。m(_ _)m


CC BY-SA 3.0
2017-09-19T00:44:25.027
38023
2017-09-19T00:44:25.027
24908
37960
answer


`pushBtn()`内のforループ内で、`QtGui.QApplication.processEvents()`を呼び出すようにすると、もう少しレスポンスが改善すると思います。

[code]

        # コード例
        # MainWindowのコンストラクタでQApplicationのオブジェクトを
        # 引数に取っているので、それをself.aとして格納している前提とします。
        def pushBtn(self):
            for i in range(3):
                print i
                a.processEvents()
                time.sleep(2)
    
[/code]

＊

この場合でも、「`time.sleep(2)`」の箇所で実行する(と予想する)「重い処理」中は操作に対するレスポンスは得られないと思うので、そうであれば、「重い処理」を`QThread`で切り離すことになると思います。


CC BY-SA 3.0
2017-09-25T09:31:23.293
38174
2017-09-25T09:31:23.293
20098
37960
answer