Skip to content

Add scroll capture functionality to WebAgg backend #30403

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

ianhi
Copy link
Contributor

@ianhi ianhi commented Aug 7, 2025

PR summary

Closes: #26032
Adds a mechanism to set whether a figure should preventDefault on scroll events. This is pretty niche, but crucial to have when you do need it. I copied the approach from the equivalent in ipympl, but used the get/set api here to be consistent with the backend conventions.

Testing
I'm not sure that there is a good place to test this, I don't see any infrastructure for end to end javascript tests of the webagg backend. I did manually test this with the below script

🤖 I guided claude through making some of the changes, then manually cleaned up docstrings and other parts. Same deal for the user test script below.

User Test Script
import matplotlib

matplotlib.use("webagg")

import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import numpy as np


def test_scroll_capture():
    """Create a test plot for testing scroll functionality with a longer page."""
    # Create a taller figure to make scrolling necessary
    fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 12))

    # Create some sample data
    x = np.linspace(0, 10, 100)
    y1 = np.sin(x)
    y2 = np.cos(x)
    y3 = np.tan(x / 2)

    # First subplot
    ax1.plot(x, y1, label="sin(x)", linewidth=2, color="blue")
    ax1.set_xlabel("X")
    ax1.set_ylabel("sin(x)")
    ax1.set_title("Test Plot for Scroll Capture Functionality - Part 1")
    ax1.legend()
    ax1.grid(True, alpha=0.3)

    # Second subplot
    ax2.plot(x, y2, label="cos(x)", linewidth=2, color="red")
    ax2.set_xlabel("X")
    ax2.set_ylabel("cos(x)")
    ax2.set_title("Part 2 - This plot should be tall enough to require scrolling")
    ax2.legend()
    ax2.grid(True, alpha=0.3)

    # Third subplot
    ax3.plot(x, y3, label="tan(x/2)", linewidth=2, color="green")
    ax3.set_xlabel("X")
    ax3.set_ylabel("tan(x/2)")
    ax3.set_title("Part 3 - Try scrolling over the plot area!")
    ax3.legend()
    ax3.grid(True, alpha=0.3)
    ax3.set_ylim(-3, 3)  # Limit y range to avoid extreme tan values

    plt.tight_layout()

    fig.canvas.set_capture_scroll(True)

    # Add toggle button
    button_ax = plt.axes([0.4, 0.81, 0.2, 0.04])
    toggle_button = Button(
        button_ax, "Disable Capture", color="lightgreen", hovercolor="green"
    )

    def toggle_scroll_capture(event):
        current_state = fig.canvas.get_capture_scroll()
        new_state = not current_state
        fig.canvas.set_capture_scroll(new_state)

        if new_state:
            toggle_button.label.set_text("Disable Capture")
            toggle_button.color = "lightgreen"
            toggle_button.hovercolor = "green"
        else:
            toggle_button.label.set_text("Enable Capture")
            toggle_button.color = "lightcoral"
            toggle_button.hovercolor = "coral"
        fig.canvas.draw_idle()

    toggle_button.on_clicked(toggle_scroll_capture)

    plt.show()


if __name__ == "__main__":
    test_scroll_capture()

PR checklist

@tacaswell tacaswell added this to the v3.11.0 milestone Aug 8, 2025
@tacaswell
Copy link
Member

I'm a bit torn. It is odd to put this sort of thing on the canvas, but given that this is the only backend we ship that this makes any sense for promoting it to the manager or figure seems worse.

Overall I lean to merging (modulo the style fix).

I don't think we have any tests of the js, but @QuLogic may know better. If we don't have tests, asking to add a test framework is overkill for this feature.

@ianhi
Copy link
Contributor Author

ianhi commented Aug 8, 2025

The closest thing to a test is probably this user automated test: https://github.com/matplotlib/matplotlib/blob/main/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb

but that tests nbagg, not webagg. But maybe nbagg is based on webagg? Although does nbagg still exist?

We could also transformat that notebook UAT into a webagg page for user testing.

docs I realize I should also add a brief mention of this to: https://matplotlib.org/stable/gallery/user_interfaces/embedding_webagg_sgskip.html

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

Successfully merging this pull request may close these issues.

[ENH]: Allow for capturing scroll in webagg backend
2 participants