Skip to content
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

Make the toolbar customizable #270

Open
nvaytet opened this issue Oct 8, 2020 · 14 comments
Open

Make the toolbar customizable #270

nvaytet opened this issue Oct 8, 2020 · 14 comments

Comments

@nvaytet
Copy link

nvaytet commented Oct 8, 2020

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

@martinRenou
Copy link
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

@thomasaarholt
Copy link
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.

@martinRenou
Copy link
Member

martinRenou commented Oct 9, 2020

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.

@nvaytet
Copy link
Author

nvaytet commented Oct 9, 2020

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!

@nvaytet
Copy link
Author

nvaytet commented Oct 9, 2020

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..)

@martinRenou
Copy link
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.

@nvaytet
Copy link
Author

nvaytet commented Oct 9, 2020

@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!

@ianhi
Copy link
Collaborator

ianhi commented Oct 9, 2020

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

@nvaytet
Copy link
Author

nvaytet commented Oct 9, 2020

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.

@martinRenou martinRenou changed the title How to edit/customize the toolbar? Make the toolbar customizable Oct 12, 2020
@nvaytet
Copy link
Author

nvaytet commented Oct 29, 2020

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.

@martinRenou
Copy link
Member

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

@nvaytet
Copy link
Author

nvaytet commented Dec 2, 2020

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...

@scimax
Copy link

scimax commented Jul 9, 2024

@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

@N-Coder
Copy link

N-Coder commented Jul 22, 2024

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

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

No branches or pull requests

6 participants