2013-03-28 2 views
3

После прочтения литературы по QProcesses и модуля многопроцессорности для python, я все еще испытываю трудности с созданием рабочего и гибкого графического интерфейса, когда в фоновом режиме происходят большие процессы. До сих пор я придумал эту упрощенную версию моего приложения, которая по-прежнему показывает похожие проблемы, о чем многие говорили.многопроцессорная обработка и обновление графического интерфейса - Qprocess или многопроцессорная обработка?

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

Это должно быть полностью выполнено, если у вас есть зависимости. На данный момент у меня есть QT-версия моей программы, которая работает с сигналами и слотами; Прежде всего, я считаю, что важно иметь возможность использовать все компьютерные процессоры, поскольку большинство пользователей имеют доступ к 8 ядрам. Таким образом, я хотел бы расширить этот подход с использованием сигнала/слота на многопроцессорную версию, используя multiprocessing или QProcess es.
У кого-нибудь есть предложения по использованию или отсутствию QProcess или multiprocessing? Хотя они и сложны для меня, QProcess кажется, что он имеет меньше форумов людей, использующих pyQt, поэтому я пошел с многопроцессорной обработкой. Будет ли проще работать с QProcess, поскольку у меня уже есть сигналы/слоты, работающие с потоками?

EDIT: Следует ли добавить такой класс, как предлагается?

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) 

ответ

2

QProcess для чтения и записи к трубам (оболочка/CMD). Используйте один из них.

  1. Создание класса с функцией рабочего и запустить его как нить по QtConcurrent.run(object, method, args)
  2. подключить QTimer.timeout() с функцией работника.
+1

большое спасибо за помощь, но я не уверен, что вы на самом деле означает. Должен ли я создать класс, который наследует QProcess и порождает поток из него? Я попробовал этот класс в моем отредактированном вопросе. – chase

+1

Я плохо разбираюсь в python, занимаюсь Qt с C++ годами.Но я немного понимаю, чего вы пытаетесь достичь. Насколько я знаю, 1. '' 'QProcess''' позволяет вам запустить другой процесс (скажем, cmd.exe или firefox.exe или что-то еще) внутри вашего приложения, и вы можете управлять (запускать, останавливать, писать, читать) их. 2. Если вы пытаетесь обработать (или сделать что-то) в фоновом режиме, перейдите в QThread или QTimer или QtConcurrent. Все они будут запускать ваш фрагмент кода в отдельном процессе. Таким образом, вы можете сделать одно из следующих действий. –

+1

a. Используйте '' 'QThread'''. Создайте класс, который наследует QThread и перегружает его метод run(). Напишите в нем свои методы complex_processing_on_spectra. Или вы можете использовать объект Spectra внутри этого класса. Излучайте сигнал, когда происходит итерация. Подключите этот сигнал к слоту, который обновит интерфейс. б. Используйте '' 'QTimer'''. Подключить таймер '' 'timeout()' '' signal с '' 'update_GUI()' '' с интервалом n milisec. c. Используйте '' '' QtConcurrent'''. Но на этот раз вы можете избежать этого. Я не уверен, что это надежный API. –

18

Даже если вопрос стар и был дан ответ, я хотел бы добавить некоторые уточнения:

  • AFAIK QtConcurrent не доступен в PyQt.
  • Темы в Qt с C++ отличаются от потоков PyQt или python. Последние должны приобретать GIL python (блокировка глобального интерпретатора), когда они запускаются, что фактически означает отсутствие реального параллелизма между потоками python/pyqt.

Нити Python в порядке, чтобы поддерживать gui отзывчиво, но вы не увидите каких-либо улучшений производительности для задач, связанных с процессором. Я рекомендую использовать multiprocessing вместе с QThread в основном процессе, который обрабатывает связь с дочерним процессом. Вы можете использовать сигналы и слоты между основным (gui) и вашим коммуникационным потоком.

enter image description here

редактировать: Я просто была такая же проблема и сделал что-то вдоль этих линий:

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) 
+0

Кто-нибудь заметил «сдвиг», когда у вас есть несколько аргументов «args = (queue, inp1, inp2)», когда я вызываю целевую функцию, которая похожа: 'def target_fun (self, inp1, inp2, ...)' 'Process' интерпретирует это как' args = (queue, inp2, inp3) ', что делает очередь здесь, если я не помещаю ее в args, все работает нормально ... – Hiatus

+0

@Hiatus Надеюсь, что вы решили проблему сейчас, однако похоже, что вы создаете процесс из функции члена класса, я не уверен, что вы можете это сделать? –

+0

Странно, похоже, что такой шаблон работает в PyQt, но не работает в PySide (1.2.4, на linux). В PySide метод '_run()', связанный с 'runner_thread.started', все еще работает в основном потоке. –

Смежные вопросы