Python 2.7 matplotlib:从打印中删除修补程序

Python 2.7 matplotlib:从打印中删除修补程序,python-2.7,matplotlib,tkinter,Python 2.7,Matplotlib,Tkinter,我正在使用matplotlib以交互方式绘制一些补丁和点 我通过队列从单独的进程接收数据,并将其发送到绘图进程。这部分代码运行良好,点显示在图形上,并按预期在绘图中不断更新 根据用户的要求,我希望删除绘图中的所有旧补丁,并替换为新补丁 我想表演一下就足够了: # remove the old patch patch.remove() # I also tried ax.cla() without any success # create a new patch monitor_box = pa

我正在使用
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()
下面是该程序的屏幕截图,图中的红点四处移动,但补丁(黑色形状)从未改变。
我设法解决了这个问题,或者确切地说,我找到了解决这个问题的办法。我将它添加到下面,也许它将在将来帮助其他人。 我基本上是在主线程中更新图形时使用队列进行通信

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

我试图了解你的问题,看看是否能解决它。在用户请求时,我设法在matplotlib实时聊天中替换了补丁图。我的工作代码如下所示

在理解您的问题时,我认为多处理部分有点分散了对主要问题的注意力,即无法刷新tkinter窗口中嵌入的“matplotlib patches.Path”绘图。因此,我采用了Sentdex提供的LIVE matplotlib图表解决方案(在我的脚本中引用),该解决方案与您的代码非常相似,作为研究主要问题的基础。我认为他创作现场情节的方法是直截了当的

因此,在我的代码中,当tkinter窗口打开时,实时数据流进入tkinter窗口实时图表,它直接从文件“patchesCoor.txt”读取“matplotlib patches.Path”绘图的坐标,并用它更新实时图表。您必须在与脚本相同的文件夹中创建“patchesCoor.txt”,它应该包含一行条目,由8个数字组成,用空格分隔。每次修改并保存该文件中的坐标时,更改将显示在tkinter窗口的实时图表中。我的解决方案排除了多处理,尽管这可以作为这个脚本的下一个阶段来实现

在“def animate(i)”中可以找到负责更新实时图表中的绘图的部分。请参见我的评论

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


原始:更新:

updateplot
函数中不调用
patch.remove()
。那么您到底希望发生什么呢?@ImportanceOfBeingErnest我只是没有添加这部分代码,configurefortrait()和configureForLandscape()是从代码中的其他地方调用的。如果没有一个函数,几乎不可能找到问题。另一方面,创造这样一个环境并不困难。在我看来,如果你不能费心制作一个,为什么有人要费心为你找到一个解决方案。@importantanceofbeingernest请参见编辑,我添加了一个最小的、完整的示例
#!/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()