8000 Redrawing figure canvas after draw_event fails · Issue #10334 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content
Redrawing figure canvas after draw_event fails #10334
Closed as not planned
Closed as not planned
@ImportanceOfBeingErnest

Description

It seems that redrawing a figure inside a callback function connected to a "draw_event" does not work as intended. This issue would occur when a callback function tries to call fig.canvas.draw_idle() .
Sketch of the setup:

def update(evt):
    if some_condition:
        # change something in the figure
        fig.canvas.draw_idle()
        # the figure is not redrawn

fig.canvas.mpl_connect('draw_event', update)

This issue is reproduced with python 2.7, matplotlib 2.1.2 on windows 8.1 using the Qt4Agg as well as the TkAgg backend.

Here is a full working example. This example is expected to draw a scatter point of the same size as a box in data coordinates. On draw_events, which happen e.g. by zooming the axes, the scatter size should update to match the box' extention.

import matplotlib
matplotlib.use("Qt4Agg")
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(figsize=(3,2.2))

class scatter():
    def __init__(self,x,y,ax,size=1,**kwargs):
        self.n = len(x)
        self.ax = ax
        self.ax.figure.canvas.draw()
        self.size_data=size
        self.size = size
        self.sc = ax.scatter(x,y,s=self.size,**kwargs)
        self._resize()
        self.cid = ax.figure.canvas.mpl_connect('draw_event', self._resize)

    def _resize(self,event=None):
        print("draw_event")
        ppd=72./self.ax.figure.dpi
        trans = self.ax.transData.transform
        s =  ((trans((1,self.size_data))-trans((0,0)))*ppd)[1]
        if s != self.size:
            self.sc.set_sizes(s**2*np.ones(self.n))          
            self.ax.figure.canvas.draw_idle()
            self.size = s

ax.axis([0,1,0,1])
sc = scatter([0.5],[0.5],ax, size=.2, linewidth=0)
ax.plot([.4,.6,.6,.4,.4],[.4,.4,.6,.6,.4], color="k")
ax.set_aspect(1)
plt.show()

The actual outcome of this is that when zooming, the scatter size is updated, but the figure is not redrawn, such that the scatter keeps its previous size on screen. One needs to explicitely trigger another draw event (e.g. by clicking inside the axes) for the changes to be reflected in the figure.

draw_event

Workaround:
As a workaround one may explicitely draw the axes again inside the updating funtion.

if s != self.size:
            self.sc.set_sizes(s**2*np.ones(self.n)) 
            self.ax.draw_artist(self.ax)                  # <----  draw axes explicitely here
            self.ax.figure.canvas.draw_idle()
            self.size = s

However, this results in the axes being drawn twice. This at least seems so, because the axis labels are now bolder than before, suggesting that they are overlayed by a previous version of them. Again after clicking (and hence drawing the figure again), the labels appear in their normal typesetting.

draw_event2

The question would be: Why is fig.canvas.draw_idle() ignored? Is this a wrong expectation from my side? Is there something in the code that explicitely forbidds a draw_event beeing triggered from a draw_event callback?

Note that according to some discussion below a stackoverflow question it seems at least one person cannot reproduce the reported behaviour, so it may well be an issue restricted to python 2 or windows or some other unknown parameter.

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: closed as inactiveIssues closed by the "Stale" Github Action. Please comment on any you think should still be open.status: inactiveMarked by the “Stale” Github Action

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0