6

QProcesses と python のマルチプロセッシング モジュールに関する文献を読んだ後でも、バックグラウンドで大きなプロセスが進行している間、動作するレスポンシブな GUI を作成するのにまだ問題があります。これまでのところ、このアプリケーションの単純化されたバージョンを考え出しましたが、多くの人が説明したものと同様の問題が依然として発生しています。

from PyQt4 import QtCore, QtGui
import multiprocessing as mp
import numpy as np
import sys
class Spectra:
    def __init__(self, spectra_name, X, Y):
        self.spectra_name = spectra_name
        self.X = X
        self.Y = Y
        self.iteration = 0

    def complex_processing_on_spectra(self, pipe_conn):
        self.iteration += 1
        pipe_conn.send(self.iteration)

class Spectra_Tab(QtGui.QTabWidget):
    def __init__(self, parent, spectra):
        self.parent = parent
        self.spectra = spectra
        QtGui.QTabWidget.__init__(self, parent)

        self.treeWidget = QtGui.QTreeWidget(self)
        self.properties = QtGui.QTreeWidgetItem(self.treeWidget, ["Properties"])
        self.step = QtGui.QTreeWidgetItem(self.properties, ["Iteration #"])

        self.consumer, self.producer = mp.Pipe()
        # Make process associated with tab
        self.process = mp.Process(target=self.spectra.complex_processing_on_spectra, args=(self.producer,))

    def update_GUI(self, iteration):
        self.step.setText(1, str(iteration))

    def start_computation(self):
        self.process.start()
        while(True):
            message = self.consumer.recv()
            if message == 'done':
                break
            self.update_GUI(message)
        self.process.join()
        return

class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self)

        self.setTabShape(QtGui.QTabWidget.Rounded)
        self.centralwidget = QtGui.QWidget(self)
        self.top_level_layout = QtGui.QGridLayout(self.centralwidget)

        self.tabWidget = QtGui.QTabWidget(self.centralwidget)
        self.top_level_layout.addWidget(self.tabWidget, 1, 0, 25, 25)

        process_button = QtGui.QPushButton("Process")
        self.top_level_layout.addWidget(process_button, 0, 1)
        QtCore.QObject.connect(process_button, QtCore.SIGNAL("clicked()"), self.process)

        self.setCentralWidget(self.centralwidget)
        self.centralwidget.setLayout(self.top_level_layout)

        # Open several files in loop from button - simplifed to one here
        X = np.arange(0.1200,.2)
        Y = np.arange(0.1200,.2)
        self.spectra = Spectra('name', X, Y)
        self.spectra_tab = Spectra_Tab(self.tabWidget, self.spectra)
        self.tabWidget.addTab(self.spectra_tab, 'name')

    def process(self):
        self.spectra_tab.start_computation()
        return

if __name__ == "__main__":
    app = QtGui.QApplication([])
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

依存関係がある場合、これは完全に実行できるはずです。現時点では、シグナルとスロットで動作するプログラムの QThreaded バージョンがあります。ただし、ほとんどのユーザーは最大 8 個のコアを使用できるため、コンピューターのすべてのプロセッサを使用できることが重要だと思います。multiprocessingそこで、このシグナル/スロット スレッド化アプローチをor QProcessesを使用してマルチプロセス バージョンに拡張したいと思います。
または を使用するかどうかについての提案はありますQProcessmultiprocessing? どちらも複雑ですが、QProcess は pyQt を使用するフォーラムが少ないように思われるため、マルチプロセッシングを使用しました。スレッドで動作するシグナル/スロットが既にあるので、QProcess を使用する方が簡単でしょうか?

編集:提案されているように、このようなクラスを追加する必要がありますか?

class My_Process(QtCore.QProcess):
    def __init__(self, spectra):
        QtCore.QProcess.__init__(self)
        self.spectra = spectra

    def worker(self):
        QtConcurrent.run(self.spectra, self.spectra.complex_processing_on_spectra)

    def run(self):
        QtCore.QObject.connect(self, QtCore.SIGNAL(QTimer.timeout()), self.worker)
4

2 に答える 2

24

質問は古く、回答済みですが、明確化を追加したいと思います。

  • AFAIKQtConcurrentは PyQt では利用できません。
  • C++ を使用した Qt のスレッドは、PyQt または Python スレッドとは異なります。後者はGIL、実行時に python (グローバル インタープリター ロック) を取得する必要があります。これは、事実上、python/pyqt スレッド間に実際の同時実行性がないことを意味します。

Python スレッドは、GUI の応答性を維持するのに問題ありませんが、CPU バウンド タスクのパフォーマンスの向上は見られません。子プロセスとの通信を処理するメイン プロセスmultiprocessingの と一緒に使用することをお勧めします。QThreadメイン (gui) と通信スレッドの間でシグナルとスロットを使用できます。

ここに画像の説明を入力

編集:私はちょうど同じ問題を抱えていて、これらの行に沿って何かをしました:

from multiprocessing import Process, Queue
from PyQt4 import QtCore
from MyJob import job_function


# Runner lives on the runner thread

class Runner(QtCore.QObject):
    """
    Runs a job in a separate process and forwards messages from the job to the
    main thread through a pyqtSignal.

    """

    msg_from_job = QtCore.pyqtSignal(object)

    def __init__(self, start_signal):
        """
        :param start_signal: the pyqtSignal that starts the job

        """
        super(Runner, self).__init__()
        self.job_input = None
        start_signal.connect(self._run)

    def _run(self):
        queue = Queue()
        p = Process(target=job_function, args=(queue, self.job_input))
        p.start()
        while True:
            msg = queue.get()
            self.msg_from_job.emit(msg)
            if msg == 'done':
                break


# Things below live on the main thread

def run_job(input):
    """ Call this to start a new job """
    runner.job_input = input
    runner_thread.start()


def handle_msg(msg):
    print(msg)
    if msg == 'done':
        runner_thread.quit()
        runner_thread.wait()


# Setup the OQ listener thread and move the OQ runner object to it
runner_thread = QtCore.QThread()
runner = Runner(start_signal=runner_thread.started)
runner.msg_from_job.connect(handle_msg)
runner.moveToThread(runner_thread)
于 2014-11-20T09:39:22.377 に答える
2

QProcessパイプ(シェル/コマンド)への読み書き用です。これらのいずれかを使用します。

  1. ワーカー関数を持つクラスを作成し、それをスレッドとして実行しますQtConcurrent.run(object, method, args)
  2. QTimer.timeout()ワーカー関数に接続します。
于 2013-03-28T08:15:42.210 に答える