mch*_*hen 21 python plot matplotlib
我希望以下内容能够显示一个情节,但我看不到情节,而且解释器只是悬挂(我的后端报告自己TkAgg).
import matplotlib.pyplot as plt
from threading import Thread
def plot():
fig, ax = plt.subplots()
ax.plot([1,2,3], [1,2,3])
plt.show()
def main():
thread = Thread(target=plot)
thread.setDaemon(True)
thread.start()
print 'Done'
Run Code Online (Sandbox Code Playgroud)
如何显示情节?
我正在运行一个包含大量迭代的模拟,并希望每1000次迭代更新我的绘图,以便我可以监视我的模拟是如何演变的.
Psuedocode如下:
iterations = 100000
for i in iterations:
result = simulate(iteration=i)
if not i % 1000:
# Update/redraw plot here:
# Add some lines, add some points, reset axis limits, change some colours
Run Code Online (Sandbox Code Playgroud)
在主线程中使用绘图会导致绘图GUI挂起/崩溃,大概是因为我正在进行其他工作.所以我的想法是在一个单独的线程中进行绘图.
我已经看到使用进程而不是线程的建议(例如这里).但是当我的模拟运行时,我无法操纵图形或轴来添加线等,因为图形对象在远程过程中.
我不相信这个问题是另一个问题的重复,因为这个问题涉及为什么pyplotapi不能用于操纵两个不同的图,每个图都在一个单独的线程上.这是因为同时执行两个图所产生的竞争条件阻止pyplot了确定哪个数字是当前数字.
但是,我只有1个情节,因此pyplot只有一个独特的电流数字.
Noe*_*raz 15
正如其他人所说,Matplotlib不是线程安全的,你有一个选择就是使用多处理.您说这对您不利,因为您需要从不同的流程访问轴,但您可以通过在模拟流程和根流程之间共享数据,然后管理根流程中的所有绘图相关活动来克服这一点.例如
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import multiprocessing
import time
import random
from Tkinter import *
#Create a window
window=Tk()
def main():
#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'
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 = matplotlib.figure.Figure()
ax = fig.add_subplot(1,1,1)
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([1,2,3], [1,2,10])
def updateplot(q):
try: #Try to check if there is data in the queue
result=q.get_nowait()
if result !='Q':
print result
#here get crazy with the plotting, you have access to all the global variables that you defined in the plot function, and have the data that the simulation sent.
line.set_ydata([1,result,10])
ax.draw_artist(line)
canvas.draw()
window.after(500,updateplot,q)
else:
print 'done'
except:
print "empty"
window.after(500,updateplot,q)
def simulation(q):
iterations = xrange(100)
for i in iterations:
if not i % 10:
time.sleep(1)
#here send any data you want to send to the other process, can be any pickable object
q.put(random.randint(1,10))
q.put('Q')
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)
小智 5
我遇到了类似的问题,我想从不同的线程更新 mapltolib 图,我在这里发布我的解决方案,以防其他人将来遇到类似的问题。
如前所述,tkagg 不是线程安全的,因此您必须确保对 matplotlib 的所有调用都来自单个线程。这意味着线程必须进行通信,以便“绘图线程”始终执行 matplotlib 函数。
我的解决方案是创建一个装饰器,它将执行“绘图线程”中的所有装饰函数,然后装饰所有相关函数。这允许您做您想做的事情,而无需对主代码中的语法进行任何更改。
即当您在一个线程中调用 ax.plot(...) 时,您将在另一个线程中自动执行它。
import matplotlib.pyplot as plt
import matplotlib
import threading
import time
import queue
import functools
#ript(Run In Plotting Thread) decorator
def ript(function):
def ript_this(*args, **kwargs):
global send_queue, return_queue, plot_thread
if threading.currentThread() == plot_thread: #if called from the plotting thread -> execute
return function(*args, **kwargs)
else: #if called from a diffrent thread -> send function to queue
send_queue.put(functools.partial(function, *args, **kwargs))
return_parameters = return_queue.get(True) # blocking (wait for return value)
return return_parameters
return ript_this
#list functions in matplotlib you will use
functions_to_decorate = [[matplotlib.axes.Axes,'plot'],
[matplotlib.figure.Figure,'savefig'],
[matplotlib.backends.backend_tkagg.FigureCanvasTkAgg,'draw'],
]
#add the decorator to the functions
for function in functions_to_decorate:
setattr(function[0], function[1], ript(getattr(function[0], function[1])))
# function that checks the send_queue and executes any functions found
def update_figure(window, send_queue, return_queue):
try:
callback = send_queue.get(False) # get function from queue, false=doesn't block
return_parameters = callback() # run function from queue
return_queue.put(return_parameters)
except:
None
window.after(10, update_figure, window, send_queue, return_queue)
# function to start plot thread
def plot():
# we use these global variables because we need to access them from within the decorator
global plot_thread, send_queue, return_queue
return_queue = queue.Queue()
send_queue = queue.Queue()
plot_thread=threading.currentThread()
# we use these global variables because we need to access them from the main thread
global ax, fig
fig, ax = plt.subplots()
# we need the matplotlib window in order to access the main loop
window=plt.get_current_fig_manager().window
# we use window.after to check the queue periodically
window.after(10, update_figure, window, send_queue, return_queue)
# we start the main loop with plt.plot()
plt.show()
def main():
#start the plot and open the window
thread = threading.Thread(target=plot)
thread.setDaemon(True)
thread.start()
time.sleep(1) #we need the other thread to set 'fig' and 'ax' before we continue
#run the simulation and add things to the plot
global ax, fig
for i in range(10):
ax.plot([1,i+1], [1,(i+1)**0.5])
fig.canvas.draw()
fig.savefig('updated_figure.png')
time.sleep(1)
print('Done')
thread.join() #wait for user to close window
main()
Run Code Online (Sandbox Code Playgroud)
请注意,如果您忘记装饰任何函数,则可能会出现分段错误。
此外,在此示例中,子线程处理绘图,主线程处理模拟。一般来说,建议执行相反的操作(即让主线程拥有图形)。
我想出了一个解决方案。不需要其他 UI 库。
以下源代码打开一个 matplotlib 窗口,其画布会定期更新。
import time
import _thread
import matplotlib.pyplot as plt
import numpy as np
def plotting_thread(fig, axe):
while (True):
mat = np.random.randn(256, 256)
time.sleep(2) # ... or some busy computing
axe.clear()
axe.imshow(mat)
fig.canvas.draw_idle() # use draw_idle instead of draw
fig = plt.figure() # the figure will be reused later
axe = fig.add_subplot(111)
_thread.start_new_thread(plotting_thread, (fig, axe))
plt.show()
Run Code Online (Sandbox Code Playgroud)
三个键:
canvas.draw_idle更新绘图,以保留窗口的响应plt.show在程序末尾使用来启动GUI主事件循环,这将阻止该程序。所以之后就不要写代码了。| 归档时间: |
|
| 查看次数: |
18464 次 |
| 最近记录: |