Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: Issue with blitting of PyQt autoscaled figure #5135

Closed
soupault opened this issue Sep 24, 2015 · 5 comments
Closed

BUG: Issue with blitting of PyQt autoscaled figure #5135

soupault opened this issue Sep 24, 2015 · 5 comments

Comments

@soupault
Copy link
Contributor

Hi!
I've faced following issue in my GUI-app with real-time data visualization.

I've created simple app with QWidget (with fixed size policy!) (for matplotlib plot) and QButton (to refresh the plot).

On creation, layout and figure occupy all available space within QWidget. Everything looks correct: I'm able to use different QAlignments, create several plots inside a single layout, etc.

But, during the blitting, it seems like that copy_from_bbox returns unscaled coordinates and background is being re-drawn incorrectly. At the same time, updated data points keep being updated on their correct positions (on x-axis), but also are being accumulated on old autoscaled background layer.

Here are some examples:

  1. Application started:
    1

  2. Clicked button once:
    2

You might see here, that grids have different steps.

  1. Clicked several times:
    3

Issue is also described at
http://stackoverflow.com/questions/32698501/fast-redrawing-with-pyqt-and-matplotlib .

As mentioned there, the issue happens both in Win7 and Linux, with both PyQt4 and PyQt5.

Using Python 3.4, latest stable matplotlib, numpy, etc.

Issue is, probably, related to #916 .

@soupault
Copy link
Contributor Author

To reproduce:

@tacaswell
Copy link
Member

Can you try with 1.5.0rc1 (conda install able from from the conda-forge channel)? I think we have fixed this on master, the issue was with resize events not properly firing.

Can this be reproduced without a custom embedding?

@soupault
Copy link
Contributor Author

@tacaswell What do you mean by 'w/o custom embedding'? 'plot/scatter/etc' <-> 'clear'?

Oh... It took me 2 weeks to find this: http://matplotlib.org/1.3.1/examples/old_animation/animation_blit_qt4.html . Using this approach everything works fine.

I'll check rc1 and update you later.

@tacaswell
Copy link
Member

re embedding, yes, using the embedding we ship. It helps to convince us (well, me) that it is our problem, not your problem 👿 .

Both of your pastebin links are dead.

@tacaswell
Copy link
Member

Re-using my SO answer


The problem is that you are grabbing the background at some point before it is fully re sized etc to what is going to end up on the screen. What you need to do is

  • use draw_idle to let Qt decide when to actually trigger the full redraw
  • use the mpl event stack to grab the background after that draw

Just the relevant methods:

        def prepare_figure(self):
            self.fig = Figure()
            self.axes = self.fig.add_subplot(111)

            self.axes.set_xlim((0, 6))
            self.axes.set_xlabel('seq.index')
            self.axes.set_ylim((0, 100))
            self.axes.set_ylabel('observation')

            N = 5
            self.graph = self.axes.scatter(
                [] * N, [] * N, s=[np.pi*5**2] * N,
                color='seagreen', animated=True)

            self.canvas = FigureCanvas(self.fig)
            # w = self.widget_mpl.width()
            # h = self.widget_mpl.height()
            # self.canvas.setFixedSize(w, h)

            self.layout_mpl.addWidget(self.canvas)

            # hook up to mpls event handling framework for draw events
            # this is emitted after the canvas has finished a full redraw
            self.canvas.mpl_connect('draw_event', self._draw_event)

            # ask the canvas to kindly draw it self some time in the future
            # when Qt thinks it is convenient
            self.canvas.draw_idle()

        def _draw_event(self, evt):
            # after drawing, grab the new background
            self.bg = self.canvas.copy_from_bbox(self.axes.bbox)

Have a look at the blitting code in the animation module for a full set of the functions you need to make this work (there is also a re-size event) and after a force re-draw you will need to re-blit what ever the last data was.

Closing as this isn't a bug, (but it does need to be better documented).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants