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.
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')
I'm using matplotlib to generate a chart with a dynamic line at any point on the y-axis as a threshold (that is, the user clicks on the chart somewhere and a new line will be generated that replaces the previous one). I can add new lines with axhline(), but I can't figure out how to remove the previous line. I've seen references to Artist.remove and ax.remove.line(0), but I'm still fairly new to matplotlib and haven't been able to get anything to work.
NB: I've only been working in Jupyter, so I don't know if this will work as-is outside of Jupyter. Also, I know that the separate update() procedure isn't really necessary here, but I'll most likely need it for future functionality.
%matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.figure as fig
import numpy as np
from matplotlib.axes import Axes as ax
from matplotlib.artist import Artist as art
x = np.random.normal(size = 1000)
plt.hist(x, bins=50, alpha=0.75)
plt.gcf().canvas.draw()
green = plt.axhline(35, color='g')
print('green line = {}'.format(green))
def update(threshold, lines):
plt.gca().set_title('most recent line = {}'.format(lines))
def on_press(event):
threshold = event.ydata
lines = plt.axhline(threshold, color='r')
update(threshold, lines)
plt.gcf().canvas.mpl_connect('button_press_event', on_press)
You can get at the lines in the subplot with plt.gca().lines, so you can just add something like this to the beginning of your on_press() function:
plt.gca().lines.pop()
Or adding plt.gca().pop(0) to the update() function also seems to work.
I don't know if this is the best way to do it or I just got lucky, but I found that calling plt.delaxes() before drawing the new line got me what I needed. The axhline() is the last thing I draw on the plot before the user has a chance to interact with it, so that may be why it works.
I have a Python program that generates graphs using matplotlib. I am trying to get the program to generate a bunch of plots in one program run (the user is asked if they want to generate another graph) all in separate windows. Any way I can do this?
To generate a new figure, you can add plt.figure() before any plotting that your program does.
import matplotlib.pyplot as plt
import numpy as np
def make_plot(slope):
x = np.arange(1,10)
y = slope*x+3
plt.figure()
plt.plot(x,y)
make_plot(2)
make_plot(3)
Using the latest matlibplot, I found the following to work for my purposes:
# create figure (will only create new window if needed)
plt.figure()
# Generate plot1
plt.plot(range(10, 20))
# Show the plot in non-blocking mode
plt.show(block=False)
# create figure (will only create new window if needed)
plt.figure()
# Generate plot2
plt.plot(range(10, 20))
# Show the plot in non-blocking mode
plt.show(block=False)
...
# Finally block main thread until all plots are closed
plt.show()
The easiest way to ensure all of your lines go to the correct figure window is something like:
from six.moves import input
import matplotlib.pyplot as plt
another = True
while another:
fig, ax = plt.subplots()
ax.plot(range(5))
fig.canvas.manager.show()
# this makes sure that the gui window gets shown
# if this is needed depends on rcparams, this is just to be safe
fig.canvas.flush_events()
# this make sure that if the event loop integration is not
# set up by the gui framework the plot will update
another = bool(input("would you like another? "))
If you want to run this with a non-gui backend you will need to drop the flush_events call or wrap it in a try: ... except NotImplementedError. Much of this complication is defensive programming because GUIs can be difficult and the behavior of this code may be dependent on many factors which are not obvious from the code shown.
Using the implicit axes of pyplot can cause problems as the 'current axes' is set by the last axes the user clicked on. You should really only use pyplot when interactively typing at the rpel and almost never (other than plt.subplots) in scripts/programs.
Use the .figure() function to create a new window, the following code makes two windows:
import matplotlib.pyplot as plt
plt.plot(range(10)) # Creates the plot. No need to save the current figure.
plt.draw() # Draws, but does not block
plt.figure() # New window, if needed. No need to save it, as pyplot uses the concept of current figure
plt.plot(range(10, 20))
plt.draw()
You can repeat this as many times as you want
I followed the setting from here to make matplotlib/seaborn available to display in Zeppelin. However, with the following code:
%python
import seaborn as sns
import matplotlib
import numpy as np
matplotlib.use('Agg')
import matplotlib.pyplot as plt
plt.rcdefaults()
import StringIO
def show(p):
img = StringIO.StringIO()
p.savefig(img, format='svg')
img.seek(0)
print "%html <div style='width:600px'>" + img.buf + "</div>"
""" Prepare your plot here ... """
# Use the custom show function instead of plt.show()
x = np.random.randn(100)
ax = sns.distplot(x)
show(sns.plt)
It is strange that the displayed figure show the desired lightblue color the first time I run the code but will display different colors if I execute the same piece of code. Is there a way to force seaborn to keep constant color being displayed? Thanks.
It's not entirely clear what is meant by "running a second time".
However you may try to actually close the figure before running it again. E.g.
plt.close("all")
in order to make sure, a new figure is created which should have the same default color every time.
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.