Skip to content

Make the toolbar customizable #270

Open

Description

@nvaytet

Hi,
Is it possible to add custom buttons to the toolbar?
Many thanks for any help.

Activity

  1. martinRenou commented on Oct 9, 2020

    @martinRenou
    Member

    Hi,

    It's not possible, although you could definitely use ipywidgets and create a Button and a container for both your button and your plot

  2. thomasaarholt commented on Oct 9, 2020

    @thomasaarholt
    Contributor

    This is something that would be quite nice to add - if it isn't that hard. We were talking about it over at the hyperspy repo in the last year.

  3. martinRenou commented on Oct 9, 2020

    @martinRenou
    Member

    It might not be too difficult I guess.

    We could actually redesign the current Toolbar as being a bunch of ipywidgets Buttons in a VBox, so that it's easier remove buttons or add new ones in Python.

  4. nvaytet commented on Oct 9, 2020

    @nvaytet
    Author

    Hi,

    It's not possible, although you could definitely use ipywidgets and create a Button and a container for both your button and your plot

    Thanks.
    I have managed to create a button, and put the toolbar and the button inside a VBox.

    import matplotlib.pyplot as plt
    import ipywidgets as ipw
    fig, ax = plt.subplots()
    new_toolbar = ipw.VBox([fig.canvas.toolbar, ipw.Button(icon='exchange', layout={"width": "34px"})])
    new_toolbar

    which gives me
    Screenshot at 2020-10-09 08-58-30
    So when I use the folding button, my new button is still there, but that's no big deal for me as I hardly ever use the fold button anyway.
    Screenshot at 2020-10-09 09-04-33

    Now my question is how can I display the new_toolbar, next to the plot?
    I was trying to put the new_toolbar and the plot inside a HBox but i can't seem to get the plot only (without the toolbar) as a widget. Any pointers?

    Thanks!

  5. nvaytet commented on Oct 9, 2020

    @nvaytet
    Author

    It might not be too difficult I guess.

    We could actually redesign the current Toolbar as being a bunch of ipywidgets Buttons in a VBox, so that it's easier remove buttons or add new ones in Python.

    That would be really nice! Would make it super easy to customize, and maybe rearrange (move buttons around into different containers, etc..)

  6. martinRenou commented on Oct 9, 2020

    @martinRenou
    Member

    Now my question is how can I display the new_toolbar, next to the plot?
    I was trying to put the new_toolbar and the plot inside a HBox but i can't seem to get the plot only (without the toolbar) as a widget. Any pointers?

    There might not be a clean way of doing it in the current state of ipympl. But you might be able to hack around it by setting the plot's canvas.toolbar_visible flag to False, then put your extended toolbar inside an HBox with your plot.

  7. nvaytet commented on Oct 9, 2020

    @nvaytet
    Author

    @martinRenou that's a great idea!
    I agree it's not super clean, but it's at least very simple and works:
    Screenshot at 2020-10-09 09-54-05
    Thanks!

  8. ianhi commented on Oct 9, 2020

    @ianhi
    Collaborator

    We could actually redesign the current Toolbar as being a bunch of ipywidgets Buttons in a VBox, so that it's easier remove buttons or add new ones in Python.

    👍 to this. It also may make sense to keep in mind that matplotlib does have methods for modifying the toolbar. https://matplotlib.org/3.3.0/gallery/user_interfaces/toolmanager_sgskip.html

  9. nvaytet commented on Oct 9, 2020

    @nvaytet
    Author

    Yes I tried those methods but the functionality is not available with ipympl. The api is not exactly the same. And using the ipywidgets api seems much cleaner and makes more sense when the canvas is itself a widget.

  10. changed the title [-]How to edit/customize the toolbar?[/-] [+]Make the toolbar customizable[/+] on Oct 12, 2020
  11. nvaytet commented on Oct 29, 2020

    @nvaytet
    Author

    In case you want to remove buttons from the toolbar, it seems that you can do this by changing the list of toolitems:

    import matplotlib.pyplot as plt
    
    %matplotlib widget
    
    fig, ax = plt.subplots()
    items = fig.canvas.toolbar.toolitems
    new_tools = [items[0], items[3], items[4], items[5]]
    fig.canvas.toolbar.toolitems = new_tools
    
    fig.canvas

    yields
    Screenshot at 2020-10-29 14-33-57

    So in combination with my workaround above, i'm able to almost completely customize my toolbar.

  12. martinRenou commented on Oct 29, 2020

    @martinRenou
    Member

    Yes indeed, I forgot about this :) This is actually Matplotlib API if I am correct.

  13. nvaytet commented on Dec 2, 2020

    @nvaytet
    Author

    In addition, I have another workaround if you wish to customize either the appearance of the buttons in the original mpl toolbar, or their behaviour.

    I can replace them with custom icons by just hiding the toolbar entirely, and add for example a custom "Home" button with any icon I want, and inside my callback I can call the original matplotlib fig.canvas.toolbar.home() function, and any other additional methods of my choosing.

    The other methods are zoom(), pan(), save_figure() etc...

  14. scimax commented on Jul 9, 2024

    @scimax

    @martinRenou that's a great idea! I agree it's not super clean, but it's at least very simple and works: Screenshot at 2020-10-09 09-54-05 Thanks!

    Many thanks for the nice idea! Unfortunately, I think it does not work like this anymore. If the toolbar is toogled invisible, it stays invisible when adding it to the VBox. So it's not possible to add a Button to the toolbar like that, only next to the actual toolbar.

    So this is the result:

    import matplotlib.pyplot as plt
    import ipywidgets as ipw
    fig, ax = plt.subplots()
    ax.plot(np.random.rand(100))
    plt.ioff()
    fig.canvas.toolbar_visible = False
    
    new_toolbar = ipw.VBox([fig.canvas.toolbar, ipw.Button(icon='clipboard', layout={"width": "34px"})])
    ipw.HBox([new_toolbar, fig.canvas])
    image
  15. N-Coder commented on Jul 22, 2024

    @N-Coder

    The following works for me, extending the "native" toolbar instead of creating a new one:

    def update_graph(*args, **kwargs):
        # do some stuff with the figure and call draw_idle()
    
    fig.canvas.toolbar.update_graph = update_graph
    fig.canvas.toolbar.toolitems = [*fig.canvas.toolbar.toolitems,
                                    ("Update", "Update the Graph", "refresh", "update_graph")]
    fig.canvas.toolbar_visible = 'visible'

    image

    pip freeze
    anyio==4.4.0
    argon2-cffi==23.1.0
    argon2-cffi-bindings==21.2.0
    arrow==1.3.0
    asttokens==2.4.1
    async-lru==2.0.4
    attrs==23.2.0
    Babel==2.15.0
    beautifulsoup4==4.12.3
    bleach==6.1.0
    certifi==2024.6.2
    cffi==1.16.0
    charset-normalizer==3.3.2
    comm==0.2.2
    contourpy==1.2.1
    cppyy==3.1.2
    cppyy-backend==1.15.2
    cppyy-cling==6.30.0
    CPyCppyy==1.12.16
    cycler==0.12.1
    debugpy==1.8.2
    decorator==5.1.1
    defusedxml==0.7.1
    executing==2.0.1
    fastjsonschema==2.20.0
    fonttools==4.53.0
    fqdn==1.5.1
    h11==0.14.0
    httpcore==1.0.5
    httpx==0.27.0
    idna==3.7
    importlib_resources==6.4.0
    ipykernel==6.29.5
    ipympl==0.9.4
    ipython==8.26.0
    ipython-genutils==0.2.0
    ipywidgets==8.1.3
    isoduration==20.11.0
    jedi==0.19.1
    Jinja2==3.1.4
    json5==0.9.25
    jsonpointer==3.0.0
    jsonschema==4.22.0
    jsonschema-specifications==2023.12.1
    jupyter-events==0.10.0
    jupyter-lsp==2.2.5
    jupyter_client==8.6.2
    jupyter_core==5.7.2
    jupyter_server==2.14.1
    jupyter_server_terminals==0.5.3
    jupyterlab==4.2.3
    jupyterlab_pygments==0.3.0
    jupyterlab_server==2.27.2
    jupyterlab_widgets==3.0.11
    kiwisolver==1.4.5
    MarkupSafe==2.1.5
    matplotlib==3.9.0
    matplotlib-inline==0.1.7
    mistune==3.0.2
    nbclient==0.10.0
    nbconvert==7.16.4
    nbformat==5.10.4
    nest-asyncio==1.6.0
    notebook_shim==0.2.4
    numpy==2.0.0
    -e git+ssh://git@github.com/N-Coder/ogdf-python.git@4ec1d9397a7c92de033265d636b032fc493ca3e4#egg=ogdf_python
    overrides==7.7.0
    packaging==24.1
    pandas==2.2.2
    pandocfilters==1.5.1
    parso==0.8.4
    pexpect==4.9.0
    pillow==10.4.0
    platformdirs==4.2.2
    prometheus_client==0.20.0
    prompt_toolkit==3.0.47
    psutil==6.0.0
    ptyprocess==0.7.0
    pure-eval==0.2.2
    pycparser==2.22
    Pygments==2.18.0
    pyparsing==3.1.2
    python-dateutil==2.9.0.post0
    python-json-logger==2.0.7
    pytz==2024.1
    PyYAML==6.0.1
    pyzmq==26.0.3
    referencing==0.35.1
    requests==2.32.3
    rfc3339-validator==0.1.4
    rfc3986-validator==0.1.1
    rpds-py==0.18.1
    seaborn==0.13.2
    Send2Trash==1.8.3
    setuptools==70.2.0
    sh==2.0.7
    six==1.16.0
    sniffio==1.3.1
    soupsieve==2.5
    stack-data==0.6.3
    terminado==0.18.1
    tinycss2==1.3.0
    tornado==6.4.1
    traitlets==5.14.3
    types-python-dateutil==2.9.0.20240316
    tzdata==2024.1
    uri-template==1.3.0
    urllib3==2.2.2
    wcwidth==0.2.13
    webcolors==24.6.0
    webencodings==0.5.1
    websocket-client==1.8.0
    widgetsnbextension==4.0.11
    
  16. MatthieuLJ commented on Jan 9, 2026

    @MatthieuLJ

    All the information here was very helpful for me to customize the toolbar. I needed to do a few more things like having a button that behaves more like pan & zoom (creates a new state), etc...

    So if that's helpful to anyone, here is my code: https://gist.github.com/MatthieuLJ/98798c4d641b6d1979570962617765c8

    (Comments are also much welcome if I'm doing something wrong or missing anything)

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions