I am having a problem getting matplotlib to work well with interactive plotting... what I see is that after displaying a few frames of my simulated data matplotlib hangs-and doesn't display any more.
Basically I've been playing around a bit with science simulations - and would like to be able to plot my results as they are being made - rather than at the end - using pylab.show().
I found a cookbook example from a while back that seems to do what I would want - in simple terms (although obv. the data is different). The cookbook is here...http://www.scipy.org/Cookbook/Matplotlib/Animations#head-2f6224cc0c133b6e35c95f4b74b1b6fc7d3edca4
I have searched around a little and I know that some people had these problems before - Matplotlib animation either freezes after a few frames or just doesn't work but it seems at the time there were no good solutions. I was wondering if someone has since found a good solution here.
I have tried a few 'backends' on matplotlib....TkAgg seems to work for a few frames.... qt4agg doesn't show the frames. I haven't yet got GTK to install properly.
I am running the most recent pythonxy(2.7.3).
Anyone have any advice?
import matplotlib
matplotlib.use('TkAgg') # 'Normal' Interactive backend. - works for several frames
#matplotlib.use('qt4agg') # 'QT' Interactive backend. - doesn't seem to work at all
#matplotlib.use('GTKAgg') # 'GTK' backend - can't seem to get this to work.... -
import matplotlib.pyplot as plt
import time
import numpy as np
plt.ion()
tstart = time.time() # for profiling
x = np.arange(0,2*np.pi,0.01) # x-array
line, = plt.plot(x,np.sin(x))
#plt.ioff()
for i in np.arange(1,200):
line.set_ydata(np.sin(x+i/10.0)) # update the data
line.axes.set_title('frame number {0}'.format(i))
plt.draw() # redraw the canvas
print 'FPS:' , 200/(time.time()-tstart)
EDIT:
edited code - to get rid of some style issues brought up.
Ok... So I have mangled together something that may sort of work for me....
Basically it is something like a watered down gui - but i'm hoping that it is a class i can import and basically forget about the details of (here's hoping).
I should say though - this is my first attempt at threading OR guis in python - so this code comes with a health warning.
** I'm not going to mark the question as answered though - because i'm sure someone more experienced will have a better solution.
'''
JP
Attempt to get multiple updating of matplotlibs working.
Uses WX to create an 'almost' gui with a mpl in the middle of it.
Data can be queued to this object - or you can directly plot to it.
Probably will have some limitations atm
- only really thinking about 2d plots for now -
but presumably can work around this for other implimentations.
- the working code seems to need to be put into another thread.
Tried to put the wx mainloop into another thread,
but it seemed unhappy. :(
Classes of Interest :
GraphData - A silly class that holds data to be plotted.
PlotFigure - Class of wx frame type.
Holds a mpl figure in it + queue to queue data to.
The frame will plot the data when it refreshes it's canvas
ThreadSimulation - This is not to do with the plotting
it is a test program.
Modified version of:
Copyright (C) 2003-2005 Jeremy O'Donoghue and others
License: This work is licensed under the PSF. A copy should be included
with this source code, and is also available at
http://www.python.org/psf/license.html
'''
import threading
import collections
import time
import numpy as np
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
import wx
class GraphData(object):
'''
A silly class that holds data to be plotted.
'''
def __init__(self, xdatainit, ydatainit):
self.xdata = xdatainit
self.ydata = ydatainit
class PlotFigure(wx.Frame):
def __init__(self ):
'''
Initialises the frame.
'''
wx.Frame.__init__(self, None, -1, "Test embedded wxFigure")
self.timerid = wx.NewId()
self.fig = Figure((5,4), 75)
self.canvas = FigureCanvasWxAgg(self, -1, self.fig)
self.toolbar = NavigationToolbar2Wx(self.canvas)
self.toolbar.Realize()
# On Windows, default frame size behaviour is incorrect
# you don't need this under Linux
tw, th = self.toolbar.GetSizeTuple()
fw, fh = self.canvas.GetSizeTuple()
self.toolbar.SetSize(wx.Size(fw, th))
# Now put all into a sizer
sizer = wx.BoxSizer(wx.VERTICAL)
# This way of adding to sizer allows resizing
sizer.Add(self.canvas, 1, wx.LEFT|wx.TOP|wx.GROW)
# Best to allow the toolbar to resize!
sizer.Add(self.toolbar, 0, wx.GROW)
self.SetSizer(sizer)
self.Fit()
wx.EVT_TIMER(self, self.timerid, self.onTimer)
self.dataqueue = collections.deque()
# Add an axes and a line to the figure.
self.axes = self.fig.add_subplot(111)
self.line, = self.axes.plot([],[])
def GetToolBar(self):
'''
returns default toolbar.
'''
return self.toolbar
def onTimer(self, evt):
'''
Every timer period this is called.
Want to redraw the canvas.
'''
#print "onTimer"
if len(self.dataqueue) > 0 :
data = self.dataqueue.pop()
x = data.xdata
y = data.ydata
xmax = max(x)
xmin = min(x)
ymin = round(min(y), 0) - 1
ymax = round(max(y), 0) + 1
self.axes.set_xbound(lower=xmin, upper=xmax)
self.axes.set_ybound(lower=ymin, upper=ymax)
self.line.set_xdata(x)
self.line.set_ydata(y)
# Redraws the canvas - does this even if the data isn't updated...
self.canvas.draw()
def onEraseBackground(self, evt):
'''
this is supposed to prevent redraw flicker on some X servers...
'''
pass
class ThreadSimulation(threading.Thread):
'''
Simulation Thread - produces data to be displayed in the other thread.
'''
def __init__(self, nsimloops, datastep, pltframe, slowloop = 0):
threading.Thread.__init__(self)
self.nsimloops = nsimloops
self.datastep = datastep
self.pltframe = pltframe
self.slowloop=slowloop
def run(self):
'''
This is the simulation function.
'''
nsimloops = self.nsimloops
datastep = self.datastep
pltframe = self.pltframe
print 'Sim Thread: Starting.'
tstart = time.time() # for profiling
# Define Data to share between threads.
x = np.arange(0,2*np.pi,datastep) # x-array
y = np.sin(x )
# Queues up the data and removes previous versions.
pltframe.dataqueue.append(GraphData(x,y))
for i in range(len(pltframe.dataqueue)-1):
pltframe.dataqueue.popleft()
pltframe.dataqueue
for i in np.arange(1, nsimloops):
x = x + datastep
y = np.sin(x)
# Queues up the data and removes previous versions.
pltframe.dataqueue.append(GraphData(x,y))
for i in range(len(pltframe.dataqueue)-1):
pltframe.dataqueue.popleft()
#pltframe.dataqueue
if self.slowloop > 0 :
time.sleep(self.slowloop)
tstop= time.time()
print 'Sim Thread: Complete.'
print 'Av Loop Time:' , (tstop-tstart)/ nsimloops
if __name__ == '__main__':
# Create the wx application.
app = wx.PySimpleApp()
# Create a frame with a plot inside it.
pltframe = PlotFigure()
pltframe1 = PlotFigure()
# Initialise the timer - wxPython requires this to be connected to
# the receiving event handler
t = wx.Timer(pltframe, pltframe.timerid)
t.Start(100)
pltframe.Show()
pltframe1.Show()
npoints = 100
nsimloops = 20000
datastep = 2 * np.pi/ npoints
slowloop = .1
#Define and start application thread
thrd = ThreadSimulation(nsimloops, datastep, pltframe,slowloop)
thrd.setDaemon(True)
thrd.start()
pltframe1.axes.plot(np.random.rand(10),np.random.rand(10))
app.MainLoop()
Related
i am trying to update a graph in tkinter .
To swap window in tkinter i use button and pack_forget()
But when re-opening the graph i have the following message:
MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
Each time the graph data change , so it must be updated. I know i have to use something like plt.clear() to erase an re-initialise the plot but i don't know how, can you help me?
#########GRAPH FRAME CONTENT##########
matplotlibframe = Frame(root,bd=2)
fig = Figure(figsize = (9, 15),dpi = 80)
canvas = FigureCanvasTkAgg(fig,master = matplotlibframe)
Return_Btn = Button(matplotlibframe,text='Return',command=machine)
def checkstock():
global canvas
global fig
hide_frame()
matplotlibframe.pack()
#Tell server i want to check the stock
tell_Stock = pickle.dumps("Graph")
client_socket.sendall(tell_Stock)
#Getting the stock data
Stock_status = client_socket.recv(1024)
Stock_status = pickle.loads(Stock_status)
plot1 = fig.add_subplot(111)
canvas.draw()
x = []
y = []
colors = []
for item in range(len(Stock_status)):
x.append(Stock_status[item][0])
y.append(Stock_status[item][1])
for loop in range(9):
if (loop % 2) == 0:
colors.append('#FCAB64')
else:
colors.append('#A1FCDF')
plot1.barh(x,y,height=0.5,color=colors)
plot1.set_title('Stock')
canvas.get_tk_widget().pack(side=LEFT)
Return_Btn.pack(side=RIGHT)
machine() contain a lots of things.
hide_frame() contain .pack_forget() for all other frame of my GUI
to swap frame , i do:
in machine():
def machine():
#plt.close(fig)
hide_frame()
frame.pack(side=LEFT,fill="none",expand=TRUE)
frameRight.pack(side=LEFT,fill="none",expand=TRUE,ipadx=50)
In hide_frame():
def hide_frame():
frame.pack_forget()
frameRight.pack_forget()
frameConfirm.pack_forget()
matplotlibframe.pack_forget()
canvas.get_tk_widget().pack_forget()
framePay.pack_forget()
I'm trying o thread the following code and send data to it (at random intervals) but I can't figure out how. I'm saving all the data to a txt file and reading the info from there, it isn't working very well. Is it possible to create a function that sends data to a specific thread( like : SendDataToThread(data, ThreadNumber) )? and how would I go about reading the data sent? I've seen a few solutions using queue but I was unable to understand them. here is the script I am temporarily using to plot the graph which I found here. sorry if the question seems simple but I've never before had to messed with threading or matplotlib.
import matplotlib.pyplot as plt
from threading import Thread
plt.ion()
class DynamicUpdate():
#Suppose we know the x range
min_x = 0
max_x = 10
def on_launch(self):
#Set up plot
self.figure, self.ax = plt.subplots()
self.lines, = self.ax.plot([],[], 'o')
#Autoscale on unknown axis and known lims on the other
self.ax.set_autoscaley_on(True)
self.ax.set_xlim(self.min_x, self.max_x)
#Other stuff
self.ax.grid()
...
def on_running(self, xdata, ydata):
#Update data (with the new _and_ the old points)
self.lines.set_xdata(xdata)
self.lines.set_ydata(ydata)
#Need both of these in order to rescale
self.ax.relim()
self.ax.autoscale_view()
#We need to draw *and* flush
self.figure.canvas.draw()
self.figure.canvas.flush_events()
#Example
def __call__(self):
# read/plot data
Here's some example code which shows how to do several of the things that were asked about. This uses multithreading rather than multiprocessing, and shows some examples of using queues, starting/stopping worker threads and updating a matplotlib plot with additional data.
(Part of the code comes from answers to other questions including this one and this one.)
The code shows a possible implementation of an asynchronous worker, to which data can be sent for subsequent processing. The worker uses an internal queue to buffer the data, and an internal thread (loop) that reads data from the queue, does some processing and sends the result for display.
An asynchronous plotter implementation is also shown. Results can be sent to this plotter from multiple workers. (This also uses an internal queue for buffering; this is done to allow the main program thread itself to call the function that updates the plot, which appears to be a requirement with matplotlib.)
NB This was written for Python 2.7 on OSX. Hope some of it may be useful.
import time
import threading
import Queue
import math
import matplotlib.pyplot as plt
class AsynchronousPlotter:
"""
Updates a matplotlib data plot asynchronously.
Uses an internal queue to buffer results passed for plotting in x, y pairs.
NB the output_queued_results() function is intended be called periodically
from the main program thread, to update the plot with any waiting results.
"""
def output_queued_results(self):
"""
Plots any waiting results. Should be called from main program thread.
Items for display are x, y pairs
"""
while not self.queue.empty():
item = self.queue.get()
x, y = item
self.add_point(x, y)
self.queue.task_done()
def queue_result_for_output(self, x, y):
"""
Queues an x, y pair for display. Called from worker threads, so intended
to be thread safe.
"""
self.lock.acquire(True)
self.queue.put([x, y])
self.lock.release()
def redraw(self):
self.ax.relim()
self.ax.autoscale_view()
self.fig.canvas.draw()
plt.pause(0.001)
def add_point(self, x, y):
self.xdata.append(x)
self.ydata.append(y)
self.lines.set_xdata(self.xdata)
self.lines.set_ydata(self.ydata)
self.redraw()
def __init__(self):
self.xdata=[]
self.ydata=[]
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111)
self.lines, = self.ax.plot(self.xdata, self.ydata, 'o')
self.ax.set_autoscalex_on(True)
self.ax.set_autoscaley_on(True)
plt.ion()
plt.show()
self.lock = threading.Lock()
self.queue = Queue.Queue()
class AsynchronousWorker:
"""
Processes data asynchronously.
Uses an internal queue and internal thread to handle data passed in.
Does some processing on the data in the internal thread, and then
sends result to an asynchronous plotter for display
"""
def queue_data_for_processing(self, raw_data):
"""
Queues data for processing by the internal thread.
"""
self.queue.put(raw_data)
def _worker_loop(self):
"""
The internal thread loop. Runs until the exit signal is set.
Processes the supplied raw data into something ready
for display.
"""
while True:
try:
# check for any data waiting in the queue
raw_data = self.queue.get(True, 1)
# process the raw data, and send for display
# in this trivial example, change circle radius -> area
x, y = raw_data
y = y**2 * math.pi
self.ap.queue_result_for_output(x, y)
self.queue.task_done()
except Queue.Empty:
pass
finally:
if self.esig.is_set():
return
def hang_up(self):
self.esig.set() # set the exit signal...
self.loop.join() # ... and wait for thread to exit
def __init__(self, ident, ap):
self.ident = ident
self.ap = ap
self.esig = threading.Event()
self.queue = Queue.Queue()
self.loop = threading.Thread(target=self._worker_loop)
self.loop.start()
if __name__ == "__main__":
ap = AsynchronousPlotter()
num_workers = 5 # use this many workers
# create some workers. Give each worker some ID and tell it
# where it can find the output plotter
workers = []
for worker_number in range (num_workers):
workers.append(AsynchronousWorker(worker_number, ap))
# supply some data to the workers
for worker_number in range (num_workers):
circle_number = worker_number
circle_radius = worker_number * 4
workers[worker_number].queue_data_for_processing([circle_number, circle_radius])
# wait for workers to finish then tell the plotter to plot the results
# in a longer-running example we would update the plot every few seconds
time.sleep(2)
ap.output_queued_results();
# Wait for user to hit return, and clean up workers
raw_input("Hit Return...")
for worker in workers:
worker.hang_up()
I kinda improved the code I can send a value to it when it is being created so that is good, but with multiprocessing I can't really figure out how to make the plot show. When I call the plot without multiprocessing it works so it might be something simple that I can't see. Also I'm trying to study the code you left a link to but to me, it's not very clear. I'm also trying to save the processes to a list so that later I can try to send the data directly to the process while the process is running(I think it's with pipe that I do this but, I'm not sure)
import matplotlib.pyplot as plt
from multiprocessing import Process
plt.ion()
class DynamicUpdate():
#Suppose we know the x range
min_x = 0
max_x = 10
def __init__(self, x):
self.number = x
def on_launch(self):
#Set up plot
self.figure, self.ax = plt.subplots()
self.lines, = self.ax.plot([],[], 'o')
#Autoscale on unknown axis and known lims on the other
self.ax.set_autoscaley_on(True)
self.ax.set_xlim(self.min_x, self.max_x)
#Other stuff
self.ax.grid()
...
def on_running(self, xdata, ydata):
#Update data (with the new _and_ the old points)
self.lines.set_xdata(xdata)
self.lines.set_ydata(ydata)
#Need both of these in order to rescale
self.ax.relim()
self.ax.autoscale_view()
#We need to draw *and* flush
self.figure.canvas.draw()
self.figure.canvas.flush_events()
#Example
def __call__(self):
print(self.number)
import numpy as np
import time
self.on_launch()
xdata = []
ydata = []
for x in np.arange(0,10,0.5):
xdata.append(x)
ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2))
self.on_running(xdata, ydata)
time.sleep(1)
return xdata, ydata
_processes_=[]
for i in range(0,2):
_processes_.append(Process(target=DynamicUpdate(i)))
p = Process(target=_processes_[i])
p.start()
# tried adding p.join(), but it didn't change anything
p.join()
I am trying to plot real time data coming to the computer using python. Data comes in a ROS topic and I use 'rospy' to subscribe to the topic in order to get data.
This is the code I wrote
import rospy
from sensor_msgs.msg import ChannelFloat32
import matplotlib.pyplot as plt
N = 200
i = 0
topic = "chatter"
x = range(N)
lmotor = [0]*N
rmotor = [0]*N
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlim([0,N])
ax.set_ylim([-1,1])
line1, = ax.plot(lmotor, 'r-')
line2, = ax.plot(rmotor, 'g')
def plotThrottle(data):
global x, lmotor, rmotor, i
[x[i],lmotor[i],rmotor[i], tmp] = data
line1.set_ydata(lmotor)
line1.set_xdata(x)
line2.set_ydata(rmotor)
line2.set_xdata(x)
fig.canvas.draw()
def callBack(packet):
data = list(packet.values)
plotThrottle(data)
def listner():
rospy.init_node('listener', anonymous=True)
rospy.Subscriber(topic, ChannelFloat32, callBack)
rospy.spin()
if __name__ == '__main__':
listner()
My problem is when I call plotThrottle() with the data I got from rostopic, I get following error.
[ERROR]
[WallTime: 1454388985.317080] bad callback: <function callBack at 0x7f13d98ba6e0>
Traceback (most recent call last):
File "/opt/ros/indigo/lib/python2.7/dist-packages/rospy/topics.py", line 720, in _invoke_callback
cb(msg)
File "dummy2.py", line 41, in callBack
plotThrottle(data)
File "dummy2.py", line 37, in plotThrottle
fig.canvas.draw()
File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 349, in draw
tkagg.blit(self._tkphoto, self.renderer._renderer, colormode=2)
File "/usr/lib/pymodules/python2.7/matplotlib/backends/tkagg.py", line 13, in blit
tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
RuntimeError: main thread is not in main loop
But if I use the same function and pass some data generated within the code (some random data) plot works fine.
I am an absolute beginner to python. I searched about this error and it says that this is because of some threading problem. But I don't understand how to fix this code. I am really grateful if someone can explain the problem and help fix this code.
Here you have two threads running, rospy.spin() and top.mainloop() (from Tkinter, backend of matplotlib in your case).
From this answer:
The problems stem from the fact that the _tkinter module attempts to
gain control of the main thread via a polling technique when
processing calls from other threads.
Your Tkinter code in Thread-1 is trying to peek into the main thread
to find the main loop, and it's not there.
From this answer:
If there is another blocking call that keeps your program running,
there is no need to call rospy.spin(). Unlike in C++ where spin() is
needed to process all the threads, in python all it does is block.
So you can use plt.show(block=True) to keep your program from closing, in that case you will use Tkinter mainloop, redrawing your canvas without problems.
The listener fuction should look like this:
def listener():
rospy.init_node('listener', anonymous=True)
rospy.Subscriber(topic, ChannelFloat32, callBack)
# rospy.spin()
plt.show(block=True)
Anyway this seems a bit a workaround for other alternatives see again this answer or simply use separate node for plotting i.e. ros suggested tools like rqt_graph.
Since this is an old post and still seems to be active in the community, I am going to provide an example, in general, how can we do real-time plotting. Here I used matplotlib FuncAnimation function.
import matplotlib.pyplot as plt
import rospy
import tf
from nav_msgs.msg import Odometry
from tf.transformations import quaternion_matrix
import numpy as np
from matplotlib.animation import FuncAnimation
class Visualiser:
def __init__(self):
self.fig, self.ax = plt.subplots()
self.ln, = plt.plot([], [], 'ro')
self.x_data, self.y_data = [] , []
def plot_init(self):
self.ax.set_xlim(0, 10000)
self.ax.set_ylim(-7, 7)
return self.ln
def getYaw(self, pose):
quaternion = (pose.orientation.x, pose.orientation.y, pose.orientation.z,
pose.orientation.w)
euler = tf.transformations.euler_from_quaternion(quaternion)
yaw = euler[2]
return yaw
def odom_callback(self, msg):
yaw_angle = self.getYaw(msg.pose.pose)
self.y_data.append(yaw_angle)
x_index = len(self.x_data)
self.x_data.append(x_index+1)
def update_plot(self, frame):
self.ln.set_data(self.x_data, self.y_data)
return self.ln
rospy.init_node('lidar_visual_node')
vis = Visualiser()
sub = rospy.Subscriber('/dji_sdk/odometry', Odometry, vis.odom_callback)
ani = FuncAnimation(vis.fig, vis.update_plot, init_func=vis.plot_init)
plt.show(block=True)
Note: change the rospy.Subscriber('/dji_sdk/odometry', Odometry, vis.odom_callback) as you need and do necessary changes accordingly.
I'm developing a GUI in python using Enthought's Traits. I keep getting a "RuntimeError: maximum recursion depth exceeded in cmp" If I flip the order in which the "Item" labels are used in my MainWindow class, the code executes fine. I can't seem to find any documentation on why this would happen. It seems like it has something to do with the Chaco plot. Below is my test code.
from chaco.api import ArrayPlotData, Plot
from traits.api import HasTraits, Instance, String, Float, Enum, Button, Str
from traitsui.api import Handler, View, Item, Group, HSplit, NoButtons, VGroup, VGrid
from pyface.api import GUI
from threading import Thread
from time import sleep
from enthought.enable.component_editor import ComponentEditor
from scipy import rand, indices, exp, sqrt, sum
import numpy as np
from PIL import Image
import matplotlib.image as mpimg
from enthought.chaco.api import gray
from enthought.savage.traits.ui.svg_button import SVGButton
class User_Input_Panel(HasTraits):
User = Str(name='User', label="User Name")
Sample_Name = Str(name='Sample_Name',label="Sample Name")
Path = Str(name='Path', label="Save Location", style = 'readonly')
#I use this to create a folder icon on the button
#~ Save_Folder_Button = SVGButton(label='Choose save Location', \
#~ filename=Folder-drag-accept.svg', \
#~ width=28, \
#~ height=28 \
#~ )
#~ #Create the User Information panel
User_Information_Panel = View(VGroup(
VGrid(
Item('User'),
Item('Sample_Name'),
Item('Path', width=.700, visible_when = 'Save_Visible == True'),
#Item('Save_Folder_Button', show_label=False),
),
show_border=True, label="User Information"
))
def _Save_Folder_Button_fired(self, event):
print("Pushed the Save Folder")
#self.file, self.dir = wxOpenFile(multi=False)
#fdir = GUI_tools.wxOpenFile()
#fdir = GUI_tools.wxSavePath()
#I'm planning on setting up threading later
class MainWindowHandler(Handler):
def close(self, info, is_OK):
#~ if (info.object.user_input.acquisition_thread and \
#~ info.object.user_input.acquisition_thread.isAlive()):
#~ info.object.user_input.acquisition_thread.wants_abort = True
#~ while info.object.user_input.acquisition_thread.isAlive():
#~ sleep(0.1)
#~ GUI.process_events()
return True
class MainWindow(HasTraits):
plot = Instance(Plot)
plotdata = Instance(ArrayPlotData, ())
user_input = Instance(User_Input_Panel, ())
def _user_input_default(self):
return User_Input_Panel(plotdata = self.plotdata)
Save_Folder_Button = SVGButton(label='Choose save Location', \
filename='C:\Python27\Examples\Mill_GUI\Tescan_BatchScan\Folder-drag-accept.svg', \
width=28, \
height=28 \
)
def _plot_default(self):
self.plotdata = ArrayPlotData(imagedata=np.zeros((100,100)))
plot = Plot(self.plotdata)
plot.img_plot('imagedata')
self.plot = plot
return plot
# If I flip the "Item('user_input'" with "Item('plot') the code will run...
view = View(VGroup(Item('user_input', style = 'custom', show_label = False),
Item('plot', editor = ComponentEditor(), dock = 'vertical'),
show_labels = False),
resizable = True, handler = MainWindowHandler(),
buttons = NoButtons)
if __name__ == '__main__':
MainWindow().configure_traits()
Does anybody know why you would get recursion errors with this code? I have to display the plot below my user_input panel so the users can see the live time data they are gathering.
Cheers,
Shivels
You need to remove the following line from your _plot_default method:
self.plot = plot
For this to happen in traits, you simply need to return that plot object and the assignment to self.plot will be done by Traits. The recursion error happens because you as trying to access the plot attribute of the object inside the method that is trying to set it.
The Chaco plotting toolkit for Python includes examples that show how to dynamically update existing plots. However, my application requires that I dynamically create and destroy plots depending on the data. I am new to programming with Chaco and Traits, so a simple example that illustrates how to do this would be really helpful.
This is a bit late, but here's an example that creates and destroys Chaco plots. The main interface is PlotSelector, which defines some fake data and radio buttons to switch between two different plot styles (line and bar plots).
This example uses a Traits event to signal when to close a plot, and then handles that signal with PlotController. There may be a better way to close the window, but I couldn't find one.
Edit: Updated imports for newer versions of Traits, Chaco, and Enable (ETS 4 instead of 3).
import numpy as np
import traits.api as traits
import traitsui.api as ui
import chaco.api as chaco
from enable.api import ComponentEditor
class PlotController(ui.Controller):
view = ui.View(ui.Item('plot', editor=ComponentEditor(), show_label=False),
height=300, width=300, resizable=True)
def object_close_signal_changed(self, info):
info.ui.dispose()
class BasicPlot(traits.HasTraits):
close_signal = traits.Event()
plot = traits.Instance(chaco.Plot)
class LinePlot(BasicPlot):
def __init__(self, plotdata):
self.plot = chaco.Plot(plotdata)
self.plot.plot(('x', 'y'))
class BarPlot(BasicPlot):
def __init__(self, plotdata):
self.plot = chaco.Plot(plotdata)
self.plot.candle_plot(('x', 'ymin', 'ymax'))
available_plot_types = dict(line=LinePlot, bar=BarPlot)
class PlotSelector(traits.HasTraits):
plot_type = traits.Enum(['line', 'bar'])
traits_view = ui.View('plot_type', style='custom')
def __init__(self, x, y):
ymin = y - 1
ymax = y + 1
self.plotdata = chaco.ArrayPlotData(x=x, y=y, ymin=ymin, ymax=ymax)
self.figure = None
def _plot_type_changed(self):
plot_class = available_plot_types[self.plot_type]
if self.figure is not None:
self.figure.close_signal = True
self.figure = plot_class(self.plotdata)
controller = PlotController(model=self.figure)
controller.edit_traits()
N = 20
x = np.arange(N)
y = x + np.random.normal(size=N)
plot_selector = PlotSelector(x, y)
plot_selector.configure_traits()
Note that the main interface (PlotSelector) calls configure_traits (starts application), while the plots are viewed with edit_traits (called from within application). Also, note that this example calls edit_traits from PlotController instead of calling it from the model. You could instead move the view from PlotController to BasicPlot and set the handler method of that view to PlotController.
Finally, if you don't need to totally destroy the plot window, then you may want to look at the Plot object's delplot method, which destroys the *sub*plot (here the line plot or bar plot).
I hope that helps.