Skip to content

handle repaint requests better it qtAgg #7366

Closed

Description

@tacaswell

To help us understand and resolve your issue please check that you have provided
the information below.

  • Matplotlib version, Python version and Platform (Windows, OSX, Linux ...)
    • v2.x, 3.5.2, linux
  • How did you install Matplotlib and Python (pip, anaconda, from source ...)
    • anaconda for deps, source for mpl
  • If possible please supply a Short, Self Contained, Correct, Example
    that demonstrates the issue i.e a small piece of code which reproduces the issue
    and can be run with out any other (or as few as possible) external dependencies.
    • run python examples/animation/strip_chart_demo.py with the qt backend
    • the x-axis does not refresh when it should, resizing or changing focus of the window forces it to update.
  • If this is a regression (Used to work in an earlier version of Matplotlib), please
    note where it used to work.
    • works correctly with tkagg backend

What I think is going on is:

  • on draw, the full figure is re-rendered to the mpl side buffer and update is called which (eventually) causes a QPaintEvent to be emitted which triggers the paintEvent method which copies data from the mpl side to the display buffers.
  • in animation with blitting on each frame the 'base' image is used as a base and just the artist(s) of interest are re-rendered, the bounding box to re-draw is added to canvas.blitbox and self.repaint(...) is called
  • in paintEvent we ignore the input object and (destructively) loop over canvas.blitbox to selectively update sub-regions of the displayed image.
  • in this example due to the event generator running as fast as it can, there is always an entry in canvas.blitbox, thus the full buffer never gets copied to the screen and we are left with incorrect labels on the x-axis (despite the mpl-side buffer being correct)
  • anything that slips a paint in between the animation events (ex changing focus, resizing) will 'fix' this temporarily.

The object which comes into paintEvent is a QPaintEvent object which has a region attached to it (http://doc.qt.io/qt-4.8/qpaintevent.html#region assume it is the same for qt5), we should be using that rather than our canvas.blitbox side-band. Each blit calls repaint which will in turn generate a QPaintEvent object (which will in turn fall through paintEvent) thus the loop over canvas.blitbox is redundant with many calls to this function.

attn @anntzer @mfitzp @pwuertz as people who were have been involved in the qt backend and the most recent re-factor.

Activity

  1. added this to the milestone on Oct 31, 2016
  2. anntzer commented on Oct 31, 2016

    @anntzer
    Contributor

    As you suggested, a patch as simple as

    diff --git a/lib/matplotlib/backends/backend_qt5agg.py b/lib/matplotlib/backends/backend_qt5agg.py
    index d9ab851..e359c69 100644
    --- a/lib/matplotlib/backends/backend_qt5agg.py
    +++ b/lib/matplotlib/backends/backend_qt5agg.py
    @@ -120,8 +120,12 @@ class FigureCanvasQTAggBase(object):
                 p = QtGui.QPainter(self)
    
                 while len(self.blitbox):
    -                bbox = self.blitbox.pop()
    -                l, b, r, t = bbox.extents
    +                l = e.rect().left()
    +                t = e.rect().bottom()
    +                r = e.rect().right()
    +                b = e.rect().top()
    +                from matplotlib.transforms import Bbox
    +                bbox = Bbox([[l, b], [r, t]])
                     w = int(r) - int(l)
                     h = int(t) - int(b)
                     t = int(b) + h
    

    does solve the issue.

    The question that remains is whether I have a way to know, while within paintEvent(), whether a normal repaint or a blitting paint was requested?

    A comparison with other backends gives the following:

    • tkagg seems to directly call the C-level blitting function in canvas.blit (without going through the event loop) so all is well.
    • gtk3agg seems to suffer from the same issue (it uses self._bbox_queue for the same purpose) but I can't test it :-) I currently get
    Traceback (most recent call last):
      File "/usr/lib/python3.5/site-packages/matplotlib/backends/backend_gtk3agg.py", line 69, in on_draw_event
        buf, cairo.FORMAT_ARGB32, width, height)
    NotImplementedError: Surface.create_for_data: Not Implemented yet.
    
  3. tacaswell commented on Oct 31, 2016

    @tacaswell
    MemberAuthor

    A better question is do we really care if it is a normal draw or a blit? In either case we are just moving values from the agg renderer to the qt screen buffer and I think that the two code paths are functionally equivalent (except the len == 0 case seems to deal with byte order better?) and the second one seems to either properly deal with / expose a pyside bug in reference counting.

    I strongly suggest the answer is we do not care and those code blocks could be unified.

    My dim recollection is that paintEvent used to call agg.draw() and that got cleaned up recently.

  4. anntzer commented on Oct 31, 2016

    @anntzer
    Contributor

    The main difference between normal draws and blits is that normal draws additionally call painter.eraseRect on the canvas first, whereas blits don't. I don't know at what level transparency is handled, and whether this can be an issue.

  5. modified the milestones: , on May 3, 2017
  6. added 2 commits that reference this issue on Jul 28, 2017
    ae281e2
    243d50d
  7. modified the milestones: , on Oct 9, 2017
  8. anntzer commented on Sep 14, 2018

    @anntzer
    Contributor

    Closed by #8951

  9. modified the milestones: needs sorting, v3.0 on Sep 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Difficulty: Mediumhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issuesGUI: Qt

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions