2016-03-23 46 views
2

編集:このようなPythonプログラムの究極の要件は次のとおりです。外部回路(おそらくいくつかのセンサーが装備されている)からUARTからデータを受信すると、プログラムはこれらのデータを処理し、コンピュータ画面上で動的に更新された曲線。python matplotlib:別のプロセスでプロットする

したがって、私は動的にプロットしたい、次のテストスクリプトはサブプロセスを開始し、そのプロセスで親プロセスからキューを介してデータを受け取り、それに従ってデータをプロットします。

しかし、スクリプトを実行すると、空の図だけが表示されます。コンソールに "put:"と "got:"というメッセージが表示され、親プロセスと副プロセスの両方が実行中で通信していますが、 GUIのFigureウィンドウ。

さらに、GUIウィンドウは反応せず、ウィンドウをクリックするとクラッシュします。

システムはWindows 10,64ビットです。 Pythonのバージョンは2.7(32ビット)です

ここで問題は何ですか?ありがとうございました!

あなたがそうでなければ何も表示されません、再スケールする必要が
import matplotlib.pyplot as plt 
import multiprocessing as mp 
import random 
import numpy 
import time 

def worker(q): 
    plt.ion() 
    ln, = plt.plot([], []) 
    plt.show() 

    while True: 
     obj = q.get() 
     n = obj + 0 
     print "sub : got:", n 

     ln.set_xdata(numpy.append(ln.get_xdata(), n)) 
     ln.set_ydata(numpy.append(ln.get_ydata(), n)) 
     plt.draw() 

if __name__ == '__main__': 
    queue = mp.Queue() 
    p = mp.Process(target=worker, args=(queue,)) 
    p.start() 

    while True: 
     n = random.random() * 5 
     print "main: put:", n 
     queue.put(n) 
     time.sleep(1.0) 

答えて

3

これは私のコンピュータ上で動作します:

import matplotlib.pyplot as plt 
import multiprocessing as mp 
import random 
import numpy 
import time 

def worker(q): 
    #plt.ion() 
    fig=plt.figure() 
    ax=fig.add_subplot(111) 
    ln, = ax.plot([], []) 
    fig.canvas.draw() # draw and show it 
    plt.show(block=False) 

    while True: 
     obj = q.get() 
     n = obj + 0 
     print "sub : got:", n 

     ln.set_xdata(numpy.append(ln.get_xdata(), n)) 
     ln.set_ydata(numpy.append(ln.get_ydata(), n)) 
     ax.relim() 

     ax.autoscale_view(True,True,True) 
     fig.canvas.draw() 

if __name__ == '__main__': 
    queue = mp.Queue() 
    p = mp.Process(target=worker, args=(queue,)) 
    p.start() 

    while True: 
     n = random.random() * 5 
     print "main: put:", n 
     queue.put(n) 
     time.sleep(1.0) 
+0

これは曲線を示すことができます。しかし、私はまだ混乱しています。なぜ、matplotlibがカーブを動的に更新するのはとても複雑ですか?これらの 'ax.relim'、' ax.autoscale_view(True、True、True) 'と' fig.canvas.draw()呼び出しは非常に不思議です。 ....さらに、このバージョンでは、図をクリックすると、ウィンドウはまだ「応答なし」になります。私はそれに描画するためのサブプロセスを作成することによって、それは問題ではないと思った。 Queue.get()コールがUIを応答不能にしていますか?データを待って、それを人物に描くのは正しい方法は何ですか? – zerox

+0

空のデータでプロットを初期化するときに、再スケールしないと何も表示されません。 matplotlibの説明は、http://matplotlib.org/users/artists.htmlをご覧ください。 「通常は」matplotlibはインタラクティブにされていないので、これを行うことで「限界を押し込む」ことに注意してください。あなたの姿を敏感に保つ方法かもしれませんが、私はそれを知らないのです。何が起こるかは、ウィンドウ全体ではなく、キャンバスを更新することです。あなたのOSはそうではなくてもそれを無応答として検出しています。 – CoMartel

+0

ご協力いただきありがとうございます、私はそれを見ていきます。 – zerox

0

私は答えとしてフラグ私の次のサンプルプログラムをしたいのですが今まで私の質問。それは間違いなく完璧なものではありません。あるいは、Pythonやmatplotlibでそれを行う正しい方法ではないかもしれません。

私が図に反応しないようにする重要なことは、UIが表示されたときにmatplotlibがイベントループを実行している可能性がありますので、time.sleep(0.1)またはスレッド実行をブロックするQueue.get()を呼び出すと、Figureウィンドウはハングアップします。

"Queue.get()"でスレッドをブロックするのではなく、受信する新しいデータのポーリング方法として "Queue.get_nowait()"を使用することを選択します。 UIスレッド(つまり、matplotlibフィギュアウィンドウの更新作業者)はmatplotlib.pyplot.pause()でのみブロックされますが、これはイベントループを中断しません。

matplotlibにシグナルをブロックして待機するコールがある場合は、このポーリング手法よりも優れていると思います。

最初はmultiprocessingの例がmatplotlibであるため、複数のプロセスを並行処理に使用しようとしていました。しかし、それはあなた自身が同期の世話をする必要があるようです、代わりにマルチスレッドを使用することは大丈夫です。マルチスレッド化は、1つのプロセス内でデータを共有する利点があります。したがって、次のプログラムはmultiprocessingの代わりにthreadingモジュールを利用しました。

私のテストプログラムは、Windows 7(64ビット)でPython 2.7で実行できます。Figure Windowはこの更新速度で反応し、ドラッグしたり、サイズを変更したりすることができます。

#!/usr/bin/python 
# vim: set fileencoding=utf-8: 

import random 
import time 
import Queue 
import threading 
import numpy as np 
import matplotlib.pyplot as plt 

## Measurement data that are shared among threads 
val1 = [] 
val2 = [] 
lock = threading.Lock() 

def update_data_sync(x, y): 
    lock.acquire() 
    val1.append(x) 
    val2.append(y) 
    if len(val1) > 50: 
     del val1[0] 
    if len(val2) > 50: 
     del val2[0] 
    lock.release() 

def get_data_sync(): 
    lock.acquire() 
    v1 = list(val1) 
    v2 = list(val2) 
    lock.release() 
    return (v1, v2) 

def worker(queue): 
    plt.ion() 
    fig = plt.figure(1) 
    ax = fig.add_subplot(111) 
    ax.margins(0.05, 0.05) 
    #ax.set_autoscale_on(True) 
    ax.autoscale(enable=True, axis='both') 
    #ax.autoscale(enable=True, axis='y') 
    ax.set_ylim(0, 1) 

    line1, line2 = ax.plot([], [], 'b-', [], [], 'r-') 

    while True: 
     need_draw = False 
     is_bye = False 

     while True: 
      ## Try to exhaust all pending messages 
      try: 
       msg = queue.get_nowait() 
       if msg is None: 
        print "thread: FATAL, unexpected" 
        sys.exit(1) 
       if msg == 'BYE': 
        print "thread: got BYE" 
        is_bye = True 
        break 
       # Assume default message is just let me draw 
       need_draw = True 
      except Queue.Empty as e: 
       # Not 'GO' or 'BYE' 
       break 

     ## Flow control 
     if is_bye: 
      break 
     if not need_draw: 
      plt.pause(0.33) 
      continue; 

     ## Draw it 
     (v1, v2) = get_data_sync() 
     line1.set_xdata(range(1, len(v1) + 1, 1)) 
     # Make a clone of the list to avoid competition on the same dataset 
     line1.set_ydata(v1) 
     line2.set_xdata(line1.get_xdata()) 
     line2.set_ydata(v2) 

     ## Adjust view 
     #ax.set_xlim(0, len(line1.get_ydata()) + 1) 
     #ax.set_ylim(0, 1) 
     ## (??) `autoscale' does not work here... 
     #ax.autoscale(enable=True, axis='x') 
     #ax.autoscale(enable=True, axis='y') 
     ax.relim() 
     ax.autoscale_view(tight=True, scalex=True, scaley=False) 

     ## "Redraw" 
     ## (??) Maybe pyplot.pause() can ensure visible redraw 
     fig.canvas.draw() 
     print "thread: DRAW" 
     plt.pause(0.05) 
    ## Holy lengthy outermost `while' loop ends here 

    print "thread: wait on GUI" 
    plt.show(block=True) 
    plt.close('all') 
    print "thread: worker exit" 
    return 

def acquire_data(): 
    # Fake data for testing 
    if not hasattr(acquire_data, 'x0'): 
     acquire_data.x0 = 0.5 
    x = int(random.random() * 100)/100.0 
    while np.abs(x - acquire_data.x0) > 0.5: 
     x = int(random.random() * 100)/100.0 
    acquire_data.x0 = x 

    y = 0.75 * np.abs(np.cos(i * np.pi/10)) + 0.15 

    return (x, y) 

if __name__ == "__main__": 
    queue = Queue.Queue() 
    thr = threading.Thread(target=worker, args=(queue,)) 
    thr.start() 

    for i in range(200): 
     (x, y) = acquire_data() 
     update_data_sync(x, y) 
     #print "main: val1: {}. val2: {}".format(x, y) 
     queue.put("GO") 
     time.sleep(0.1) 
    queue.put('BYE') 

    print "main: waiting for children" 
    thr.join() 
    print "main: farewell" 
関連する問題