2017-01-13 164 views
2

matplotlibinteractivelyプロットの一部はpatchespointsです。matplotlib:プロットからパッチを削除する

私はキューを介して別のプロセスからデータを受け取り、それらを私のプロットプロセスに送ります。コードのその部分は正常に機能し、ポイントはグラフに表示され、期待どおりにプロット内で継続的に更新されます。

ユーザからのご要望に応じて、私はプロット内のすべての古いパッチを削除し、新しいパッチで置き換えたいと思います。

私はそれを実行するのに十分だろうと思った:

# remove the old patch 
patch.remove() 
# I also tried ax.cla() without any success 
# create a new patch 
monitor_box = path.Path([(305, 500), (11, -213), (300, -220), (500, 1734)]) 
patch = patches.PathPatch(monitor_box, facecolor='black', lw=1) 
# add the patch to the axis 
ax.add_patch(patch) 

、その後、次の反復の間に、プロットは、新しいパッチで更新する必要があります。

canvas.draw() 

が、私は上記のコードを使用します、パッチはまだウィンドウに残り、何も変わりません。 (私はまだプロットのポイントを得ているので、それでも少なくとも継続的に更新されています)

以下、問題の最小限の実例を示しました。コードを実行すると、異なる点がプロットされますが、パッチは削除されません。

import matplotlib 
matplotlib.use('TkAgg') 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg 
import multiprocessing 
from Tkinter import * 
import matplotlib.path as path 
import matplotlib.patches as patches 
import sys, thread, time 

from random import randint 

#Create a window 
window=Tk() 

sendProximityInfo = True 
latest_published_msg = "" 

def erasePatchesAndCreateNew_A(): 
    print "erasePatchesAndCreateNew_A" 
    global line, ax, canvas 
    global monitor_box 
    global patch 
    patch.remove() 
    ax.cla()    
    monitor_box = path.Path([(35, 1677), (11, -213), (652, -220), (500, 1734)]) 
    patch = patches.PathPatch(monitor_box, facecolor='black', lw=1) 

    ax.add_patch(patch) 

def erasePatchesAndCreateNew_B(): 
    print "erasePatchesAndCreateNew_B" 
    global line, ax, canvas 
    global monitor_box 
    global patch 
    patch.remove() 
    ax.cla() 
    monitor_box = path.Path([(35, 500), (11, -213), (300, -220), (500, 1734)]) 
    patch = patches.PathPatch(monitor_box, facecolor='red', lw=1) 

    ax.add_patch(patch) 

monitor_box = path.Path([(35, 1677), (111, -213), (62, -220), (800, 1734)]) 
fig = matplotlib.figure.Figure() 
ax = fig.add_subplot(1,1,1) 
ax.set_xlim(-1500,2000) 
ax.set_ylim(-1500,2000) 
patch = patches.PathPatch(monitor_box, facecolor='black', lw=1) 
ax.add_patch(patch) 

def main(): 
    erasePatchesAndCreateNew_B() 

    #Create a queue to share data between process 
    q = multiprocessing.Queue() 
    #Create and start the simulation process 
    simulate = multiprocessing.Process(None, simulation,args=(q,)) 
    simulate.start() 
    #Create the base plot 
    plot() 
    #Call a function to update the plot when there is new data 
    updateplot(q) 

    window.mainloop() 
    print 'Done' 
    simulate.join() # wait for the other process to finish as well 

def plot(): #Function to create the base plot, make sure to make global the lines, axes, canvas and any part that you would want to update later 
    global line, ax, canvas 
    global monitor_box 
    global patch 

    fig = matplotlib.figure.Figure() 
    ax = fig.add_subplot(1,1,1) 
    ax.set_xlim(-1500,2000) 
    ax.set_ylim(-1500,2000) 

    patch = patches.PathPatch(monitor_box, facecolor='black', lw=1) 
    ax.add_patch(patch) 

    ax.invert_yaxis() 
    canvas = FigureCanvasTkAgg(fig, master=window) 
    canvas.show() 
    canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1) 
    canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1) 
    line, = ax.plot([], [], 'ro') 

def updateplot(q): 
    try:  #Try to check if there is data in the queue 
     result = q.get_nowait() 

     if result != 'Q': 
      x, y = result 
      line.set_data(x, y) 
      ax.draw_artist(line) 
      canvas.draw() 
      window.after(1,updateplot,q) 
     else: 
      print 'done' 
    except: 
     window.after(1,updateplot,q) 

def simulation(q): 
    try: 
     while True: 
      for i in range(10): 
       q.put((randint(0,1500), randint(0,1500))) 
       time.sleep(1) 
      erasePatchesAndCreateNew_A() 
      time.sleep(1) 
      for i in range(10): 
       q.put((randint(0,1500), randint(0,1500))) 
       time.sleep(1) 
      erasePatchesAndCreateNew_B() 
      time.sleep(1) 
    except KeyboardInterrupt: 
     print "received KeyboardInterrupt" 
    finally: 
     print "simulation ended" 
     sys.exit() 

if __name__ == '__main__': 
    main() 

以下はプログラムのスクリーンショットです。赤い点がグラフ内を移動しますが、パッチ(黒い形)は決して変化しません。 enter image description here

+0

。だから、あなたは何を期待していますか? – ImportanceOfBeingErnest

+0

@ImportanceOfBeingErnestコードの一部を追加しなかっただけで、configureForPortrait()とconfigureForLandscape()はコードのどこかから呼び出されます。 – theAlse

+0

[MCVE]がなければ、問題を見つけることはほとんど不可能です。一方、そのような[MCVE]の作成はそれほど難しいことではありません。私の意見では、あなたが1つを作るのに気を使うことができないならば、なぜ誰かがあなたのための解決策を見つけるのに気をつけなければなりません。 – ImportanceOfBeingErnest

答えて

1

私はこの問題を解決することができましたし、まさに私はこの問題を回避することができました。私はそれを下に追加していて、将来誰か他の人に役立つかもしれない。 私は基本的に、グラフがメインスレッドで更新されるべきときに通信するためにキューを使用しています。

import matplotlib 
matplotlib.use('TkAgg') 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg 
import multiprocessing 
from Tkinter import * 
import matplotlib.path as path 
import matplotlib.patches as patches 
import matplotlib.pyplot as plt 
import sys, thread, time 

from random import randint 

#Create a window 
window=Tk() 

sendProximityInfo = True 
latest_published_msg = "" 

monitor_box = path.Path([(1000, -1000), (111, -213), (62, -220), (800, 1734)]) 
fig = matplotlib.figure.Figure() 
ax = fig.add_subplot(1,1,1) 
ax.set_xlim(-1500,2000) 
ax.set_ylim(-1500,2000) 
patch = patches.PathPatch(monitor_box, facecolor='black', lw=1) 
ax.add_patch(patch) 

def main(): 
    #Create a queue to share data between process 
    q = multiprocessing.Queue() 
    #Create and start the simulation process 
    simulate = multiprocessing.Process(target=simulation,args=(q,)) 
    simulate.start() 
    #Create the base plot 
    plot() 
    #Call a function to update the plot when there is new data 
    updateplot(q) 

    window.mainloop() 
    print 'Done' 
    simulate.join() # wait for the other process to finish as well 

def plot(): #Function to create the base plot, make sure to make global the lines, axes, canvas and any part that you would want to update later 
    global line, ax, canvas, fig, monitor_box, patch 
    patch.remove() 
    monitor_box = path.Path([(500, -500), (111, -213), (62, -220), (800, 1734)]) 
    patch = patches.PathPatch(monitor_box, facecolor='pink', lw=1) 
    ax.add_patch(patch) 

    canvas = FigureCanvasTkAgg(fig, master=window) 
    canvas.show() 
    canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1) 
    canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1) 
    line, = ax.plot([], [], 'ro') 

def erasePatchesAndCreateNew_A(): 
    print "erasePatchesAndCreateNew_A" 
    global ax, monitor_box, patch 
    patch.remove() 
    monitor_box = path.Path([(35, 1677), (11, -213), (652, -220), (500, 1734)]) 
    patch = patches.PathPatch(monitor_box, facecolor='red', lw=1) 
    ax.add_patch(patch) 


def erasePatchesAndCreateNew_B(): 
    print "erasePatchesAndCreateNew_B" 
    global ax, monitor_box, patch 
    patch.remove() 
    monitor_box = path.Path([(-2000, 2000), (11, -213), (300, -220), (500, 1734)]) 
    patch = patches.PathPatch(monitor_box, facecolor='blue', lw=1) 
    ax.add_patch(patch) 


def updateplot(q): 
    try:  #Try to check if there is data in the queue 
     result = q.get_nowait() 

     if result != 'A' and result != 'B': 
      x, y = result 
      line.set_data(x, y) 
      ax.draw_artist(line) 
      canvas.draw() 
      window.after(10,updateplot,q) 
     elif result == 'A': 
      erasePatchesAndCreateNew_A() 
      canvas.draw() 
      window.after(10,updateplot,q) 
     elif result == 'B': 
      erasePatchesAndCreateNew_B() 
      canvas.draw() 
      window.after(10,updateplot,q) 
    except: 
     window.after(10,updateplot,q) 

def simulation(q): 
    try: 
     while True: 
      for i in range(5): 
       q.put((randint(0,1500), randint(0,1500))) 
       time.sleep(0.5) 
      #erasePatchesAndCreateNew_A() 
      q.put('A') 
      time.sleep(1) 
      for i in range(5): 
       q.put((randint(0,1500), randint(0,1500))) 
       time.sleep(0.5) 
      #erasePatchesAndCreateNew_B() 
      q.put('B') 
      time.sleep(1) 
    except KeyboardInterrupt: 
     print "received KeyboardInterrupt" 
    finally: 
     print "simulation ended" 
     sys.exit() 

if __name__ == '__main__': 
    main() 
1

問題を理解して解決できるかどうかを確認しようとしました。私は、LIVE matplotlibチャットのパッチプロットをユーザーの要求に応じて置き換えることができました。私の作業コードを以下に示します。

問題を消化している間、私はマルチプロセッシングセクションが主な問題に気をそらしていると考えました。つまり、tkinterウィンドウに埋め込まれた "matplotlib patches.Path"プロットを更新できませんでした。そのため、私はあなたの主な問題を研究するための基礎として、あなたのコードと非常に似ているSentdex(私のスクリプトで参照される)によってLIVE matplotlib Chartソリューションを採用しました。ライブプロットを生み出す彼のアプローチは分かりやすいと思います。

私のコードでは、ライブデータはtkinterウィンドウのLIVEチャートにストリームされているので、 "matplotlib patches.Path"プロットの座標をファイル"patchesCoorから直接読み込みます。 txt "とLIVEチャートを更新します。末尾に"patchesCoor.txt"をスクリプトと同じフォルダに作成する必要があります。また、空白で区切られた8つの数字で構成される1行のエントリを含める必要があります。そのファイルの座標を修正して保存するたびに、変更がtkinterウィンドウのLIVE Chartに表示されます。私のソリューションではマルチプロセッシングは除外されていますが、これはこのスクリプトの次の段階として実装できます。

LIVEチャートのプロットを更新するセクションは、「def animate(i)」にあります。そこに私のコメントを見てください。

あなたが投稿した答えの数時間後に来たものの、この解決法が役立つことを願っています。 :)

#!/usr/bin/python3.5 
# -*- coding: utf-8 -*- 
""" 
1. Script for creating LIVE matplotlib figure inside a tkinter window via 
    tkinter backend TkAgg (see Reference 1). I replaced the Pack system with a 
    Grid system for the Tk objects (see Reference 2), created the scripts to input 
    the live data and added your random coordinate generator. 

2. It requires 1 input file: 
    patchesCoor.txt - 4 x,y coordinate points for the patches.PathPatch plot 
        space seperated type. 

References: 
1. https://www.youtube.com/watch?v=Zw6M-BnAPP0 
2. http://stackoverflow.com/questions/12913854/displaying-matplotlib-navigation-toolbar-in-tkinter-via-grid 

Author: Sun Bear 
Created on: 17 Jan 2017 
""" 

import matplotlib 
matplotlib.use('TkAgg') # Backend of matplotlib 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg 
from matplotlib.figure import Figure 
import matplotlib.patches as patches 
import matplotlib.path as path 
import matplotlib.animation as animation 
from matplotlib import style 
style.use('ggplot') 

try: 
    # for Python2 
    import Tkinter as tk ## notice capitalized T in Tkinter 
    import ttk 
except ImportError: 
    # for Python3 
    import tkinter as tk 
    import tkinter.ttk as ttk 

import random 


class App(ttk.Frame): 
    ''' Create tkinter window frame with base matplotlib figure, subplot and 
     toolbar. ''' 

    def __init__(self, parent, *args, **kwargs): 

     # Customise ttk styles 
     s=ttk.Style() 
     s.configure(".", font=('URW Gothic L', '11', 'bold'), foreground='#3D3C3A', 
        cap=tk.ROUND, join=tk.ROUND) 
     s.configure('App.TFrame', background='pink') 

     # Initialise App Frame 
     ttk.Frame.__init__(self, parent, style='App.TFrame', borderwidth=20, 
          relief=tk.FLAT) 
     self.grid(row=0, column=0, sticky='nsew') 

     # Create tk Canvas 
     canvas = FigureCanvasTkAgg(f, self) 
     canvas.show() 
     canvas.get_tk_widget().grid(row=0, column=0, sticky='nsew') 

     # Create matplotlib navigation toolbar in a grid frame 
     toolbar_frame = ttk.Frame(self, style='App.TFrame', borderwidth=2, 
          relief=tk.RAISED) 
     toolbar_frame.grid(row=1, column=0, sticky='nsew') 
     toolbar = NavigationToolbar2TkAgg(canvas, toolbar_frame) 

     root.rowconfigure(0, weight=1) 
     root.columnconfigure(0, weight=1) 
     self.rowconfigure(0, weight=1) 
     self.columnconfigure(0, weight=1) 

def animate(i): 
    '''Provide matplotlib figure with latest plots coordinates and refresh 
     matplotlib figure.''' 

    # 1. Obtain x, y coordinates for Live data plot 
    xList, yList = simulate() 

    # 2. Obtain x, y coordinates for patches.PathPatch plot 
    patchesData = open('patchesCoor.txt', 'r').read() 
    print('patchesData = {}'.format(patchesData)) 
    patchesList = patchesData.split() 
    print('patchesList = {}'.format(patchesList)) 
    if len(patchesList) > 1: 
     x1,y1,x2,y2,x3,y3,x4,y4 = tuple(int(x) for x in patchesList) 
    print('patchesCoor = {0} {1} {2} {3} {4} {5} {6} {7}'. 
      format(x1,y1,x2,y2,x3,y3,x4,y4)) 
    monitor_box = path.Path([(x1, y1), (x2, y2), (x3, y3), (x4, y4)]) 
    patch = patches.PathPatch(monitor_box, facecolor='blue', lw=1) 

    # 3. Clear LIVE Chart and update it with latest plot data 
    ax.clear() 
    ax.plot(xList, yList) # For random x, y data plot 
    ax.add_patch(patch)  # For patches.PathPatch plot 
    ax.set_xlim(-1500,2000) # Need the following 2 lines to ensure 
    ax.set_ylim(-1500,2000) # the Live Chart axis is updated at every read 

def simulate(): 
    ''' Generate random x, y coordinate for Live data''' 
    xList = [] 
    yList = [] 
    for i in range(100): 
     x, y = random.randint(0,1500), random.randint(0,1500) 
     xList.append(int(x)) 
     yList.append(int(y)) 
    return xList, yList 

def matplotlib_base_figure(): 
    ''' Create matplotlib base figure ''' 
    f = Figure(figsize=(5,5), dpi=100) 
    ax = f.add_subplot(111) # One chart created 
    ax.plot([1,2,3,4,5,6,7,8], [5,6,1,8,6,10,2,7]) 
    monitor_box = path.Path([(35, 1677), (111, -213), (62, -220), (800, 1734)]) 
    patch = patches.PathPatch(monitor_box, facecolor='black', lw=1) 
    ax.add_patch(patch) 
    ax.set_xlim(-1500,2000) 
    ax.set_ylim(-1500,2000) 
    return f, ax 

if __name__ == '__main__': 

    # 1. Create matplotlib base figure and subplot 
    f, ax = matplotlib_base_figure() 

    # 2. Create tkinter GUI with matplotlib base figure, subplot & toolbar. 
    root = tk.Tk() 
    app = App(root) 

    # 3. Make matplotlib figure LIVE via animation 
    ani = animation.FuncAnimation(f, animate, interval=1000) 
    # 'interval' control the update rate of the LIVE Chart. 

    # 4. Activate GUI continually via an infinite loop. 
    app.mainloop() 

オリジナル:Before更新:() `` updateplot`関数内patch.remove `へのコールがないUpdated

関連する問題