¿Es posible enlazar la rueda de desplazamiento para acercar/alejar cuando el cursor se desplaza sobre un gráfico matplotlib?Gráfico Matplotlib zoom con rueda de desplazamiento
Respuesta
Esto debería funcionar. Reubica el gráfico en la ubicación del puntero cuando se desplaza.
import matplotlib.pyplot as plt
def zoom_factory(ax,base_scale = 2.):
def zoom_fun(event):
# get the current x and y limits
cur_xlim = ax.get_xlim()
cur_ylim = ax.get_ylim()
cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5
cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5
xdata = event.xdata # get event x location
ydata = event.ydata # get event y location
if event.button == 'up':
# deal with zoom in
scale_factor = 1/base_scale
elif event.button == 'down':
# deal with zoom out
scale_factor = base_scale
else:
# deal with something that should never happen
scale_factor = 1
print event.button
# set new limits
ax.set_xlim([xdata - cur_xrange*scale_factor,
xdata + cur_xrange*scale_factor])
ax.set_ylim([ydata - cur_yrange*scale_factor,
ydata + cur_yrange*scale_factor])
plt.draw() # force re-draw
fig = ax.get_figure() # get the figure of interest
# attach the call back
fig.canvas.mpl_connect('scroll_event',zoom_fun)
#return the function
return zoom_fun
Asumiendo que tiene un objeto eje ax
ax.plot(range(10))
scale = 1.5
f = zoom_factory(ax,base_scale = scale)
El argumento opcional base_scale
le permite establecer el factor de escala para ser cualquier cosa que quiera.
asegúrese de mantener una copia de f
alrededor. La devolución de llamada utiliza un ref débil, por lo que si no conserva una copia de f
, puede ser un recolector de basura.
Después de escribir esta respuesta decidí esto en realidad bastante útil y lo puso en un gist
¡Yo también hice esto independientemente! Ojalá hubiera verificado antes. Me gustaría haber contribuido también. – RodericDay
@RodericDay Usted puede agarrar la esencia y hacerlo mejor – tacaswell
no estoy en la etapa donde presento código real que hay para que otras personas usen, pero voy a recomendar una solución a continuación en caso de que el usuario está interesado en coordenadas relativas – RodericDay
def zoom(self, event, factor):
curr_xlim = self.ax.get_xlim()
curr_ylim = self.ax.get_ylim()
new_width = (curr_xlim[1]-curr_ylim[0])*factor
new_height= (curr_xlim[1]-curr_ylim[0])*factor
relx = (curr_xlim[1]-event.xdata)/(curr_xlim[1]-curr_xlim[0])
rely = (curr_ylim[1]-event.ydata)/(curr_ylim[1]-curr_ylim[0])
self.ax.set_xlim([event.xdata-new_width*(1-relx),
event.xdata+new_width*(relx)])
self.ax.set_ylim([event.ydata-new_width*(1-rely),
event.ydata+new_width*(rely)])
self.draw()
El propósito de este código ligeramente alterada es hacer un seguimiento de la posición del cursor en relación con el nuevo centro de zoom . De esta forma, si amplía y aleja la imagen en puntos distintos al centro, permanece en el mismo punto.
Gracias chicos, los ejemplos fueron muy útiles. Tuve que hacer algunos cambios para trabajar con un diagrama de dispersión y agregué el barrido con un botón izquierdo. Con suerte, alguien encontrará esto útil.
from matplotlib.pyplot import figure, show
import numpy
class ZoomPan:
def __init__(self):
self.press = None
self.cur_xlim = None
self.cur_ylim = None
self.x0 = None
self.y0 = None
self.x1 = None
self.y1 = None
self.xpress = None
self.ypress = None
def zoom_factory(self, ax, base_scale = 2.):
def zoom(event):
cur_xlim = ax.get_xlim()
cur_ylim = ax.get_ylim()
xdata = event.xdata # get event x location
ydata = event.ydata # get event y location
if event.button == 'down':
# deal with zoom in
scale_factor = 1/base_scale
elif event.button == 'up':
# deal with zoom out
scale_factor = base_scale
else:
# deal with something that should never happen
scale_factor = 1
print event.button
new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor
relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0])
rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])
ax.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
ax.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
ax.figure.canvas.draw()
fig = ax.get_figure() # get the figure of interest
fig.canvas.mpl_connect('scroll_event', zoom)
return zoom
def pan_factory(self, ax):
def onPress(event):
if event.inaxes != ax: return
self.cur_xlim = ax.get_xlim()
self.cur_ylim = ax.get_ylim()
self.press = self.x0, self.y0, event.xdata, event.ydata
self.x0, self.y0, self.xpress, self.ypress = self.press
def onRelease(event):
self.press = None
ax.figure.canvas.draw()
def onMotion(event):
if self.press is None: return
if event.inaxes != ax: return
dx = event.xdata - self.xpress
dy = event.ydata - self.ypress
self.cur_xlim -= dx
self.cur_ylim -= dy
ax.set_xlim(self.cur_xlim)
ax.set_ylim(self.cur_ylim)
ax.figure.canvas.draw()
fig = ax.get_figure() # get the figure of interest
# attach the call back
fig.canvas.mpl_connect('button_press_event',onPress)
fig.canvas.mpl_connect('button_release_event',onRelease)
fig.canvas.mpl_connect('motion_notify_event',onMotion)
#return the function
return onMotion
fig = figure()
ax = fig.add_subplot(111, xlim=(0,1), ylim=(0,1), autoscale_on=False)
ax.set_title('Click to zoom')
x,y,s,c = numpy.random.rand(4,200)
s *= 200
ax.scatter(x,y,s,c)
scale = 1.1
zp = ZoomPan()
figZoom = zp.zoom_factory(ax, base_scale = scale)
figPan = zp.pan_factory(ax)
show()
Muchas gracias. Esto funcionó muy bien. Sin embargo, para las parcelas donde la escala ya no es lineal (parcelas de registro, por ejemplo), esto se descompone. He escrito una nueva versión para esto. Espero que esto ayude a alguien.
Básicamente, acerco las coordenadas de los ejes que están normalizadas a ser [0,1]. Entonces, si amplío dos en x, ahora quiero estar en el rango [.25, .75]. También he agregado una función para ampliar solo x si estás directamente encima o debajo del eje x, y solo en y si estás directamente a la izquierda o derecha en el eje y. Si no necesita esto, simplemente configure zoomx = True y zoomy = True e ignore las declaraciones if.
Esta referencia es muy útil para aquellos que quieren entender cómo se transforma matplotlib entre los diferentes sistemas de coordenadas: http://matplotlib.org/users/transforms_tutorial.html
Esta función está dentro de un objeto que contiene un puntero a los ejes (self.ax).
def zoom(self,event):
'''This function zooms the image upon scrolling the mouse wheel.
Scrolling it in the plot zooms the plot. Scrolling above or below the
plot scrolls the x axis. Scrolling to the left or the right of the plot
scrolls the y axis. Where it is ambiguous nothing happens.
NOTE: If expanding figure to subplots, you will need to add an extra
check to make sure you are not in any other plot. It is not clear how to
go about this.
Since we also want this to work in loglog plot, we work in axes
coordinates and use the proper scaling transform to convert to data
limits.'''
x = event.x
y = event.y
#convert pixels to axes
tranP2A = self.ax.transAxes.inverted().transform
#convert axes to data limits
tranA2D= self.ax.transLimits.inverted().transform
#convert the scale (for log plots)
tranSclA2D = self.ax.transScale.inverted().transform
if event.button == 'down':
# deal with zoom in
scale_factor = self.zoom_scale
elif event.button == 'up':
# deal with zoom out
scale_factor = 1/self.zoom_scale
else:
# deal with something that should never happen
scale_factor = 1
#get my axes position to know where I am with respect to them
xa,ya = tranP2A((x,y))
zoomx = False
zoomy = False
if(ya < 0):
if(xa >= 0 and xa <= 1):
zoomx = True
zoomy = False
elif(ya <= 1):
if(xa <0):
zoomx = False
zoomy = True
elif(xa <= 1):
zoomx = True
zoomy = True
else:
zoomx = False
zoomy = True
else:
if(xa >=0 and xa <= 1):
zoomx = True
zoomy = False
new_alimx = (0,1)
new_alimy = (0,1)
if(zoomx):
new_alimx = (np.array([1,1]) + np.array([-1,1])*scale_factor)*.5
if(zoomy):
new_alimy = (np.array([1,1]) + np.array([-1,1])*scale_factor)*.5
#now convert axes to data
new_xlim0,new_ylim0 = tranSclA2D(tranA2D((new_alimx[0],new_alimy[0])))
new_xlim1,new_ylim1 = tranSclA2D(tranA2D((new_alimx[1],new_alimy[1])))
#and set limits
self.ax.set_xlim([new_xlim0,new_xlim1])
self.ax.set_ylim([new_ylim0,new_ylim1])
self.redraw()
¿Puede enviar esta corriente ascendente? Debe haber un parche en https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/backend_tools.py#L625 – tacaswell
hecho. https://github.com/matplotlib/matplotlib/pull/4970 por primera vez haciendo esto, así que avíseme si hay algo que debería haber hecho, o podría haberlo hecho mejor. ¡Gracias! – julienl
Me gustan mucho los modos "x solo" o "solo y solo" en los diagramas de figuras. Puede enlazar las teclas x e y para que el zoom solo ocurra en una dirección. Tenga en cuenta que también puede tener que colocar el foco de nuevo en el lienzo si hace clic en un cuadro de entrada o algo -
canvas.mpl_connect('button_press_event', lambda event:canvas._tkcanvas.focus_set())
El resto del código modificado es el siguiente:
from matplotlib.pyplot import figure, show
import numpy
class ZoomPan:
def __init__(self):
self.press = None
self.cur_xlim = None
self.cur_ylim = None
self.x0 = None
self.y0 = None
self.x1 = None
self.y1 = None
self.xpress = None
self.ypress = None
self.xzoom = True
self.yzoom = True
self.cidBP = None
self.cidBR = None
self.cidBM = None
self.cidKeyP = None
self.cidKeyR = None
self.cidScroll = None
def zoom_factory(self, ax, base_scale = 2.):
def zoom(event):
cur_xlim = ax.get_xlim()
cur_ylim = ax.get_ylim()
xdata = event.xdata # get event x location
ydata = event.ydata # get event y location
if(xdata is None):
return()
if(ydata is None):
return()
if event.button == 'down':
# deal with zoom in
scale_factor = 1/base_scale
elif event.button == 'up':
# deal with zoom out
scale_factor = base_scale
else:
# deal with something that should never happen
scale_factor = 1
print(event.button)
new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor
relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0])
rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])
if(self.xzoom):
ax.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
if(self.yzoom):
ax.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
ax.figure.canvas.draw()
ax.figure.canvas.flush_events()
def onKeyPress(event):
if event.key == 'x':
self.xzoom = True
self.yzoom = False
if event.key == 'y':
self.xzoom = False
self.yzoom = True
def onKeyRelease(event):
self.xzoom = True
self.yzoom = True
fig = ax.get_figure() # get the figure of interest
self.cidScroll = fig.canvas.mpl_connect('scroll_event', zoom)
self.cidKeyP = fig.canvas.mpl_connect('key_press_event',onKeyPress)
self.cidKeyR = fig.canvas.mpl_connect('key_release_event',onKeyRelease)
return zoom
def pan_factory(self, ax):
def onPress(event):
if event.inaxes != ax: return
self.cur_xlim = ax.get_xlim()
self.cur_ylim = ax.get_ylim()
self.press = self.x0, self.y0, event.xdata, event.ydata
self.x0, self.y0, self.xpress, self.ypress = self.press
def onRelease(event):
self.press = None
ax.figure.canvas.draw()
def onMotion(event):
if self.press is None: return
if event.inaxes != ax: return
dx = event.xdata - self.xpress
dy = event.ydata - self.ypress
self.cur_xlim -= dx
self.cur_ylim -= dy
ax.set_xlim(self.cur_xlim)
ax.set_ylim(self.cur_ylim)
ax.figure.canvas.draw()
ax.figure.canvas.flush_events()
fig = ax.get_figure() # get the figure of interest
self.cidBP = fig.canvas.mpl_connect('button_press_event',onPress)
self.cidBR = fig.canvas.mpl_connect('button_release_event',onRelease)
self.cidBM = fig.canvas.mpl_connect('motion_notify_event',onMotion)
# attach the call back
#return the function
return onMotion
Este es una sugerencia para una ligera modificación del código anterior: hace que mantener el zoom centrado sea más manejable.
cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5
cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5
xmouse = event.xdata # get event x location
ymouse = event.ydata # get event y location
cur_xcentre = (cur_xlim[1] + cur_xlim[0])*.5
cur_ycentre = (cur_ylim[1] + cur_ylim[0])*.5
xdata = cur_xcentre+ 0.25*(xmouse-cur_xcentre)
ydata = cur_ycentre+ 0.25*(ymouse-cur_ycentre)
- 1. Desplazamiento horizontal con la rueda de desplazamiento en Eclipse
- 2. Pygame zoom/ventana de desplazamiento
- 3. Matplotlib imshow función de zoom?
- 4. Zoom gráfico de barras con d3.js
- 5. Hacer Gráfico DataVisualization objeto de zoom y desplazamiento arrastrando la barra de desplazamiento
- 6. matplotlib gráfico de barras con las fechas
- 7. Gráfico de puntos y figuras con matplotlib
- 8. Matplotlib: gráfico de contorno con control deslizante
- 9. matplotlib gráfico de barras centrado con fechas
- 10. Matplotlib - Gráfico de contorno con valor único
- 11. Desplazamiento horizontal con la rueda del mouse en Visual Studio
- 12. Desplazamiento horizontal con la rueda del mouse en un div
- 13. Desplazamiento vertical suave de la página con la rueda del mouse y la barra de desplazamiento
- 14. Clase de Android ImageViewer compatible con Pinch-Zoom y desplazamiento
- 15. deshabilitar el efecto CTRL/Zoom de rueda en el tiempo de ejecución
- 16. Gráfico de barras con etiquetas verticales en python/matplotlib
- 17. ¿Cómo puedo crear un gráfico de líneas apiladas con matplotlib?
- 18. Matplotlib: Anotando un gráfico de dispersión 3D
- 19. Desplazamiento y zoom SVG Viewer en Windows?
- 20. Matplotlib: gráfico interactivo en un servidor web
- 21. Cómo eliminar líneas en un gráfico Matplotlib
- 22. Usando la rueda de desplazamiento del mouse en GLUT
- 23. TListView y desplazamiento de la rueda del mouse
- 24. Matplotlib/Pyplot: ¿Cómo hacer zoom de subparcelas juntas?
- 25. matplotlib gráfico imagen pequeña sin remuestreo
- 26. Matplotlib: guardar el gráfico en numpy array
- 27. UIScrollView. ¿Alguna idea sobre la implementación de desplazamiento/zoom "infinito"?
- 28. Gráfico de Javafx 2 y zoom a mano alzada
- 29. Dibujo de un gráfico de correlación en matplotlib
- 30. Google Maps API v3 no deshabilitará la rueda de desplazamiento después de cargar el mapa
puede escribir una función de devolución de llamada para hacer que los http://matplotlib.sourceforge.net/api/backend_bases_api.html?highlight=mpl_connect#matplotlib.backend_bases.FigureCanvasBase.mpl_connect – tacaswell
ningún ejemplo de esto? – dimka