GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — [Bug]: Slider.set_val renders the figure before it notifies its observers, sometimes causing two renders per event · Issue #32526 · matplotlib/matplotlib

[Bug]: Slider.set_val renders the figure before it notifies its observers, sometimes causing two renders per event #32526

New issue
Copy link
New issue
Copy link
Open
#32587
Open
[Bug]: Slider.set_val renders the figure before it notifies its observers, sometimes causing two renders per event#32526
#32587
Copy link
Labels
first-contributiontopic: widgets/UI

Description

@dpthurst
dpthurst
opened on Oct 23, 2026
Issue body actions

Bug summary

Slider.set_val renders the figure before it notifies its observers, so a callback that
updates other artists causes two renders per event and the first of them is drawn with the
previous data.

Code for reproduction

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider

fig, (ax, axsl) = plt.subplots(2, 1, height_ratios=[4, 1], figsize=(4, 3))
fig.subplots_adjust(bottom=0.25)
(line,) = ax.plot([0, 1], [0, 0])            # an artist the callback will update
sl = Slider(axsl, "x", 0.0, 1.0, valinit=0.0)

ndraw = [0]
_real_draw_idle = fig.canvas.draw_idle
def counting_draw_idle(*a, **k):
    ndraw[0] += 1
    return _real_draw_idle(*a, **k)
fig.canvas.draw_idle = counting_draw_idle

at_callback = []
def on_change(v):
    at_callback.append(ndraw[0])             # how many draws already happened
    line.set_ydata([v, v])
    fig.canvas.draw_idle()                   # the callback's own draw
sl.on_changed(on_change)

for drawon in (True, False):
    sl.drawon = drawon
    ndraw[0] = 0
    at_callback.clear()
    sl.set_val(0.7)
    print(f"drawon={drawon!s:<5}  draws before the callback ran: {at_callback[0]}"
          f"   draws in total: {ndraw[0]}")

Actual outcome

drawon=True draws before the callback ran: 1 draws in total: 2
drawon=False draws before the callback ran: 0 draws in total: 1

Expected outcome

One render per event, none of them before the observers run. Either set_val notifies first and draws afterwards, or it leaves drawing to the observers, in which case the docstring should say that callbacks are responsible for the draw_idle.

Additional information

The widget draws in Slider.set_val (lib/matplotlib/widgets.py): it updates the handle and value text, calls self.ax.get_figure(root=True).canvas.draw_idle() when self.drawon, and only then calls self._observers.process('changed', val).

The doubled render halves the interactive frame rate of any app built this way, and on a slower backend the stale frame is visible. In the app that prompted this report (four panels redrawn per slider event) one event cost 97 ms, 49 ms of it the wasted render; this was really obvious interactively.

drawon = False plus one draw_idle in the callback is the workaround and works, but it is not discoverable: drawon is not a constructor argument and the Slider docstring does not mention it. If changing the ordering risks compatibility, documenting the ordering and the workaround would already remove the trap.

This was discovered by a DeepSeek v4.1 Flash agent running on Openmath OS, but I verified it independently.

Operating system

Debian

Matplotlib Version

3.10.1+dfsg1

Matplotlib Backend

qtagg (or Agg as in the MWE)

Python version

Python 3.13.5

Jupyter version

N/A

Installation

Linux package manager

Activity

  1. github-actions
    added
    first-contribution
    on Oct 23, 2026
  2. iccir
    added
    topic: widgets/UI
    on Oct 23, 2026
  3. Rudra-G-23 commented on Nov 10, 2026

    @Rudra-G-23
    Rudra-G-23
    on Nov 10, 2026
    More actions

    I would like to contribute. I have few questions

    • A behavioral fix that avoids unnecessary rendering while preserving existing widget functionality.
    • Documentation explaining the drawon=False workaround.
  4. huzaifarahman13 commented on Nov 10, 2026

    @huzaifarahman13
    huzaifarahman13
    on Nov 10, 2026
    More actions

    I've opened #32587 for this. It moves the draw_idle() call after the observer notification in both Slider.set_val and RangeSlider.set_val, and adds a regression test that checks the order with drawon set to True and False. Happy to adjust based on feedback.

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

    first-contributiontopic: widgets/UI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.