Real-time graph, redrawing a changing Y axis

Hello everyone,

Thomas Caswell has kindly directed me from the old Matplotlib-users mailing
list on Sourceforge, to this address at Python.org. I submitted this post
once before to this mailing list, yesterday, before I found the web page
for subscribing. If a duplicate message should appear, please accept my
apologies.

I am using MPL 1.4.2, Python 3.4, and Ubuntu 15.04. I am developing a
program which displays real-time data. On each update cycle, the graph
needs to adjust the y-axis scale. I have code that mostly works, but when
it re-renders the y-axis, it is not completely erasing the old one. I get
tick labels written on top of each other, until everything is an unreadable
smear, like this:

[image: Inline image 1]

Here is the relevant part of my update method:

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.redraw_in_frame()
# Something which erases the Y axis should go here?
self.ax.get_yaxis().draw(self.parent.get_renderer())

I think that the details of setting the Y limits are unimportant, but I've
included that code anyway, so that you can see my set_data() method call at
the top, and you can also see that I'm continually adjusting the Y range to
track the data.

I think that I am searching for a method in the axis class which would
erase the previously drawn tick marks and labels. So far, I haven't found
one. It would replace the comment line in my code.

I am trying to avoid redrawing the entire canvas on which this plot is
embedded, since there are several other live data plots besides the one I
have shown. The first version of my program did a full redraw, and it took
over 150 milliseconds to complete an update call. That's too slow for my
needs.

Thanks for any help you can provide!

···

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20160629/f98f94c3/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: For matplotlib-users.png
Type: image/png
Size: 14864 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20160629/f98f94c3/attachment-0001.png>

I am using MPL 1.4.2, Python 3.4, and Ubuntu 15.04. I am developing a program which displays real-time data. On each update cycle, the graph needs to adjust the y-axis scale. I have code that mostly works, but when it re-renders the y-axis, it is not completely erasing the old one. I get tick labels written on top of each other, until everything is an unreadable smear, like this:

I think thats because `redraw_in_frame()` doesn?t update any of the axes data?

Have you looked into the animation API? Its meant for this sort of thing, and seems pretty snappy. Most of what you are doing below would just go into the `animate` function.

http://matplotlib.org/examples/animation/simple_anim.html

Cheers, Jody

<For matplotlib-users.png>

Here is the relevant part of my update method:

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.redraw_in_frame()
# Something which erases the Y axis should go here?
self.ax.get_yaxis().draw(self.parent.get_renderer())

I think that the details of setting the Y limits are unimportant, but I've included that code anyway, so that you can see my set_data() method call at the top, and you can also see that I'm continually adjusting the Y range to track the data.

I think that I am searching for a method in the axis class which would erase the previously drawn tick marks and labels. So far, I haven't found one. It would replace the comment line in my code.

I am trying to avoid redrawing the entire canvas on which this plot is embedded, since there are several other live data plots besides the one I have shown. The first version of my program did a full redraw, and it took over 150 milliseconds to complete an update call. That's too slow for my needs.

Thanks for any help you can provide!

--
John J. Ladasky Jr., Ph.D.
Research Scientist
International Technological University
2711 N. First St, San Jose, CA 95134 USA
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20160629/5c2ac08c/attachment.html&gt;

···

On Jun 29, 2016, at 15:12 PM, John Ladasky <jladasky at itu.edu> wrote:

Hi Jody,

Thanks for your reply. I'm aware of Matplotlib's animation API. I'm not
sure that it would help me.

As far as I can tell, the purpose of animation.FuncAnimation() is to
connect a data generating function to the update function of a MPL object,
and to drive it all with a timer. It's an event loop, for people who
aren't already writing event-driven code (which I am, I have 1,200 lines of
mostly-working PyQt5). What actually gets done in the MPL update method is
what I think is important to me. I want to change only what needs to be
changed.

My approach is best described by this article:

http://bastibe.de/2013-05-30-speeding-up-matplotlib.html

The article is a few years old, I know. Still, using the advice I found in
articles like this one, I limited redrawing, and everything updates in
under 10 milliseconds, a 15-fold improvement over my original
redraw-everything approach. In total, I have five live graphs on the
screen. It's only this one self-adjusting time series graph that is
misbehaving for me.

···

On Wed, Jun 29, 2016 at 3:36 PM, Jody Klymak <jklymak at uvic.ca> wrote:

On Jun 29, 2016, at 15:12 PM, John Ladasky <jladasky at itu.edu> wrote:

I am using MPL 1.4.2, Python 3.4, and Ubuntu 15.04. I am developing a
program which displays real-time data. On each update cycle, the graph
needs to adjust the y-axis scale. I have code that mostly works, but when
it re-renders the y-axis, it is not completely erasing the old one. I get
tick labels written on top of each other, until everything is an unreadable
smear, like this:

I think thats because `redraw_in_frame()` doesn?t update any of the axes
data?

Have you looked into the animation API? Its meant for this sort of thing,
and seems pretty snappy. Most of what you are doing below would just go
into the `animate` function.

http://matplotlib.org/examples/animation/simple_anim.html

Cheers, Jody

<For matplotlib-users.png>

Here is the relevant part of my update method:

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.redraw_in_frame()
# Something which erases the Y axis should go here?
self.ax.get_yaxis().draw(self.parent.get_renderer())

I think that the details of setting the Y limits are unimportant, but I've
included that code anyway, so that you can see my set_data() method call at
the top, and you can also see that I'm continually adjusting the Y range to
track the data.

I think that I am searching for a method in the axis class which would
erase the previously drawn tick marks and labels. So far, I haven't found
one. It would replace the comment line in my code.

I am trying to avoid redrawing the entire canvas on which this plot is
embedded, since there are several other live data plots besides the one I
have shown. The first version of my program did a full redraw, and it took
over 150 milliseconds to complete an update call. That's too slow for my
needs.

Thanks for any help you can provide!

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20160629/4007b88e/attachment-0001.html&gt;

If you are re-computing the limits and re-drawing the yticks anyway, you
are not getting much benefit from being fancy about re-drawing. You might
as well do

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.figure.canvas.draw_idle()

which will schedule a full re-draw the next time that the GUI repaints (and
requests the the GUI do so 'some time soon'). Depending on your data-rates
this may allow multiple draws to be squashed into one. This assumes that
each of your axes is in it's own figure.

Failing that, you will have to do something like, on first draw / or resize
turn off the y-ticks, use `copy_from_bbox` to grab the empty region and the
use blit to restore it before drawing the next frame.

You might also get away with just keeping better track of when the view
limits _need_ to change and then only triggering the `draw_idle` for that.

Tom

···

On Wed, Jun 29, 2016 at 7:23 PM John Ladasky <jladasky at itu.edu> wrote:

Hi Jody,

Thanks for your reply. I'm aware of Matplotlib's animation API. I'm not
sure that it would help me.

As far as I can tell, the purpose of animation.FuncAnimation() is to
connect a data generating function to the update function of a MPL object,
and to drive it all with a timer. It's an event loop, for people who
aren't already writing event-driven code (which I am, I have 1,200 lines of
mostly-working PyQt5). What actually gets done in the MPL update method is
what I think is important to me. I want to change only what needs to be
changed.

My approach is best described by this article:

Speeding up Matplotlib

The article is a few years old, I know. Still, using the advice I found
in articles like this one, I limited redrawing, and everything updates in
under 10 milliseconds, a 15-fold improvement over my original
redraw-everything approach. In total, I have five live graphs on the
screen. It's only this one self-adjusting time series graph that is
misbehaving for me.

On Wed, Jun 29, 2016 at 3:36 PM, Jody Klymak <jklymak at uvic.ca> wrote:

On Jun 29, 2016, at 15:12 PM, John Ladasky <jladasky at itu.edu> wrote:

I am using MPL 1.4.2, Python 3.4, and Ubuntu 15.04. I am developing a
program which displays real-time data. On each update cycle, the graph
needs to adjust the y-axis scale. I have code that mostly works, but when
it re-renders the y-axis, it is not completely erasing the old one. I get
tick labels written on top of each other, until everything is an unreadable
smear, like this:

I think thats because `redraw_in_frame()` doesn?t update any of the axes
data?

Have you looked into the animation API? Its meant for this sort of
thing, and seems pretty snappy. Most of what you are doing below would
just go into the `animate` function.

http://matplotlib.org/examples/animation/simple_anim.html

Cheers, Jody

<For matplotlib-users.png>

Here is the relevant part of my update method:

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.redraw_in_frame()
# Something which erases the Y axis should go here?
self.ax.get_yaxis().draw(self.parent.get_renderer())

I think that the details of setting the Y limits are unimportant, but
I've included that code anyway, so that you can see my set_data() method
call at the top, and you can also see that I'm continually adjusting the Y
range to track the data.

I think that I am searching for a method in the axis class which would
erase the previously drawn tick marks and labels. So far, I haven't found
one. It would replace the comment line in my code.

I am trying to avoid redrawing the entire canvas on which this plot is
embedded, since there are several other live data plots besides the one I
have shown. The first version of my program did a full redraw, and it took
over 150 milliseconds to complete an update call. That's too slow for my
needs.

Thanks for any help you can provide!

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20160630/78aa3ec8/attachment.html&gt;

Hi Thomas,

Thanks for your reply.

self.ax.figure.canvas.draw_idle()

which will schedule a full re-draw the next time that the GUI repaints
(and requests the the GUI do so 'some time soon'). Depending on your
data-rates this may allow multiple draws to be squashed into one. This
assumes that each of your axes is in it's own figure.

Alas, at least for now, each of my five axes is contained in a single
Figure. I suppose that I could refactor my code and embed five separate
FigureCanvas objects, each containing one axes, in my PyQt object. If, as
you say, I would gain very little by getting fancy with the redrawing of
this one object, that may be the way to go.

I like your idea of blitting the empty region, though. Is anyone aware of
any example code that shows this being done?

···

On Wed, Jun 29, 2016 at 6:15 PM, Thomas Caswell <tcaswell at gmail.com> wrote:

If you are re-computing the limits and re-drawing the yticks anyway, you
are not getting much benefit from being fancy about re-drawing. You might
as well do

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.figure.canvas.draw_idle()

which will schedule a full re-draw the next time that the GUI repaints
(and requests the the GUI do so 'some time soon'). Depending on your
data-rates this may allow multiple draws to be squashed into one. This
assumes that each of your axes is in it's own figure.

Failing that, you will have to do something like, on first draw / or
resize turn off the y-ticks, use `copy_from_bbox` to grab the empty region
and the use blit to restore it before drawing the next frame.

You might also get away with just keeping better track of when the view
limits _need_ to change and then only triggering the `draw_idle` for that.

Tom

On Wed, Jun 29, 2016 at 7:23 PM John Ladasky <jladasky at itu.edu> wrote:

Hi Jody,

Thanks for your reply. I'm aware of Matplotlib's animation API. I'm not
sure that it would help me.

As far as I can tell, the purpose of animation.FuncAnimation() is to
connect a data generating function to the update function of a MPL object,
and to drive it all with a timer. It's an event loop, for people who
aren't already writing event-driven code (which I am, I have 1,200 lines of
mostly-working PyQt5). What actually gets done in the MPL update method is
what I think is important to me. I want to change only what needs to be
changed.

My approach is best described by this article:

Speeding up Matplotlib

The article is a few years old, I know. Still, using the advice I found
in articles like this one, I limited redrawing, and everything updates in
under 10 milliseconds, a 15-fold improvement over my original
redraw-everything approach. In total, I have five live graphs on the
screen. It's only this one self-adjusting time series graph that is
misbehaving for me.

On Wed, Jun 29, 2016 at 3:36 PM, Jody Klymak <jklymak at uvic.ca> wrote:

On Jun 29, 2016, at 15:12 PM, John Ladasky <jladasky at itu.edu> wrote:

I am using MPL 1.4.2, Python 3.4, and Ubuntu 15.04. I am developing a
program which displays real-time data. On each update cycle, the graph
needs to adjust the y-axis scale. I have code that mostly works, but when
it re-renders the y-axis, it is not completely erasing the old one. I get
tick labels written on top of each other, until everything is an unreadable
smear, like this:

I think thats because `redraw_in_frame()` doesn?t update any of the axes
data?

Have you looked into the animation API? Its meant for this sort of
thing, and seems pretty snappy. Most of what you are doing below would
just go into the `animate` function.

http://matplotlib.org/examples/animation/simple_anim.html

Cheers, Jody

<For matplotlib-users.png>

Here is the relevant part of my update method:

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.redraw_in_frame()
# Something which erases the Y axis should go here?
self.ax.get_yaxis().draw(self.parent.get_renderer())

I think that the details of setting the Y limits are unimportant, but
I've included that code anyway, so that you can see my set_data() method
call at the top, and you can also see that I'm continually adjusting the Y
range to track the data.

I think that I am searching for a method in the axis class which would
erase the previously drawn tick marks and labels. So far, I haven't found
one. It would replace the comment line in my code.

I am trying to avoid redrawing the entire canvas on which this plot is
embedded, since there are several other live data plots besides the one I
have shown. The first version of my program did a full redraw, and it took
over 150 milliseconds to complete an update call. That's too slow for my
needs.

Thanks for any help you can provide!

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20160629/eb69956f/attachment-0001.html&gt;

My go-to reference for blitting is either the animation.py source or Joe
Kington's answers on stackoverflow.

If you go down the blitting route you have to be careful to refresh your
saved canvas areas on re-size.

To extend on my comment about only re-drawing when you really have to. If
you mark an artist as `animated` (`art.set_animated(True)`) it will be
excluded from the normal draw loop. You can then, only when you have to,
trigger a full redraw (via `draw_idle` ), in a draw_event callback re-draw
the ticks, and then update the artists in the axes on every new data point.

Tom

···

On Wed, Jun 29, 2016 at 10:30 PM John Ladasky <jladasky at itu.edu> wrote:

Hi Thomas,

Thanks for your reply.

self.ax.figure.canvas.draw_idle()

which will schedule a full re-draw the next time that the GUI repaints
(and requests the the GUI do so 'some time soon'). Depending on your
data-rates this may allow multiple draws to be squashed into one. This
assumes that each of your axes is in it's own figure.

Alas, at least for now, each of my five axes is contained in a single
Figure. I suppose that I could refactor my code and embed five separate
FigureCanvas objects, each containing one axes, in my PyQt object. If, as
you say, I would gain very little by getting fancy with the redrawing of
this one object, that may be the way to go.

I like your idea of blitting the empty region, though. Is anyone aware of
any example code that shows this being done?

On Wed, Jun 29, 2016 at 6:15 PM, Thomas Caswell <tcaswell at gmail.com> > wrote:

If you are re-computing the limits and re-drawing the yticks anyway, you
are not getting much benefit from being fancy about re-drawing. You might
as well do

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.figure.canvas.draw_idle()

which will schedule a full re-draw the next time that the GUI repaints
(and requests the the GUI do so 'some time soon'). Depending on your
data-rates this may allow multiple draws to be squashed into one. This
assumes that each of your axes is in it's own figure.

Failing that, you will have to do something like, on first draw / or
resize turn off the y-ticks, use `copy_from_bbox` to grab the empty region
and the use blit to restore it before drawing the next frame.

You might also get away with just keeping better track of when the view
limits _need_ to change and then only triggering the `draw_idle` for that.

Tom

On Wed, Jun 29, 2016 at 7:23 PM John Ladasky <jladasky at itu.edu> wrote:

Hi Jody,

Thanks for your reply. I'm aware of Matplotlib's animation API. I'm
not sure that it would help me.

As far as I can tell, the purpose of animation.FuncAnimation() is to
connect a data generating function to the update function of a MPL object,
and to drive it all with a timer. It's an event loop, for people who
aren't already writing event-driven code (which I am, I have 1,200 lines of
mostly-working PyQt5). What actually gets done in the MPL update method is
what I think is important to me. I want to change only what needs to be
changed.

My approach is best described by this article:

Speeding up Matplotlib

The article is a few years old, I know. Still, using the advice I found
in articles like this one, I limited redrawing, and everything updates in
under 10 milliseconds, a 15-fold improvement over my original
redraw-everything approach. In total, I have five live graphs on the
screen. It's only this one self-adjusting time series graph that is
misbehaving for me.

On Wed, Jun 29, 2016 at 3:36 PM, Jody Klymak <jklymak at uvic.ca> wrote:

On Jun 29, 2016, at 15:12 PM, John Ladasky <jladasky at itu.edu> wrote:

I am using MPL 1.4.2, Python 3.4, and Ubuntu 15.04. I am developing a
program which displays real-time data. On each update cycle, the graph
needs to adjust the y-axis scale. I have code that mostly works, but when
it re-renders the y-axis, it is not completely erasing the old one. I get
tick labels written on top of each other, until everything is an unreadable
smear, like this:

I think thats because `redraw_in_frame()` doesn?t update any of the
axes data?

Have you looked into the animation API? Its meant for this sort of
thing, and seems pretty snappy. Most of what you are doing below would
just go into the `animate` function.

http://matplotlib.org/examples/animation/simple_anim.html

Cheers, Jody

<For matplotlib-users.png>

Here is the relevant part of my update method:

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.redraw_in_frame()
# Something which erases the Y axis should go here?
self.ax.get_yaxis().draw(self.parent.get_renderer())

I think that the details of setting the Y limits are unimportant, but
I've included that code anyway, so that you can see my set_data() method
call at the top, and you can also see that I'm continually adjusting the Y
range to track the data.

I think that I am searching for a method in the axis class which would
erase the previously drawn tick marks and labels. So far, I haven't found
one. It would replace the comment line in my code.

I am trying to avoid redrawing the entire canvas on which this plot is
embedded, since there are several other live data plots besides the one I
have shown. The first version of my program did a full redraw, and it took
over 150 milliseconds to complete an update call. That's too slow for my
needs.

Thanks for any help you can provide!

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20160630/45f8fa57/attachment.html&gt;

I thought ahead about the window resizing problem. I can see how that
might complicate the process of blitting. I fix the size of my application
window and its children on initialization, and disable window resizing.

···

On Wed, Jun 29, 2016 at 7:55 PM, Thomas Caswell <tcaswell at gmail.com> wrote:

My go-to reference for blitting is either the animation.py source or Joe
Kington's answers on stackoverflow.

If you go down the blitting route you have to be careful to refresh your
saved canvas areas on re-size.

To extend on my comment about only re-drawing when you really have to. If
you mark an artist as `animated` (`art.set_animated(True)`) it will be
excluded from the normal draw loop. You can then, only when you have to,
trigger a full redraw (via `draw_idle` ), in a draw_event callback re-draw
the ticks, and then update the artists in the axes on every new data point.

Tom

On Wed, Jun 29, 2016 at 10:30 PM John Ladasky <jladasky at itu.edu> wrote:

Hi Thomas,

Thanks for your reply.

self.ax.figure.canvas.draw_idle()

which will schedule a full re-draw the next time that the GUI repaints
(and requests the the GUI do so 'some time soon'). Depending on your
data-rates this may allow multiple draws to be squashed into one. This
assumes that each of your axes is in it's own figure.

Alas, at least for now, each of my five axes is contained in a single
Figure. I suppose that I could refactor my code and embed five separate
FigureCanvas objects, each containing one axes, in my PyQt object. If, as
you say, I would gain very little by getting fancy with the redrawing of
this one object, that may be the way to go.

I like your idea of blitting the empty region, though. Is anyone aware of
any example code that shows this being done?

On Wed, Jun 29, 2016 at 6:15 PM, Thomas Caswell <tcaswell at gmail.com> >> wrote:

If you are re-computing the limits and re-drawing the yticks anyway, you
are not getting much benefit from being fancy about re-drawing. You might
as well do

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.figure.canvas.draw_idle()

which will schedule a full re-draw the next time that the GUI repaints
(and requests the the GUI do so 'some time soon'). Depending on your
data-rates this may allow multiple draws to be squashed into one. This
assumes that each of your axes is in it's own figure.

Failing that, you will have to do something like, on first draw / or
resize turn off the y-ticks, use `copy_from_bbox` to grab the empty region
and the use blit to restore it before drawing the next frame.

You might also get away with just keeping better track of when the view
limits _need_ to change and then only triggering the `draw_idle` for that.

Tom

On Wed, Jun 29, 2016 at 7:23 PM John Ladasky <jladasky at itu.edu> wrote:

Hi Jody,

Thanks for your reply. I'm aware of Matplotlib's animation API. I'm
not sure that it would help me.

As far as I can tell, the purpose of animation.FuncAnimation() is to
connect a data generating function to the update function of a MPL object,
and to drive it all with a timer. It's an event loop, for people who
aren't already writing event-driven code (which I am, I have 1,200 lines of
mostly-working PyQt5). What actually gets done in the MPL update method is
what I think is important to me. I want to change only what needs to be
changed.

My approach is best described by this article:

Speeding up Matplotlib

The article is a few years old, I know. Still, using the advice I
found in articles like this one, I limited redrawing, and everything
updates in under 10 milliseconds, a 15-fold improvement over my original
redraw-everything approach. In total, I have five live graphs on the
screen. It's only this one self-adjusting time series graph that is
misbehaving for me.

On Wed, Jun 29, 2016 at 3:36 PM, Jody Klymak <jklymak at uvic.ca> wrote:

On Jun 29, 2016, at 15:12 PM, John Ladasky <jladasky at itu.edu> wrote:

I am using MPL 1.4.2, Python 3.4, and Ubuntu 15.04. I am developing a
program which displays real-time data. On each update cycle, the graph
needs to adjust the y-axis scale. I have code that mostly works, but when
it re-renders the y-axis, it is not completely erasing the old one. I get
tick labels written on top of each other, until everything is an unreadable
smear, like this:

I think thats because `redraw_in_frame()` doesn?t update any of the
axes data?

Have you looked into the animation API? Its meant for this sort of
thing, and seems pretty snappy. Most of what you are doing below would
just go into the `animate` function.

http://matplotlib.org/examples/animation/simple_anim.html

Cheers, Jody

<For matplotlib-users.png>

Here is the relevant part of my update method:

self.plot.set_data(self.x[:size], self.y[:size])
lo = self.y[:size].min()
hi = self.y[:size].max()
if hi - lo > 150:
    self.ax.set_ylim(lo-25, hi+25)
else:
    mid = self.y[:size].mean()
    self.ax.set_ylim(mid-100, mid+100)
self.ax.relim()
self.ax.autoscale_view(None, False, True)
self.ax.redraw_in_frame()
# Something which erases the Y axis should go here?
self.ax.get_yaxis().draw(self.parent.get_renderer())

I think that the details of setting the Y limits are unimportant, but
I've included that code anyway, so that you can see my set_data() method
call at the top, and you can also see that I'm continually adjusting the Y
range to track the data.

I think that I am searching for a method in the axis class which would
erase the previously drawn tick marks and labels. So far, I haven't found
one. It would replace the comment line in my code.

I am trying to avoid redrawing the entire canvas on which this plot is
embedded, since there are several other live data plots besides the one I
have shown. The first version of my program did a full redraw, and it took
over 150 milliseconds to complete an update call. That's too slow for my
needs.

Thanks for any help you can provide!

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
Matplotlib-users Info Page

--
*John J. Ladasky Jr., Ph.D.*
*Research Scientist*
*International Technological University*
*2711 N. First St, San Jose, CA 95134 USA*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20160629/9326d6c3/attachment-0001.html&gt;