カーソルがマットプロットプロット上をホバリングしているときにスクロールホイールを使用してズームイン/アウトすることはできますか?Matplotlibプロットズームスクロールホイール
答えて
これは動作するはずです。スクロールすると、グラフの位置がポインタの位置に戻ります。あなたが軸オブジェクトax
ax.plot(range(10))
scale = 1.5
f = zoom_factory(ax,base_scale = scale)
にオプションの引数base_scale
を持っていると仮定すると
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
はあなたが欲しい、これまで何をするスケール係数を設定することができます。
f
のコピーを保管してください。コールバックでは弱参照が使用されるため、f
のコピーを保持しないとガベージコレクションが行われる可能性があります。
この答えを書いた後、私は、この実際には非常に便利なことを決定し、それを入れgist
私はこれも独立してやった! 私はそれ以前にチェックしておきたいと思います。私も貢献したいと思います。 – RodericDay
@RodericDayあなたは要点をつかみ、それをより良くすることができます – tacaswell
私は実際のコードを他の人が使用するためにそこに提出する段階ではありませんが、ユーザーが相対座標に興味を持っている場合は以下の修正をお勧めします – 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()
この少し変更されたコードの目的は、新たなズーム中心にカーソルの位置を追跡することです。この方法では、中心以外の点で画像を拡大または縮小すると、同じ点に留まります。
ありがとうございました。この例は非常に役に立ちました。私は散布図で作業するためにいくつかの変更を加えなければならず、左ボタンドラッグでパンを追加しました。うまくいけば、誰かがこれが役に立つと思います。
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()
ありがとう。これはうまくいった。しかし、スケールが線形ではないプロット(ログプロットなど)では、これは分解されます。私はこれのために新しいバージョンを書いています。私はそれが誰かを助けることを望む。
基本的には、[0,1]に正規化された軸座標を拡大します。したがって、xで2倍にズームすると、今は[.25、.75]の範囲になりたいです。 x軸の上下にある場合はxを拡大し、y軸の左右に直接移動する場合はyのみを拡大する機能も追加しました。これを必要としない場合は、zoomx = True、zoomy = Trueを設定し、if文は無視してください。 http://matplotlib.org/users/transforms_tutorial.html
この関数は、軸(self.ax)へのポインタを含むオブジェクト内にある:
この参考文献はmatplotlibの異なる座標系の間で変換する方法を理解したい人のために非常に有用です。
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()
Figureプロットでは、「xのみ」または「yのみ」のモードが本当に好きです。 xとyキーをバインドして、ズームが一方向のみで行われるようにすることができます。
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
この:
canvas.mpl_connect('button_press_event', lambda event:canvas._tkcanvas.focus_set())
変更されたコードの残りの部分は以下の通りです - あなたが入力ボックスか何かをクリックした場合にも、バックキャンバスにフォーカスを配置する必要があることに注意してください上記のコードをわずかに修正することを提案します。これにより、ズームをより管理しやすくすることができます。
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. matplotlibの:matplotlibのスクラッチ
- 2. matplotlibのではmatplotlibの
- 3. matplotlib
- 4. matplotlibの
- 5. Matplotlib:スクロールプロット
- 6. matplotlibの
- 7. Matplotlibワークフロー
- 8. Matplotlibコンタープロットラベル
- 9. matplotlibの
- 10. matplotlibノーマルヒストグラム
- 11. Matplotlibアニメーション
- 12. matplotlibの
- 13. Matplotlibカラーパレット
- 14. Matplotlibアニメーションヒストグラム
- 15. matplotlibアニメートメモリリーク
- 16. matplotlibの:
- 17. matplotlibカラーバーのトップラベル
- 18. matplotlib Axes3Dエラー
- 19. Matplotlib上のスクロールバー
- 20. Tensorflow + Matplotlibアニメーション
- 21. グループ化ボックスプロットmatplotlib
- 22. Qt5 Matplotlibデザイナープラグイン
- 23. MatplotlibのX軸
- 24. インタラクティブグラフPython Jupyter/Matplotlib
- 25. set_dataとautoscale_view matplotlib
- 26. matplotlibの - はImportError:
- 27. matplotlibのをset_yticklabels)
- 28. jupyter notebook%matplotlibエラー
- 29. python matplotlibアニメマルチライン
- 30. Matplotlibラインタイムシリーズ散布
あなたはこののhttp://matplotlib.sourceforge.net/api/backend_bases_api.html?highlight=mpl_connect#matplotlib.backend_bases.FigureCanvasBase.mpl_connect – tacaswell
任意の例ということを行うには、コールバック関数を書くことができますか? – dimka