我正在使用matplotlib
来interactively
绘制一些patches
和points
。
我通过队列从单独的进程接收数据并将它们发送到我的plot-process。代码的这一部分工作正常,点在图表上显示,并按预期在图中不断更新。
根据用户的要求,我想删除绘图中的所有旧补丁并替换为新补丁。
我认为这样就足够了:
# 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()
答案 0 :(得分: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 :(得分:1)
我试图了解您的问题,看看我是否可以解决它。我设法在用户请求时替换LIVE matplotlib聊天中的补丁图。我的工作代码如下所示。
在消化你的问题时,我认为多处理部分对主要问题有点分心,即无法刷新嵌入在tkinter窗口中的“matplotlib patches.Path”图。因此,我采用了Sentdex的LIVE matplotlib Chart解决方案(在我的脚本中引用),它与您的代码非常相似,作为研究主要问题的基础。我认为他制作现场情节的方法很容易理解。
所以在我的代码中,当tkinter窗口启动时,实时数据正在流入tkinter窗口LIVE图表,它直接从文件“patchesCoor读取”matplotlib patches.Path“的坐标.txt“并用它更新LIVE图表。您必须在与脚本相同的文件夹中创建“patchesCoor.txt”,并且它应包含一行条目,其中包含由空格分隔的8个数字。每次修改该文件中的坐标并保存时,更改都将显示在tkinter窗口的LIVE图表中。我的解决方案排除了多处理,尽管可以将其作为此脚本的下一个阶段实现。
负责更新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()