Confusion with matplotlib timers - python

I tried to implement the workaround mentioned here to the problem of the figure not updating properly when a draw_event is triggered once a user zooms into a plot. This was meant to improve this answer. In the solution, a Timer is added to the Canvas of the Figure, which delays the draw function for a short while.
In the linked answer this is done for a single figure and a reference to the timer is stored as self.timer to the class that contains the update function. I would like to have an a bit more general behaviour and allow this for many Figure instances. Therefore I tried not to save the Timer reference (I don't need it anymore). But when I do so, the script crashes with a Segmentation Fault (no traceback). Here is my code:
from matplotlib import pyplot as plt
import numpy as np
##plt.switch_backend('TkAgg')
class DrawEventHandler:
def __init__(self):
x = np.linspace(0,1,10)
y = np.sin(x)
self.fig, self.ax = plt.subplots()
self.ax.plot(x,y)
self.ax.figure.canvas.mpl_connect('draw_event', self.update)
def update(self, event = None):
self._redraw_later_ok()
##self._redraw_later_not_ok(self.fig)
def _redraw_later_ok(self):
self.timer = self.fig.canvas.new_timer(interval=10)
self.timer.single_shot = True
self.timer.add_callback(lambda : self.fig.canvas.draw_idle())
self.timer.start()
def _redraw_later_not_ok(self, fig):
print('start')
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
print('stop')
if __name__ == '__main__':
my_handler = DrawEventHandler()
plt.show()
The original solution is implemented as _redraw_later_ok(), which works fine for me. The problematic solution is called `_redraw_later_not_ok()', which produces this output:
start
stop
Segmentation fault: 11
I use a Mac with High Sierra, Python 3.6.4 or Python 2.7.14 and Matplotlib 2.1.1 with the MacOSX backend. When I switch to the TkAgg backend (which is terribly slow), the code works fine. Can anybody explain what is going on?

As usual with interactive features which use the event loop of a GUI you need to keep a reference to the object that handles the callback.
The problem with the approach of not storing the timer in this code
def _redraw_later_not_ok(self, fig):
print('start')
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
print('stop')
is that timer gets garbage collected once the _redraw_later_not_ok function terminates (i.e. directly after stop is printed). At this point there would be a pointer to a place in memory, which may or may not store the callback (any more). An attempt by the python interpreter to call the callback would hence (most probably) fail.
The solution is indeed to always keep a reference to the object that steers the callback. This is the solution shown in _redraw_later_ok, where the timer is made a class attribute, self.timer. Such that it can later be used at the time the callback is called.
I do not understand in how far using this working approach would prevent the use of several figures, so it may make sense to create a MWE with two figures that shows the problem clearly.

Related

Python Matplotlib: Clear figure when figure window is not open

I'm working with matplotlib plotting and use ioff() to switch interactive mode off to suppress the automatic opening of the plotting window on figrue creation. I want to have full control over the figure and only see it when explicitely using the show() command.
Now apparently the built-in commands to clear figures and axes do not work properly anymore.
Example:
import numpy as np
import matplotlib.pyplot as mpp
class PlotTest:
def __init__(self,nx=1,ny=1):
# Switch off interactive mode:
mpp.ioff()
# Create Figure and Axes:
self.createFigure(nx, ny)
def createFigure(self,nx=1,ny=1):
self.fig, self.axes = mpp.subplots(nx,ny)
if nx*ny == 1:
self.axes = np.array([self.axes])
def linePlot(self):
X = np.linspace(0,20,21)
Y = np.random.rand(21)
self.axes[0].plot(X,Y)
P = PlotTest()
P.linePlot()
P.fig.show()
Now I was thinking I could use P.fig.clear() any time to simply clear P.fig, but apparently that's not the case.
Writing P.fig.clear() directly into the script and execute it together it works and all I see is an empty figure. However that's rather pointless as I never get to see the actual plot like that.
Doing P.fig.clear() manually in the console does not do anything, regardless if the plot window is open or not, all other possible commands fail as well:
P.fig.clf()
P.axes[0].clear()
P.axes[0].cla()
mpp.clf()
mpp.cla()
mpp.close(P.fig)
Wrapping the command into a class method doesn't work either:
def clearFig(self):
self.fig.clear()
EDIT ================
After a clear() fig.axes is empty, yet show() still shows the old plot with the axes still being plotted.
/EDIT ================
Is it because I switched off interactive mode?
If you add a call to plt.draw() after P.fig.clear() it clears the figure. From the docs,
This is used in interactive mode to update a figure that has been altered, but not automatically re-drawn. This should be only rarely needed, but there may be ways to modify the state of a figure with out marking it as stale. Please report these cases as bugs.
I guess this is not a bug as you have switched off interactive mode so it is now your responsibility to explicitly redraw when you want to.
You can also use P.fig.canvas.draw_idle() which could be wrapper in the class as clearFigure method.

Plot freezing because of fast input stream to a GNU Radio block

I have implemented a sync block which plots inside its work function using the input_items values. Now the problem is that the plotting mechanism isn't fast enough for the input stream ( the value of input_items keeps on changing ).
I have tried to simplify the code as much as possible and added comments. Here it is:
....
import matplotlib
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
temp = ''
class xyz(gr.sync_block):
def __init__(self,parent,title,order):
a = []
self.count = 1
gr.sync_block.__init__(self,
name="xyz",
in_sig=[numpy.float32,numpy.float32],
out_sig=None)
self.win = xyzPlot(parent) #I have a Top Block(wxFrame). I am just making it the parent of xyzPlot(wxPanel) here.
def work(self, input_items, output_items):
global temp
if (self.count == 1):
temp = input_items+list()
bool = not(np.allclose(temp,input_items))#bool will be true if the value of `input_items` changes.
.......
#the code that evaluates z1,z2 depending on the value of input_items
.......
if ( bool or self.count == 1 ):
#If bool is true or it is the first time then I am calling plot() which plots the graph.
self.win.plot(tf(self.z1,self.z3),None,True,True,True,True)
self.count = 0
temp = input_items+list()
return len(input_items[0])
class xyzPlot(wx.Panel):
def __init__(self, parent, dB=None, Hz=None, deg=None):
wx.Panel.__init__(self , parent , -1 ,size=(600,475))
self.fig = Figure()
self.axes = self.fig.add_subplot(111)
def plot(self, syslist, omega=None, dB=None, Hz=None, deg=None, Plot=True, *args , **kwargs):
self.axes.clear() #I clear the graph here so that new values can be plotted.
.....
self.axes.semilogx(omega,mag,*args,**kwargs)
self.canvas = FigCanvas(self, -1, self.fig)
self.canvas.draw()
As you can see I am working with wxPython but the panel freezes whenever I change the value of input_items too fast ( It works fine if I change it slowly ). Any recommendations? I am new to this.
To cite another answer I gave:
This will quickly also get a multithreading problem. To be clear: What
you're trying to do (call a plotting function from a block thread) is
problematic and usually won't work.
The problem is that you're working in a complex multithreading environment:
each GNU Radio block works in its own thread
The WX Gui main loop runs continously to update the screen.
What you're doing here is, from a GNU Radio block thread, change what is shown in the window. That is a bad thing, because it changes things that are in the context of the WX Gui thread. This can work, if these changes don't conflict, and if the WX Gui thread doesn't access this kind of data while you're changing it (at some point, it has to access it -- otherwise, noone will update your window).
This is a problem common to all kind of updated GUIs, not only to GNU Radio!
Whether or not that happens is a mere case of probability: With a slowly updated display, your probability of conflict is low, but when you update often, it approaches 1.
The existing visualizations are written in C++ and take very great care to do things the right way -- which is, letting your Gui toolkit (WX in your case, though I explicitely recommend, and have recommended, to move away from that) know that things need to be updated, and then offering WX a function to update the display in its own thread.

Memory leak when embedding and updating a matplotlib graph in a PyQt GUI

I am trying to embed a matplotlib graph that updates every second into a PyQt GUI main window.
In my program I call an update function every second using threading.Timer via the timer function shown below. I have a problem: my program grows bigger every second - at a rate of about 1k every 4 seconds. My initial thoughts are that the append function (that returns a new array in update_figure) does not delete the old array? Is it possible this is the cause of my problem?
def update_figure(self):
self.yAxis = np.append(self.yAxis, (getCO22()))
self.xAxis = np.append(self.xAxis, self.i)
# print(self.xAxis)
if len(self.yAxis) > 10:
self.yAxis = np.delete(self.yAxis, 0)
if len(self.xAxis) > 10:
self.xAxis = np.delete(self.xAxis, 0)
self.axes.plot(self.xAxis, self.yAxis, scaley=False)
self.axes.grid(True)
self.i = self.i + 1
self.draw()
This is my timer function - this is triggered by the click of a button in my PyQt GUI and then calls itself as you can see:
def timer(self):
getCH4()
getCO2()
getConnectedDevices()
self.dc.update_figure()
t = threading.Timer(1.0, self.timer)
t.start()
EDIT: I cant post my entire code because it requires a lot of .dll includes. So i'll try to explain what this program does.
In my GUI I want to show the my CO2 value over time. My get_co22 function just returns a float value and I'm 100% sure this works fine. With my timer, shown above, I want to keep append a value to a matplotlib graph - the Axes object is available to me as self.axes. I try to plot the last 10 values of the data.
EDIT 2: After some discussion in chat, I tried putting the call to update_figure() in a while loop and using just one thread to call it and was able to make this minimal example http://pastebin.com/RXya6Zah. This changed the structure of the code to call update_figure() to the following:
def task(self):
while True:
ui.dc.update_figure()
time.sleep(1.0)
def timer(self):
t = Timer(1.0, self.task())
t.start()
but now the program crashes after 5 iterations or so.
The problem is definitely not with how you are appending to your numpy array, or truncating it.
The problem here is with your threading model. Integrating calculation loops with a GUI control loop is difficult.
Fundamentally, you need your GUI threading to have control of when your update code is called (spawning a new thread to handle it if necessary) - so that
your code does not block the GUI updating,
the GUI updating does not block your code executing and
you don't spawn loads of threads holding multiple copies of objects (which might be where your memory leak comes from).
In this case, as your main window is controlled by PyQt4, you want to use a QTimer (see a simple example here)
So - alter your timer code to
def task(self):
getCH4()
getCO2()
getConnectedDevices()
self.dc.update_figure()
def timer(self):
self.t = QtCore.QTimer()
self.t.timeout.connect(self.task)
self.t.start(1000)
and this should work. Keeping the reference to the QTimer is essential - hence self.t = QtCore.QTimer() rather than t = QtCore.QTimer(), otherwise the QTimer object will be garbage collected.
Note:
This is a summary of a long thread in chat clarifying the issue and working through several possible solutions. In particular - the OP managed to mock up a simpler runnable example here: http://pastebin.com/RXya6Zah
and the fixed version of the full runnable example is here: http://pastebin.com/gv7Cmapr
The relevant code and explanation is above, but the links might help anyone who wants to replicate / solve the issue. Note that they require PyQt4 to be installed
if you are creating a new figure for every time this is quite common.
matplotlib do not free the figures you create, unless you ask it, somethink like:
pylab.close()
see How can I release memory after creating matplotlib figures

wxpython interface becomes unresponsive when running live matplotlib draw() function

So I am using wxpython to make a GUI for a program. I have also embedded matplotlib graphs in this program.
My problem is when I try to use draw() to update the plot continuously, my program becomes unresponsible, however the matplotlib graph is still updating.
Here is part of my code, and how I execute all this,
def update(self, e):
if self.liveMode:
self.tune.plot(); # new plot
self.canvas.draw();
self.startLive(e);
def startLive(self, e):
self.liveMode = False;
refFreq = 500e6 / 80;
qx = self.pv1.get();
qy = self.pv2.get();
self.tune.newWorkingpoint(refFreq/qx, refFreq/qy);
self.tune.liveUpdate();
time.sleep(0.3);
self.update(e);
When the 'live mode' button is pressed in my program the 'update' method gets called.
I plot all the background lines, and then repeatedly get new point values from elsewhere and plot this as a circle on the graph and update with the draw() method. Of course since I am essentially in a continuous loop, nothing else will work until this process is finished. My goal was to just put a 'stop' button to stop this live plotting loop, but since everything becomes unresponsive when matplotlib is drawing, no buttons are clickable, and I have to essentially stop compiling to stop the program.
Is there anyway around this? I have looked into threading, but I get the same problems with this.
EDIT: ----------------------------------------------------------------------------------------------------------------------------I have got it working using this page as a guide. However, after a few seconds I get this fatal error and my program crashes.
'python2.7: Fatal IO error 11 (Resource temporarily unavailable) on X server :0.0.'
Here is the method I implemented. When a start button is pressed, 'startlive' starts, and when a stop button is pressed, 'stoplive' starts. Everything works fine, and my matplotlib graphs are updated properly without my wxpython GUI becoming unresponsive.
The problem is after a few seconds I get the fatal error and my program crashes. I believe this is most likely tied to the canvas.draw() method updating matplotlib inside a thread. Any ideas on how to get around this problem?
def startLive(self, e):
self.livemode.change(True);
self.worker = CountingThread(self.panel,self.tune,self.canvas,self.livemode, self.pv1, self.pv2);
self.worker.start();
def stopLive(self, evt):
self.livemode.change(False);
class CountingThread(threading.Thread):
def __init__(self, parent, tune, canvas, livemode, pv1, pv2):
threading.Thread.__init__(self)
self._parent = parent;
self._tune = tune;
self._canvas = canvas;
self._livemode = livemode;
self._pv1 = pv1;
self._pv2 = pv2;
def run(self):
refFreq = 500e6 / 80;
i=0;
while self._livemode.get():
self._tune.newWorkingpoint(refFreq / self._pv1.get(), refFreq / self._pv2.get());
self._tune.liveUpdate();
self._canvas.draw();
time.sleep(0.2);
i+=1;
print(i)
class livemode:
livemode=False;
def __init__(self):
self.livemode = False;
def change(self, livemode):
self.livemode = livemode;
def get(self):
return self.livemode;
--------------------------------EDIT---------------------------------
I solved the problem if anyone is interested,
In the threading run method, I removed the loop and simply had one instance of 'updating'. After the update, wx.PostEvent is called which calls a method back in the main class containing the wx.App which updates the plot by calling draw(). After this, the threading is restarted again and the same process repeats. My GUI continues to work and the plotting speed is still really fast. The important methods are below,
Threading.start() is called and the below exectures
def run(self):
refFreq = 500e6 / 80;
self._tune.newWorkingpoint(refFreq / self._pv1.get(), refFreq /self._pv2.get());
self._tune.liveUpdate();
evt = CountEvent(myEVT_PLOT, -1);
wx.PostEvent(self._parent, evt);
wx.PostEvent calls the following new method in main wx.App class,
def continueLive(self, evt):
if self.livemode.get():
self.canvas.draw();
self.startLive(evt)
The plot is updated and the whole process starts over.
This methods keeps the GUI responsive and the plotting speed does not slow down.
I think it might be a stack issue. Your update loop is recursive and the stack has a limited size (every function call gets pushed to the stack and removed once the function returns).
You should change it to a iterative version.
def update(self, e):
if self.liveMode:
self.tune.plot(); # new plot
self.canvas.draw();
#self.startLive(e); # causes endless recursion
def startLive(self, e):
while (self.isInLiveMode): # some flag here that gets set to False once the mode is deactivated
self.liveMode = False;
refFreq = 500e6 / 80;
qx = self.pv1.get();
qy = self.pv2.get();
self.tune.newWorkingpoint(refFreq/qx, refFreq/qy);
self.tune.liveUpdate();
time.sleep(0.3);
self.update(e);
Well i assume self.liveMode should be that flag.
Hope this helps, LG
Daniel

PySide QPropertyAnimation Not Starting

The problem is that when I call QPropertyAnimation.start(), nothing happens.
Color is the property I'm animating and button is the class.
class Button(QPushButton):
def __init__(self,text="",parent=None):
super(Button,self).__init__(text,parent)
# ...
self.innercolor = QColor(200,0,20)
def setcolor(self,value): self.innercolor = value
def getcolor(self): return self.innercolor
color = Property(QColor,getcolor,setcolor)
def paintEvent(self, event):
p = QPainter(self)
p.fillRect(self.rect(),self.color)
# ...
p.end()
def animated(self,value): print "animating"; self.update()
def enterEvent(self, event):
ani = QPropertyAnimation(self,"color")
ani.setStartValue(self.color)
ani.setEndValue(QColor(0,0,10))
ani.setDuration(2000)
ani.valueChanged.connect(self.animated)
ani.start()
print ani.state()
return QPushButton.enterEvent(self, event)
I'm confused because "animating" never prints out, but ani.state() says the animation is running.
I'm not asking to debug my code or anything, but I think there must be something I'm missing, either in my code or in my understanding of the use of QPropertyAnimation.
I've searched google for an answer, but nothing came up, nothing relevant to me anyway. The closest I found was another SO question, but I still couldn't turn that into an answer for myself. I also saw something about a custom interpolator, do I need to make a custom interpolator, if so, how do I do that.
Cool code. It almost works, but the animation isn't persisting past the enterEvent (although I don't entirely understand the mechanics.) If you change
ani = QPropertyAnimation(self,"color")
to
self.ani = QPropertyAnimation(self, "color")
# etc
then it will work.
I'm confused because "animating" never prints out, but ani.state() says the
animation is running.
At the point of the print, the anmiation exists and is running. When Python
returns from enterEvent, ani goes out of scope. As there are no other
reference to the object, Python garbage collects the object assuming there is
no need to maintain an object that is not referenced. Since the object is
deleted, the animation never executes.
Thats interesting, I'd love to know why the animation has to be a property
of the object.
The accepted answer changes ani to self.ani. This change provides a
refrence to the object outside the scope enterEvent. In the corrected code,
when enterEvent exits, the object maintains a reference to ani and it is
no longer garbage collected due to the additional reference. It exists when Qt
returns to the event loop and the animation successfully executes.

Categories