Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1df4a99

Browse files
committed
Add blitting to Slider widgets
1 parent 72cee0a commit 1df4a99

File tree

1 file changed

+85
-20
lines changed

1 file changed

+85
-20
lines changed

lib/matplotlib/widgets.py

Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ class SliderBase(AxesWidget):
232232
For the slider to remain responsive you must maintain a reference to it.
233233
"""
234234
def __init__(self, ax, orientation, closedmin, closedmax,
235-
valmin, valmax, valfmt, dragging, valstep):
235+
valmin, valmax, valfmt, dragging, valstep, useblit):
236236
if ax.name == '3d':
237237
raise ValueError('Sliders cannot be added to 3D Axes')
238238

@@ -265,6 +265,11 @@ def __init__(self, ax, orientation, closedmin, closedmax,
265265
ax.set_axis_off()
266266
ax.set_navigate(False)
267267

268+
self._useblit = useblit and self.canvas.supports_blit
269+
if self._useblit:
270+
self._background = None
271+
self.connect_event("draw_event", self._clear)
272+
268273
self.connect_event("button_press_event", self._update)
269274
self.connect_event("button_release_event", self._update)
270275
if dragging:
@@ -301,6 +306,18 @@ def reset(self):
301306
if np.any(self.val != self.valinit):
302307
self.set_val(self.valinit)
303308

309+
def _blit_draw(self, artists):
310+
if not self.drawon:
311+
return
312+
if not self._useblit or self._background is None:
313+
self.ax.figure.canvas.draw_idle()
314+
return
315+
316+
self.canvas.restore_region(self._background)
317+
for a in artists:
318+
self.ax.draw_artist(a)
319+
self.canvas.blit(self.ax.get_tightbbox())
320+
304321

305322
class Slider(SliderBase):
306323
"""
@@ -320,7 +337,8 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
320337
closedmin=True, closedmax=True, slidermin=None,
321338
slidermax=None, dragging=True, valstep=None,
322339
orientation='horizontal', *, initcolor='r',
323-
track_color='lightgrey', handle_style=None, **kwargs):
340+
track_color='lightgrey', handle_style=None, useblit=False,
341+
**kwargs):
324342
"""
325343
Parameters
326344
----------
@@ -390,6 +408,13 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
390408
`~.Line2D` constructor. e.g. ``handle_style = {'style'='x'}`` will
391409
result in ``markerstyle = 'x'``.
392410
411+
useblit : bool, default: False
412+
Use blitting for faster drawing if supported by the backend.
413+
See the tutorial :doc:`/tutorials/advanced/blitting` for details.
414+
415+
If you enable blitting, you should set *valfmt* to a fixed-width
416+
format, or there may be artifacts if the text changes width.
417+
393418
Notes
394419
-----
395420
Additional kwargs are passed on to ``self.poly`` which is the
@@ -398,7 +423,7 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
398423
``edgecolor``, ``alpha``, etc.).
399424
"""
400425
super().__init__(ax, orientation, closedmin, closedmax,
401-
valmin, valmax, valfmt, dragging, valstep)
426+
valmin, valmax, valfmt, dragging, valstep, useblit)
402427

403428
if slidermin is not None and not hasattr(slidermin, 'val'):
404429
raise ValueError(
@@ -419,6 +444,7 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
419444
marker_props = {
420445
f'marker{k}': v for k, v in {**defaults, **handle_style}.items()
421446
}
447+
animated_style = {'animated': True} if self._useblit else {}
422448

423449
if orientation == 'vertical':
424450
self.track = Rectangle(
@@ -427,11 +453,14 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
427453
facecolor=track_color
428454
)
429455
ax.add_patch(self.track)
430-
self.poly = ax.axhspan(valmin, valinit, .25, .75, **kwargs)
456+
self.poly = ax.axhspan(valmin, valinit, .25, .75, **animated_style,
457+
**kwargs)
431458
# Drawing a longer line and clipping it to the track avoids
432459
# pixelation-related asymmetries.
433-
self.hline = ax.axhline(valinit, 0, 1, color=initcolor, lw=1,
434-
clip_path=TransformedPatchPath(self.track))
460+
self._line = self.hline = ax.axhline(
461+
valinit, 0, 1, color=initcolor, lw=1,
462+
clip_path=TransformedPatchPath(self.track),
463+
**animated_style)
435464
handleXY = [[0.5], [valinit]]
436465
else:
437466
self.track = Rectangle(
@@ -440,15 +469,19 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
440469
facecolor=track_color
441470
)
442471
ax.add_patch(self.track)
443-
self.poly = ax.axvspan(valmin, valinit, .25, .75, **kwargs)
444-
self.vline = ax.axvline(valinit, 0, 1, color=initcolor, lw=1,
445-
clip_path=TransformedPatchPath(self.track))
472+
self.poly = ax.axvspan(valmin, valinit, .25, .75, **animated_style,
473+
**kwargs)
474+
self._line = self.vline = ax.axvline(
475+
valinit, 0, 1, color=initcolor, lw=1,
476+
clip_path=TransformedPatchPath(self.track),
477+
**animated_style)
446478
handleXY = [[valinit], [0.5]]
447479
self._handle, = ax.plot(
448480
*handleXY,
449481
"o",
450482
**marker_props,
451-
clip_on=False
483+
clip_on=False,
484+
**animated_style
452485
)
453486

454487
if orientation == 'vertical':
@@ -459,7 +492,8 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
459492
self.valtext = ax.text(0.5, -0.02, self._format(valinit),
460493
transform=ax.transAxes,
461494
verticalalignment='top',
462-
horizontalalignment='center')
495+
horizontalalignment='center',
496+
**animated_style)
463497
else:
464498
self.label = ax.text(-0.02, 0.5, label, transform=ax.transAxes,
465499
verticalalignment='center',
@@ -468,10 +502,21 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
468502
self.valtext = ax.text(1.02, 0.5, self._format(valinit),
469503
transform=ax.transAxes,
470504
verticalalignment='center',
471-
horizontalalignment='left')
505+
horizontalalignment='left',
506+
**animated_style)
472507

473508
self.set_val(valinit)
474509

510+
def _clear(self, event):
511+
"""Internal event handler to refresh the blitting background."""
512+
if self.ignore(event):
513+
return
514+
area = self.ax.get_tightbbox()
515+
self._background = self.canvas.copy_from_bbox(area)
516+
for a in artists:
517+
self.ax.draw_artist(a)
518+
self.ax.figure.canvas.blit(area)
519+
475520
def _value_in_bounds(self, val):
476521
"""Makes sure *val* is with given bounds."""
477522
val = self._stepped_value(val)
@@ -549,8 +594,7 @@ def set_val(self, val):
549594
self._handle.set_xdata([val])
550595
self.poly.xy = xy
551596
self.valtext.set_text(self._format(val))
552-
if self.drawon:
553-
self.ax.figure.canvas.draw_idle()
597+
self._blit_draw([self.poly, self._line, self.valtext, self._handle])
554598
self.val = val
555599
if self.eventson:
556600
self._observers.process('changed', val)
@@ -603,6 +647,7 @@ def __init__(
603647
orientation="horizontal",
604648
track_color='lightgrey',
605649
handle_style=None,
650+
useblit=False,
606651
**kwargs,
607652
):
608653
"""
@@ -662,6 +707,13 @@ def __init__(
662707
`~.Line2D` constructor. e.g. ``handle_style = {'style'='x'}`` will
663708
result in ``markerstyle = 'x'``.
664709
710+
useblit : bool, default: False
711+
Use blitting for faster drawing if supported by the backend.
712+
See the tutorial :doc:`/tutorials/advanced/blitting` for details.
713+
714+
If you enable blitting, you should set *valfmt* to a fixed-width
715+
format, or there may be artifacts if the text changes width.
716+
665717
Notes
666718
-----
667719
Additional kwargs are passed on to ``self.poly`` which is the
@@ -670,7 +722,7 @@ def __init__(
670722
``edgecolor``, ``alpha``, etc.).
671723
"""
672724
super().__init__(ax, orientation, closedmin, closedmax,
673-
valmin, valmax, valfmt, dragging, valstep)
725+
valmin, valmax, valfmt, dragging, valstep, useblit)
674726

675727
# Set a value to allow _value_in_bounds() to work.
676728
self.val = [valmin, valmax]
@@ -689,6 +741,7 @@ def __init__(
689741
marker_props = {
690742
f'marker{k}': v for k, v in {**defaults, **handle_style}.items()
691743
}
744+
animated_style = {'animated': True} if self._useblit else {}
692745

693746
if orientation == "vertical":
694747
self.track = Rectangle(
@@ -710,7 +763,7 @@ def __init__(
710763
poly_transform = self.ax.get_xaxis_transform(which="grid")
711764
handleXY_1 = [valinit[0], .5]
712765
handleXY_2 = [valinit[1], .5]
713-
self.poly = Polygon(np.zeros([5, 2]), **kwargs)
766+
self.poly = Polygon(np.zeros([5, 2]), **animated_style, **kwargs)
714767
self._update_selection_poly(*valinit)
715768
self.poly.set_transform(poly_transform)
716769
self.poly.get_path()._interpolation_steps = 100
@@ -721,13 +774,15 @@ def __init__(
721774
*handleXY_1,
722775
"o",
723776
**marker_props,
724-
clip_on=False
777+
clip_on=False,
778+
**animated_style
725779
)[0],
726780
ax.plot(
727781
*handleXY_2,
728782
"o",
729783
**marker_props,
730-
clip_on=False
784+
clip_on=False,
785+
**animated_style
731786
)[0]
732787
]
733788

@@ -748,6 +803,7 @@ def __init__(
748803
transform=ax.transAxes,
749804
verticalalignment="top",
750805
horizontalalignment="center",
806+
**animated_style
751807
)
752808
else:
753809
self.label = ax.text(
@@ -766,11 +822,21 @@ def __init__(
766822
transform=ax.transAxes,
767823
verticalalignment="center",
768824
horizontalalignment="left",
825+
**animated_style
769826
)
770827

771828
self._active_handle = None
772829
self.set_val(valinit)
773830

831+
def _clear(self, event):
832+
"""Internal event handler to refresh the blitting background."""
833+
if self.ignore(event):
834+
return
835+
self._background = self.canvas.copy_from_bbox(self.ax.get_tightbbox())
836+
for a in artists:
837+
self.ax.draw_artist(a)
838+
self.canvas.blit(area)
839+
774840
def _update_selection_poly(self, vmin, vmax):
775841
"""
776842
Update the vertices of the *self.poly* slider in-place
@@ -931,8 +997,7 @@ def set_val(self, val):
931997

932998
self.valtext.set_text(self._format((vmin, vmax)))
933999

934-
if self.drawon:
935-
self.ax.figure.canvas.draw_idle()
1000+
self._blit_draw([self.poly, self.valtext, *self._handles])
9361001
self.val = (vmin, vmax)
9371002
if self.eventson:
9381003
self._observers.process("changed", (vmin, vmax))

0 commit comments

Comments
 (0)