私は答えとしてフラグ私の次のサンプルプログラムをしたいのですが今まで私の質問。それは間違いなく完璧なものではありません。あるいは、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"
これは曲線を示すことができます。しかし、私はまだ混乱しています。なぜ、matplotlibがカーブを動的に更新するのはとても複雑ですか?これらの 'ax.relim'、' ax.autoscale_view(True、True、True) 'と' fig.canvas.draw()呼び出しは非常に不思議です。 ....さらに、このバージョンでは、図をクリックすると、ウィンドウはまだ「応答なし」になります。私はそれに描画するためのサブプロセスを作成することによって、それは問題ではないと思った。 Queue.get()コールがUIを応答不能にしていますか?データを待って、それを人物に描くのは正しい方法は何ですか? – zerox
空のデータでプロットを初期化するときに、再スケールしないと何も表示されません。 matplotlibの説明は、http://matplotlib.org/users/artists.htmlをご覧ください。 「通常は」matplotlibはインタラクティブにされていないので、これを行うことで「限界を押し込む」ことに注意してください。あなたの姿を敏感に保つ方法かもしれませんが、私はそれを知らないのです。何が起こるかは、ウィンドウ全体ではなく、キャンバスを更新することです。あなたのOSはそうではなくてもそれを無応答として検出しています。 – CoMartel
ご協力いただきありがとうございます、私はそれを見ていきます。 – zerox