1

編集 #3: tcaswell は元の問題のいくつかの問題を解決しましたが、複数のオブジェクトを並行して使用する必要がある場合に、オブジェクトの同じインスタンスを参照しているように見えます。(tcaswellの回答のコメントセクションを参照)

元の問題: ユーザーが作成したオブジェクトを GUI に渡して GUI が更新され、「応答なし」に移動しないようにする際に発生している問題について、誰かが何か洞察を持っているかどうか疑問に思っていました。私はこれがかなり一般的な問題であることを知っており、QThreads、シグナル、スロット、マルチプロセッシングなどを理解しようとしているいくつかのフォーラムを読みましたが、まだ問題が発生しています。現在、ウィンドウがグレー表示されるのを回避していますが、バックグラウンドでいくつかの大きなプロセスを開始したい場合、プログラムは何もしません。

私のプロジェクトには、独自のプロセスで動作する複数のタブが必要ですが、各タブには、matplotlib プロットに表示する独自のデータがあります。データの処理を開始し、matplotlib プロットの変更を表示するボタンがいくつかあります。スレッドを整理する方法に関するアイデアの多くは、このスレッドから生まれました。ボタンが押された後に開始される関数は次のとおりです。

# This appears to be where the problem lies because this should initialize all of the processes
def process_tabs(self):
    for special_object in self.special_objects_list:
        thread = QtCore.QThread(parent=self)
        worker = Worker(special_object)
        worker.moveToThread(thread)
        worker.signal.connect(self.update_GUI)
        thread.start()
    return

ワーカーは、オブジェクトを送信して GUI を更新する一連のシグナルをループ内で作成する必要があります。これが私が作ったワーカークラスです:

# This class performs the iterative computation that needs to update the GUI
# the signals it send would *ideally* be special_obect objects so any of the parameters can be shown
class Worker(QtCore.QObject):
    signal = QtCore.pyqtSignal(QtCore.QObject)
    done = QtCore.pyqtSignal()
    def __init__(self, special_object):
        QtCore.QObject.__init__(self)
        self.special_object = special_object
    @QtCore.pyqtSlot()
    def process_on_special_object(self):
        # do a long fitting process involving the properties of the special_object
        for i in range(0,99999999999999999):
            self.special_object.Y += .1
            self.signal.emit(self.special_object)
        self.done.emit()
        return

これについて何か助けてくれてありがとう、それは大歓迎です。

編集: tcaswell のスキーマに従うようにコードを書き直し、Python スロット デコレータを変更して、special_objects を update_GUI スロットに渡しました。

もう一度編集: time.sleep(0.03) を追加して、GUI の応答性を維持します。完全な形式の新しいコードは次のとおりです。

import multiprocessing as mp
from PyQt4 import QtGui, QtCore
import numpy as np
import matplotlib
matplotlib.use('QtAgg')
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import figure
import sys
import lmfit
import time
# This object will just hold certain objects which will help create data objects ato be shown in matplotlib plots
# this could be a type of species with properties that could be quantized to a location on an axis (like number of teeth)
#, which special_object would hold another quantization of that property (like length of teeth) 
class object_within_special_object:
    def __init__(self, n, m):
        self.n = n
        self.m = m
    def location(self, i):
        location = i*self.m/self.n
        return location
    def NM(self):
        return str(self.n) + str(self.m)
# This is what will hold a number of species and all of their properties, 
# as well as some data to try and fit using the species and their properties
# I made this inherit QObject becuase I figured it *may* be more acceptable to send as a signal if the class inherited a PyQt4 class
class special_object(QtCore.QObject):
    def __init__(self, name, X, Y):
        QtCore.QObject.__init__(self)
        self.name = name
        self.X = X
        self.Y = Y
        self.params = lmfit.Parameters()
        self.things = self.make_a_whole_bunch_of_things()
        for thing in self.things:
            self.params.add('something' + str(thing.NM()) + 's', value = 3)
    def make_a_whole_bunch_of_things(self):
        things = []
        for n in range(0,20):
            m=1
            things.append(object_within_special_object(n,m))
        return things
# a special type of tab which holds a (or a couple of) matplotlib plots and a special_object ( which holds the data to display in those plots)
class Special_Tab(QtGui.QTabWidget):
    start_comp = QtCore.pyqtSignal()
    def __init__(self, parent, tmp_so):
        QtGui.QTabWidget.__init__(self, parent)
        self.special_object = tmp_so
        self.grid = QtGui.QGridLayout(self)
        # matplotlib figure put into tab
        self.fig = figure.Figure()
        self.plot = self.fig.add_subplot(111)
        self.line, = self.plot.plot(0, 0, 'r-')
        self.canvas = FigureCanvas(self.fig)
        self.grid.addWidget(self.canvas)
        self.canvas.show()
        self.canvas.draw()
        self.canvas_BBox = self.plot.figure.canvas.copy_from_bbox(self.plot.bbox)
        self.ax1 = self.plot.figure.axes[0]
        thread = QtCore.QThread(parent=self)
        self.worker = Worker(self.special_object)
        self.worker.moveToThread(thread)
        self.worker.update_signal.connect(self.update_GUI)
        #        self.worker.done_signal.connect(?)
        self.start_comp.connect(self.worker.process_on_special_object)
        thread.start()

    @QtCore.pyqtSlot(special_object)
    def update_GUI(self, tmp_so):
        """
        have the tab update it's self
        """
        # change the GUI to reflect changes made to special_object
        self.line.set_data(tmp_so.X, tmp_so.Y)
        self.ax1.set_xlim(tmp_so.X.min(), tmp_so.X.max())
        self.ax1.set_ylim(0, tmp_so.Y.max() + 0.05*tmp_so.Y.max())
        self.plot.draw_artist(self.line)
        self.plot.figure.canvas.blit(self.plot.bbox)

    def start_computation(self):
        self.start_comp.emit()

# This class performs the iterative computation that needs to update the GUI
# the signals it send would *ideally* be special_obect objects so any of the parameters can be shown
class Worker(QtCore.QObject):
    update_signal = QtCore.pyqtSignal(QtCore.QObject)
    done_signal = QtCore.pyqtSignal()
    def __init__(self, tmp_so):
        QtCore.QObject.__init__(self)
        self.tmp_so = tmp_so

    @QtCore.pyqtSlot()
    def process_on_special_object(self):
        # do a long fitting process involving the properties of the special_object
        for i in range(0,999):
            self.tmp_so.Y += .1
            time.sleep(0.03)
            self.update_signal.emit(self.tmp_so)
        self.done_signal.emit()
        return

# This window just has a button to make all of the tabs in separate processes
class MainWindow(QtGui.QMainWindow):
    process_signal = QtCore.pyqtSignal()
    def __init__(self, parent = None):
        # This GUI stuff shouldn't be too important
        QtGui.QMainWindow.__init__(self)
        self.resize(int(app.desktop().screenGeometry().width()*.6), int(app.desktop().screenGeometry().height()*.6))
        self.tabs_list = []
        self.special_objects_list = []
        central_widget = QtGui.QWidget(self)
        self.main_tab_widget = QtGui.QTabWidget()
        self.layout = QtGui.QHBoxLayout(central_widget)
        self.layout.addWidget(self.main_tab_widget)
        button = QtGui.QPushButton('Open Tabs')
        self.layout.addWidget(button)
        QtCore.QObject.connect(button, QtCore.SIGNAL("clicked()"), self.open_tabs)
        button2 = QtGui.QPushButton('process Tabs')
        self.layout.addWidget(button2)
        QtCore.QObject.connect(button2, QtCore.SIGNAL("clicked()"), self.process_tabs)
        self.setCentralWidget(central_widget)
        central_widget.setLayout(self.layout)

    # Here we open several tabs and put them in different processes
    def open_tabs(self):
        for i in range(0, 10):
            # this is just some random data for the objects
            X = np.arange(1240.0/1350.0, 1240./200., 0.01)
            Y = np.array(np.e**.2*X + np.sin(10*X)+np.cos(4*X))
            # Here the special tab is created
            temp_special_object = special_object(str(i), X, Y)
            new_tab = Special_Tab(self.main_tab_widget, temp_special_object)
            self.main_tab_widget.addTab(new_tab, str(i))
            # this part works fine without the .start() function
            self.tabs_list.append(new_tab)
        return
    # This appears to be where the problem lies because this should initialize all of the processes
    def process_tabs(self):
        for tab in self.tabs_list:
            tab.start_computation()

        return

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

1 に答える 1

1

まず、実際に を呼び出すことはないprocess_on_special_objectため、計算が実行されることはありません。

第二に、シグナルとスロットを正しく理解していないと思います。それらを逆止弁付きのパイプと考えてください。ASignalはパイプの開放端であり、物は流出のみ可能であり、aSlotは開放端にあり、物は流入のみ可能です。に接続SignalするとSlot、出力が入力にパッチされます。を呼び出すと、パイプに物を押し込んでいますemit。単一の を複数に接続したり、複数をSignal単一の に接続したりできます。SignalSlotsSignalsSlot

第三に、クラス名を変数名で隠さないように注意してください(special_object両方として使用しているようです)

第 4 に、無数のスレッドとワーカーを作成しますが、それらに対して何もしません。大幅なデザイン変更を提案します。Special_Tabクラスは完全に自己完結型であり、グラフ、ワーカー、およびスレッドをカプセル化したものと考える必要があります。

class Special_Tab(QtGui.QTabWidget):
    start_comp = QtCore.pyqtSignal()
    kill_thread = QtCore.pyqtSignal()
    def __init__(self, parent, tmp_so):
        QtGui.QTabWidget.__init__(self, parent)
        self.special_object = tmp_so
        self.grid = QtGui.QGridLayout(self)
        # matplotlib figure put into tab
        self.fig = figure.Figure()
        self.plot = self.fig.add_subplot(111)
        self.line, = self.plot.plot(0, 0, 'r-')
        self.canvas = FigureCanvas(self.fig)
        self.grid.addWidget(self.canvas)
        self.canvas.show()
        self.canvas.draw()
        self.canvas_BBox = self.plot.figure.canvas.copy_from_bbox(self.plot.bbox)
        ax1 = self.plot.figure.axes[0]
        thread = QtCore.QThread(parent=self)
        self.worker = Worker(self.special_object)
        self.worker.moveToThread(thread)
        self.worker.update_signal.connect(self.update_GUI)
        #        self.worker.done_signal.connect(?)
        self.start_comp.connect(self.worker.process_on_special_object)
        self.kill_thread.connect(thread.quit)
        thread.start()

    @QtCore.pyqtSlot(special_object)
    def update_GUI(self, tmp_so):
        """
        have the tab update it's self
        """
        # change the GUI to reflect changes made to special_object
        self.line.set_data(tmp_so.X, tmp_so.Y)
        self.plot.draw_artist(self.line)
        self.plot.figure.canvas.blit(self.plot.bbox)

    def start_computation(self):
        self.start_comp.emit()

    def closeEvent(self, ce):
        self.kill_thread.emit()
        QtGui.QTabWidget.closeEvent(self, ce)

関連する変更により、Worker

# This class performs the iterative computation that needs to update the GUI
# the signals it send would *ideally* be special_obect objects so any of the parameters can be shown
class Worker(QtCore.QObject):
    update_signal = QtCore.pyqtSignal(QtCore.QObject)
    done_signal = QtCore.pyqtSignal()
    def __init__(self, special_object):
        QtCore.QObject.__init__(self)
        self.special_object = special_object

    @QtCore.pyqtSlot()
    def process_on_special_object(self):
        # do a long fitting process involving the properties of the special_object
        for i in range(0,99999999999999999):
            self.special_object.Y += .1
            self.signal.emit(self.special_object)
        self.done.emit()
        return

これにより、メイン ウィンドウがシンプルになります

# This window just has a button to make all of the tabs in separate processes
class MainWindow(QtGui.QMainWindow):
    process_signal = QtCore.pyqtSignal()
    def __init__(self, parent = None):
        ## snipped
        pass

    # Here we open several tabs and put them in different processes
    def open_tabs(self):
        for i in range(0, 10):
            # this is just some random data for the objects
            X = np.arange(1240.0/1350.0, 1240./200., 0.01)
            Y = np.array(np.e**.2*X + np.sin(10*X)+np.cos(4*X))
            # Here the special tab is created
            temp_special_object = special_object(str(i), X, Y)
            new_tab = Special_Tab(self.main_tab_widget, temp_special_object)
            self.main_tab_widget.addTab(new_tab, str(i))
            # this part works fine without the .start() function
            self.tabs_list.append(new_tab)
        return
    # This appears to be where the problem lies because this should initialize all of the processes
    def process_tabs(self):
        for tab in self.tabs_list:
            tab.start_computation()

        return
    # This should be the 'slot' which the signal is communicating to
    def update_GUI(self, special_object):
        pass
于 2013-02-10T17:46:43.753 に答える