Well, this was a short joy; as of ipympl 0.8.4, the below hack does not work anymore: Getting back toolbar on left (if set to top position) in 0.8.4? · Issue #411 · matplotlib/ipympl - so the work I put in to get the hack working is wasted now. Such is the world - things never get better, and everything rots and goes to shit; just an endless waste of time ...
Well, I managed to do something - it basically comes from the realization, that the toolbar and the plot have their own separate divs, and then realizing (via How would you make two <div>s overlap?) that they can be placed on top of one another using CSS position: absolute
.
In any case, if there is a better solution, I'd love to hear it. Here is the code:
%matplotlib widget
import matplotlib.pyplot as plt
from ipywidgets import widgets, Layout
from IPython.display import display, HTML
# the below CSS style override in Jupyter, allows the div that hosts the Matplotlib toolbar, to be placed "on top" of the div that hosts the plot:
# widget-label does the same, but for the bottom "x/y" indicator (which is why its position needs to also be controlled with margin)
HTML("""
<style>
.jupyter-matplotlib-toolbar {
overflow: visible;
position: absolute;
z-index: 1000;
}
.widget-label {
color: var(--jp-widgets-label-color);
font-size: var(--jp-widgets-font-size);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: var(--jp-widgets-inline-height);
z-index: 1000;
margin-top: 465px;
margin-left: 291px;
}
</style>
""")
out_widget_plot = widgets.Output()
with out_widget_plot:
with plt.style.context("default"): # ggplot
fig, (ax1) = plt.subplots(1, 1)
fig.suptitle('test title', fontsize=10)
tline = ax1.plot([0,1,2], [10,20,30])
fig.tight_layout()
#fig.canvas.toolbar.button_style = 'info' # test: ok, works (but don't need it here)
#fig.canvas.toolbar.orientation = 'horizontal' # no error, but no effect either
fig.canvas.toolbar_position = 'top' # this also switches the orientation to horizontal, too
fig.canvas.header_visible = False # Gets rid of "Figure 1" on top
the_ui = widgets.VBox([ widgets.HTML("<b>Hello</b>"), out_widget_plot, widgets.HTML("<b>World</b>"),])
display(the_ui)
This is the output - note that you can right-click the plot once, and you will get that thin blue outline indicating the size of the plot element - and which ultimately shows, that the toolbar and the 'x/y' label are now on top of the plot element:

EDIT: In another code I've had, the above with HTML(""" ... """)
failed; so what worked for me there, was to output the HTML via widgets.HTML
, like this:
%matplotlib widget
import matplotlib.pyplot as plt
from ipywidgets import widgets, Layout
from IPython.display import display, HTML
out_widget_plot = widgets.Output()
with out_widget_plot:
with plt.style.context("default"): # ggplot
fig, (ax1) = plt.subplots(1, 1)
fig.suptitle('test title', fontsize=10)
tline = ax1.plot([0,1,2], [10,20,30])
fig.tight_layout()
#fig.canvas.toolbar.button_style = 'info' # test: ok, works (but don't need it here)
#fig.canvas.toolbar.orientation = 'horizontal' # no error, but no effect either
fig.canvas.toolbar_position = 'top' # this also switches the orientation to horizontal, too
fig.canvas.header_visible = False # Gets rid of "Figure 1" on top
# the below CSS style override in Jupyter, allows the div that hosts the Matplotlib toolbar, to be placed "on top" of the div that hosts the plot:
# widget-label does the same, but for the bottom "x/y" indicator (which is why its position needs to also be controlled with margin)
sthtml = """
<style>
.jupyter-matplotlib-toolbar {
overflow: visible;
position: absolute;
z-index: 1000;
}
.widget-label {
color: var(--jp-widgets-label-color);
font-size: var(--jp-widgets-font-size);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: var(--jp-widgets-inline-height);
z-index: 1000;
margin-top: -41px;
margin-left: 58px;
}
</style>
"""
the_ui = widgets.VBox([ widgets.HTML(sthtml), widgets.HTML("<b>Hello</b>"), out_widget_plot, widgets.HTML("<b>World</b>"),])
display(the_ui)