2016-12-30 9 views
0

私はPyQT GUIでスレッディングを実装しようとしていますが、問題があります。いくつかの背景、私はいくつかのソフトウェアをアンインストールし、いくつかのフォルダを削除し、新しいビルドを再インストールしたスタンドアロンスクリプトを持っています。私はフォルダを削除するためにスレッドモジュールを使用し、それぞれの新しいスレッドをスピンアップしました。フォルダのカップルが大きかったといくつかの時間がかかったので、私は別のスレッドを反復処理するが、大きなフォルダをスキップして、スレッドに参加するだろう:私はPyQT4を使用してUIを作り始めたらスレッディングとPyQTの理解

thread = threading.Thread(name=portalDirToDelete,target=deleteFolder,args=(portalDirToDelete,)) 
    thread.start() 
    .... 
    for thread in threading.enumerate(): 
     if not "MainThread" in thread.getName() and not "content" in thread.getName() and not "temp" in thread.getName(): 
     thread.join() 

、私がことがわかりました私がそれらに参加しようとするまで、スレッドは開始しませんでした。私はいくつかの読書をし、スレッドモジュールを使用してもはや動作しないことを学んだので(How to keep track of thread progress in Python without freezing the PyQt GUI?)、私はQThreadsを調べ始めましたが、多くの運がなかった。以下は私の他のスクリプトが何をしているかの簡易版である:

# -*- coding: utf-8 -*- 

# Form implementation generated from reading ui file 'Samples\ThreadUI1.ui' 
# 
# Created by: PyQt4 UI code generator 4.11.4 
# 
# WARNING! All changes made in this file will be lost! 

from PyQt4 import QtCore, QtGui 
import shutil, os, time 

try: 
    _fromUtf8 = QtCore.QString.fromUtf8 
except AttributeError: 
    def _fromUtf8(s): 
     return s 

try: 
    _encoding = QtGui.QApplication.UnicodeUTF8 
    def _translate(context, text, disambig): 
     return QtGui.QApplication.translate(context, text, disambig, _encoding) 
except AttributeError: 
    def _translate(context, text, disambig): 
     return QtGui.QApplication.translate(context, text, disambig) 

class workerThread(QtCore.QObject): 

    finished = QtCore.pyqtSignal() 

    def deleteFolder(self,path): 
     if os.path.exists(path): 
      shutil.rmtree(path) 
     self.finished.emit() 

class Ui_Form(object): 
    def setupUi(self, Form): 
     Form.setObjectName(_fromUtf8("Form")) 
     Form.resize(115, 66) 
     self.runApp = QtGui.QPushButton(Form) 
     self.runApp.setGeometry(QtCore.QRect(20, 20, 75, 23)) 
     self.runApp.setObjectName(_fromUtf8("runApp1")) 

     self.retranslateUi(Form) 
     QtCore.QMetaObject.connectSlotsByName(Form) 

     self.runApp.clicked.connect(lambda:self.runSetups()) 

    def retranslateUi(self, Form): 
     Form.setWindowTitle(_translate("Form", "Form", None)) 
     self.runApp.setText(_translate("Form", "Run App", None)) 

    def usingMoveToThread(self,path): 
     self.app = QtCore.QCoreApplication([]) 
     self.objThread = QtCore.QThread() 
     self.obj = workerThread() 
     self.obj.moveToThread(self.objThread) 
     self.obj.finished.connect(self.objThread.quit) 
     self.objThread.started.connect(self.obj.deleteFolder(path)) 
     self.objThread.finished.connect(app.exit) 
     self.objThread.start() 

    def runSetups(self): 
     self.usingMoveToThread(r"C:\arcgisportal") 
     for x in range(1,11): 
      print(x) 
      time.sleep(1) 

if __name__ == "__main__": 
    import sys 
    app = QtGui.QApplication(sys.argv) 
    Form = QtGui.QWidget() 
    ui = Ui_Form() 
    ui.setupUi(Form) 
    Form.show() 
    sys.exit(app.exec_()) 

私はこれを考えるには間違っているかもしれないが、私のアプローチは新しいスレッドでフォルダを削除するプロセスをキックオフし、それかどうかを確認することでしたループの中で印刷を続けます。私はThreading and PyQTをメインガイドとして使用してきました。 QThreadをサブクラス化することに関して意見が違うようですので、私はちょうどこのブログ(https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/)を使っていませんでした。

私はPython 2.7、PyQt4 4.11.4を使用しています。このアプリケーションはUI Designerで作成されています。生成されたui/pyファイルを更新するのは良い考えではないことを知っています。他の場所では使用していませんが、このサンプルをコンテキストとして提供するだけです。

ご協力いただきましてありがとうございます。

更新

私はモーダルQMessageBox()を開いて、それが開いている間、QMessageBoxをクローズボタンを追加する場合は、より大きなフォルダ内のフォルダは削除されます。 QMessageBox()を閉じると、フォルダの削除が止まるので、スレッドやUIに関して私には分かりません。

答えて

1

あなたが書いたものにいくつかの問題がありますが(うまくいけば、私はそれらすべてをキャッチしましたが、私はファイルを削除するに関連する何かをし、私のマシン上でコードを実行するには消極的だよ!)

  1. メソッドで新しいQCoreApplicationを作成しています。 Pythonのインスタンスごとに1つだけQApplicationする必要があります。これを行うことが正確にはわからないが、モーダルダイアログを作成するときの動作を説明していると思われる。 QApplication(またはQCoreApplication)を作成すると、信号/呼び出しスロットの処理とウィンドウの再描画処理のすべてを処理するメインのQtイベントループが開始されます。メインスレッドに2番目のイベントループを作成しましたが、exec_()という名前で開始していません。あなたがダイアログを作成するとき、これはexec_()(または第2のイベントループによって作成された問題を何らかの形で修正する第3のイベントループを作成する)を呼び出していると思われます。 手動で2番目のイベントループを作成する理由はありません。主なものはスレッドのものをうまく処理します。(注:pyqtアプリケーションごとに複数のイベントループを持つことができます。たとえば、スレッドにイベントループがあり(以下を参照)、ダイアログに独自のイベントループがあることがあります。

  2. runSetupsのループがメインスレッドをブロックするため、GUIが応答しなくなります。これはスレッドには影響しないはずですが、スレッドをスレッドにオフロードするのは、GUIをブロックしようとしているのですか? Python GILは複数のスレッドが同時に実行されるのを防ぎます。したがって、複数のスレッドが別々のフォルダを同時に削除したい場合は、Python GILを回避するためにマルチプロセッシングを調べることをお勧めします。しかし、これは時期尚早の最適化である可能性があります。おそらく、ディスクI/Oの考慮事項があり、これらのアプローチのいずれかが意味のある高速化を提供することがあります。

  3. 主な問題は次の行です:self.objThread.started.connect(self.obj.deleteFolder(path))。実際には、メインスレッドでdeleteFolder(path)を実行し、その関数の戻り値をconnectメソッドに渡しています。この結果、スレッドで何も実行されず、メインスレッドで発生しているすべての処理(およびGUIのブロック)が行われます。このエラーは、deleteFolderメソッドにパラメータを渡すために発生しています。しかし、あなたはstartedシグナルを発していないので(Qtはそうする)、パラメータを供給することはできません。通常は、lambdaまたはpartialにメソッドをラップすることでこの問題を回避しますが、これはスレッドで作業するときに他の問題を引き起こします(here参照)。その代わりに、のシグナルを定義し、str引数を取り、そのシグナルをdeleteFolderスロットに接続し、スレッドを開始した後に手動でシグナルを出す必要があります。

このような何か:

class Ui_Form(object): 
    deleteFolder = QtCore.pyqtSignal(str) 

    ... 

    def usingMoveToThread(self,path): 
     self.objThread = QtCore.QThread() 
     self.obj = workerThread() 
     self.obj.moveToThread(self.objThread) 
     self.deleteFolder.connect(self.obj.deleteFolder) 
     self.objThread.start() 
     self.deleteFolder.emit(path) 

あなたは各として、このアプローチについて少し詳しくthis質問(基本的に、Qtは異なるスレッドでシグナル/スロット間の接続を処理することができますをチェックアウトする場合がありますスレッドには独自のイベントループがあります)。

+0

本当に助けてくれてありがとう、本当に私が間違っていたことを理解するのを助けました!私はこれを試してみましょう、私は何か質問がある場合は戻ってきてみましょう。あなたのコメントのいくつかに対応するために、私はGUIをブロックすることにあまり関心がありません。このアプリの目的は、最新のビルドでソフトウェアスタックをアンインストールして再インストールすることです。実行したら、実際にメインのGUIを隠し、進行状況を報告する新しいダイアログを開きます。複数のスレッドに関する良い点は、各フォルダが同時に削除されているかどうかを確認することはありませんでした。もう一度、助けてくれてありがとう! – lilquinny14

+0

メインGUIを隠して別のダイアログを開いて進捗状況を記録することに興味があれば、新しいQThreadで起動できるようにrunSetupsを再作成することをお勧めしますか?それから、私は新しいダイアログに進行状況を渡します。 – lilquinny14

+0

'QDialog'をサブクラス化し、そのサブクラスの中に' runSetups'を置いておきたいと思うでしょう。次に、メインウィンドウからダイアログをインスタンス化し、メインウィンドウを非表示にして、ダイアログを表示してスレッドを開始するサブクラス内のメソッドを呼び出します。スレッドとダイアログプログレスバーの間の接続は、サブクラス内で行うことができ、モジュラーダイアログがあり、すべてが自己完結型です。 –

-2
# your app initialization: 
# application = QApplication(sys.argv) 
# main_window.show() 
# application.exec_() 
... 
thread = threading.Thread(name=portalDirToDelete,target=deleteFolder,args=(portalDirToDelete,)) 
    thread.start() 
    .... 
    for thread in threading.enumerate(): 
     if not "MainThread" in thread.getName() and not "content" in thread.getName() and not "temp" in thread.getName(): 
     while thread.is_alive(): 
      application.processEvents() 
+0

多分私は何かが不足していますが、大きなフォルダを削除するときにスクリプト全体がハングアップしないようにするため、バックグラウンドで実行することをお勧めします。各ループの後にprocessEvents()を呼び出すと、フォルダが大きいフォルダから削除されているように見えますが、表示されるのはわずかです。私はそれがprocessEvents()呼び出し中にフォルダを削除することができると仮定します。 – lilquinny14

+0

PyQtアプリケーションでPythonスレッドを使用することを追加したいと思います。http://stackoverflow.com/q/1595649/1994235 –

+0

によると、GUIのフリーズは問題​​ではありませんか? マルチスレッドを使用したいだけですか? – ADR

関連する問題