【问题标题】:matplotlib: removing patches from plotmatplotlib:从情节中删除补丁
【发布时间】:2017-05-28 13:07:32
【问题描述】:

我正在使用matplotlibinteractively 绘制一些patchespoints

我通过队列从单独的进程接收数据并将它们发送到我的绘图进程。这部分代码工作正常,点显示在图表上,并按预期在图表中不断更新。

根据用户的要求,我想删除情节中的所有旧补丁并替换为新补丁。

我认为表演就足够了:

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

下面是程序的截图,红点在图表中移动,但补丁(黑色形状)从未改变。

【问题讨论】:

  • updateplot 函数中没有对patch.remove() 的调用。那么您到底希望发生什么?
  • @ImportanceOfBeingErnest 我只是没有添加那部分代码,configureForPortrait() 和 configureForLandscape() 是从代码的其他地方调用的。
  • 没有minimal reproducible example 几乎不可能找到问题所在。另一方面,创建这样的minimal reproducible example 并不难。在我看来,如果你懒得制作一个,为什么还要有人为你寻找解决方案。
  • @ImportanceOfBeingErnest 请查看编辑,我添加了一个最小的完整示例

标签: python-2.7 matplotlib tkinter


【解决方案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()

【讨论】:

    【解决方案2】:

    我试图了解您的问题,看看我是否可以解决它。我设法根据用户请求替换了 LIVE matplotlib 聊天中的补丁图。我的工作代码如下所示。

    在消化您的问题时,我认为多处理部分有点分散主要问题的注意力,即无法刷新嵌入在 tkinter 窗口中的“matplotlib patchs.Path”图。因此,我采用了 Sentdex 的 LIVE matplotlib Chart 解决方案(在我的脚本中引用),它与您的代码非常相似,作为研究您的主要问题的基础。我认为他生成现场情节的方法很容易理解。

    所以在我的代码中,当 tkinter 窗口启动时,实时数据正在流入 tkinter 窗口 LIVE 图表,它直接从文件中读取“matplotlib patch.Path”图的坐标 “patchesCoor .txt" 并用它更新实时图表。您必须在与脚本相同的文件夹中最后创建 "patchesCoor.txt",并且它应该包含单行条目,其中包含由空格分隔的 8 个数字。每次修改该文件中的坐标并保存时,更改都会出现在 tkinter 窗口的 LIVE Chart 中。我的解决方案不包括多处理,尽管这可以作为该脚本的下一阶段来实现。

    负责更新实时图表中的绘图的部分位于“def animate(i)”中。在那里看我的cmets。

    希望您发现此解决方案很有用,尽管它是在您发布答案几个小时后出现的。 :)

    #!/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()
    

    原文: 更新:

    【讨论】:

      猜你喜欢
      • 2014-03-08
      • 1970-01-01
      • 2019-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-19
      • 2020-12-11
      • 2017-01-06
      相关资源
      最近更新 更多