I'm not so much experienced with Python and Matplotlib and ipywidgets. I'm trying to build a jupyter interactive notebook that shows a map and then overplots different marine isobtahs (lines of constant depths). I would like to show/hide some lines with the help of an array of checkboxes. The problem is trying to redraw the map once some lines are selected. So the strategy is
schematically:
%matplotlib inline
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from ipywidgets import *
from IPython.display import display
load_map() # loads isobaths from files (arrays of lat,lon pairs)
def draw_mapa(): #draw the map
mapa = Basemap()
mapa.drawcoastlines()
def draw_level(level): #draw a specific line (z=level)
mapa.plot(x,y)
def on_level_change(change): #handler of checkboxes
draw_mapa()
if button is True:
draw_level(level)
display(container) #container with all the checkboxes
#attaching handler to checkbox through .observe() method
for ilevel in checkboxes:
ilevel.observe(on_level_change,names='value')
Everything is ok except when I start to interact with the checkboxes. Each time I select/deselect a box a new additonal map with the corrected isobaths appears.
So the question is how is the way to "overplot" correctly in one figure avoiding creating new instances each time I check a box.
Thanks
Related
I wish to have an interactive map that you can click where, once clicked, a SkewT and Hodograph will be plotted showing the information for that location. I have thus created a class where I add all the necessary informations using the metpy library and I am able to successfully create these graphs:
SkewT and Hodograph plotted
The problem comes when I'm trying to import the classes I've created to generate these plots into jupyterlab. Since the code to actually make these plots is quite cumbersome, I'd rather
keep the code in a separate file and import my SoundingGraphs class, but it's not working. The graphs never get plotted inside a cell, they instead appear in the logs as a Warning and as an Info and I have no idea why:
Graphs appearing inside logs
Tried to use plt.show() inside my file, tried returning plt to then use plt.show() inside a cell of the notebook, tried using %matplotlib widget, %matplotlib notebook and %matplotlib inline, tried changing jupyterlab versions, none of these changed anything.
I have found one solution that I disliked, but that does work, which is rather than doing a plt.show(), to instead do this inside my class:
buffer = BytesIO()
plt.savefig(buffer, format='png')
return buffer
And in the notebook I would do:
image = Image()
display(image)
def on_generate_button_clicked(b):
buffer = SoundingGraphs(infos)
buffer.seek(0)
image.value=buffer.read()
image.format='png'
generate_button.on_click(on_generate_button_clicked)
I don't quite like this approach because further down the line I would like to add interactivity to my plots, like show values of plot when hovered and things like that, thus I don't just want to show an image. So I'd like to know if it is indeed possible to plt.show() a plot created inside another file in a cell.
Using:
Python 3.6.9
jupyterlab==3.2.9
jupyterlab-pygments==0.1.2
jupyterlab-server==2.10.3
jupyterlab-widgets==1.1.0
ipykernel==5.5.6
ipyleaflet==0.14.0
ipympl==0.8.8
ipython==7.16.3
ipython-genutils==0.2.0
ipywidgets==7.7.0
matplotlib==3.3.4
Thanks!
Yes, it is possible after all!
%matplotlib widget needs to be used at the start of the notebook and since the class method will be called from another function (on a button.on_click event), it is possible to use the #out.capture() decorator above it so that the plt.show() gets displayed. It's also possible to make the figure a class attribute to be able to have more control.
So here's a bit of working code if someone would like to replicate:
Notebook
%matplotlib widget
from ipywidgets import Button, Output
from myfile import MyClass
out = Output()
example_button = Button(
description='Example',
disabled=False,
button_style='',
tooltip='Click me'
)
#out.capture()
def on_example_button_clicked(b):
example_button.disabled = True
myclass = MyClass()
myclass.create_plot()
out.clear_output(wait=True)
display(myclass.fig.canvas)
example_button.disabled = False
example_button.on_click(on_example_button_clicked)
display(example_button)
display(out)
myfile.py
import matplotlib.pyplot as plt
class MyClass():
def __init__(self):
plt.ioff() # otherwise it'll also show inside logs
plt.clf()
self.fig = plt.figure()
def create_plot(self):
plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
Using Python 3.6+, Jupyter notebooks and matplotlib, I want
to find out how to get the x,y position of an image by
moving the mouse over it and/or clicking the position
I'm using any image, for example a png sized 966 x 525 pixel.
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
fig = plt.figure(figsize=(20,30))
img=mpimg.imread('ausgabe.png')
imgplot = plt.imshow(img)
plt.show();
Many suggested solutions on stackoverflow involve connecting
an event to matplotlib, like
fig.canvas.mpl_connect('button_press_event', onclick)
(see Store mouse click event coordinates with matplotlib)
But in Jupyter that just doesn't react. Instead clicking on the image sometimes enlarges it.
What is a good way to display a png in Jupyter so that I could collect
(=print) the click positions - or rerun the cell with the collected information?
you can use the native Tk backend and you can retrieve the mouse click position this way.
for example
%matplotlib tk
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
fig = plt.figure(figsize=(20,30))
img=mpimg.imread('ausgabe.png')
def onclick(event):
ix, iy = event.xdata, event.ydata
print(ix, iy)
cid = fig.canvas.mpl_connect('button_press_event', onclick)
imgplot = plt.imshow(img)
plt.show()
A separate tk window will pop-up and when you click, the x,y position will print in the notebook.
if that doesn't work, you might need to use %matplotlib qt5 or even %matplotlib qt4
I agree with the post above about switching to tk.
The only thing I would like to add is that in order to get point clicks on an image, one can simply use ginput().
import matplotlib
matplotlib.use('TkAgg')
pil = Image.open("..\img\japan.jpg")
im = array(pil)
imshow(im)
pts = ginput(3) #number of clicks
print(pts)
Result:
[(0.052688172043010795, 0.06725720343611918),
(1.9451612903225803, 0.8951697538724439),
(3.9854838709677414, -0.7310870416274797)]
As pointed out by #ImportanceOfBeingErnest.
Add %matplotlib notebook to your program and you would be able to register clicks and interact with the graph like zoom in / out, move and reset. Here's how the result would appear.
Before:
After:
When I create a new figure using pyplot, it opens automatically on the top left of my screen. I would like it to open at another position (for example top right of my screen).
What I have been doing so far is change the position afterwards using:
import matplotlib.pyplot as plt
plt.figure() # opens on the top left
(x,y,w,h) = ... # The desired position
plt.get_current_fig_manager().window.setGeometry(x,y,w,h)
Is there any way I could set the desired position as default to Matplotlib? I looked up in the matplotlibrc file but found nothing that could help me... any ideas?
Thank you guys for your answers. I understand these defaults are not governed by Python.
The solution I found is to define a function to open a new figure and move it in the right place. This way, although I have to define or import the function each time I open a ipython console, I will not have to move each figure afterwards:
# in file mymodule.py
import matplotlib.pyplot as plt
def newfigure(num=None):
hfig = plt.figure(num)
plt.get_current_fig_manager().window.setGeometry(x,y,w,h)
return hfig
# in a python script, or in the interactive console:
import matplotlib.pyplot as plt
import mymodule as mm
plt.figure() # Opens where I don't want it to
mm.newfigure() # Opens at position (x,y) and with width and height (w,h)
Assuming you are using iPython I came up with the following hack.
Write this in your startup.py in ~/.ipython/profile_default/startup/
import matplotlib.pyplot as plt
def _new_figure(*args, **kwargs):
"""
This hack that creates a figure and position it
"""
fig = plt._figure(*args, **kwargs)
fig.canvas.manager.window.move(0)
return fig
plt._figure = plt.figure
plt.figure = _new_figure
This way, all plots created by matplotlib are automatically created at the given position.
Is there a way that I can make a matplotlib figure disappear and reappear in response to some event? (i.e. a keypress)
I've tried using fig.set_visible(False) but that doesn't seem to do anything for me.
Simple example of code:
import matplotlib
import matplotlib.pyplot as plt
fig=matplotlib.pyplot.figure(figsize=(10, 10))
# Some other code will go here
def toggle_plot():
# This function is called by a keypress to hide/show the figure
fig.set_visible(not fig.get_visible()) # This doesn't work for me
plt.show()
The reason I'm trying to do this is because I have a bunch of plots/animations running on the figure that show the output of a running simulation, but displaying them all the time slows down my computer a lot.
Any ideas?
You have to call plt.draw() to actually instantiate any changes. This should work:
def toggle_plot():
# This function is called by a keypress to hide/show the figure
fig.set_visible(not fig.get_visible())
plt.draw()
There is a small guide to image toggling in the matplotlib gallery. I was able to use set_visible and get_visible() as shown in the example. The calls in the matplotlib gallery example are on AxesImage instances, rather than Figure instances, as in your example code. That is my guess as to why it did not work for you.
You can you use the Toplevel() widget from the tkinter library together with the matplotlib backend.
Here is a full example:
from tkinter import *
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
fig,(ax) = plt.subplots()
x = np.linspace(0, 2 * np.pi)
y = np.transpose([np.sin(x)])
ax.plot(y)
graph = Toplevel()
canvas = FigureCanvasTkAgg(fig,master=graph)
canvas.get_tk_widget().grid()
canvas.show()
import pdb; pdb.set_trace()
Calling:
graph.withdraw()
will hide the plot, and:
graph.deiconify()
will display it again.
I would like to customize matplotlib image display so that i can type control-c and it will copy the image to the clipboard so then i can copy it to openoffice spreadsheet to organize all of my raw data and image results. Is there any way to do this? Thanks!
If you're using the wx backend, FigureCanvasWxAgg has a Copy_to_Clipboard method you can use. You could bind the CTRL+C key event to call this method. For an example, see this sample code.
import matplotlib
import matplotlib.pyplot as plt
if not globals().has_key('__figure'):
__figure = matplotlib.pyplot.figure
def on_key(event):
print event
if event.key=='c':
#print event.canvas.__dict__#.Copy_to_Clipboard(event=event)
# print event.canvas._tkphoto.__dict__
plt.savefig("/tmp/fig.png")
def my_figure():
fig = __figure()
fig.canvas.mpl_connect('key_press_event',on_key)
return fig
matplotlib.pyplot.figure = my_figure
This works for tk backend, but i have no clue how to copy an image to a clipboard. For text, i can use xclip, but images dont work! And for some reason the wx backend doesnt work too well on ubuntu...