Closed GvozdevLeonid closed 5 months ago
Maybe you can rewrite apply_pan function in your case and create a custom matplotlib graph
""" Custom pan for MatplotFigure
"""
import matplotlib
matplotlib.use('Agg')
from kivy_matplotlib_widget.uix.graph_widget import MatplotFigure
from kivy.properties import ListProperty
class MatplotFigureCustomPan(MatplotFigure):
"""Custom pan for MatplotFigure
"""
stop_x_scroll = ListProperty(None, allownone=True)
stop_y_scroll = ListProperty(None, allownone=True)
def __init__(self, **kwargs):
super(MatplotFigureCustomPan, self).__init__(**kwargs)
def apply_pan(self, ax, event, mode='pan'):
""" pan method """
trans = ax.transData.inverted()
xdata, ydata = trans.transform_point((event.x-self.pos[0], event.y-self.pos[1]))
xpress, ypress = trans.transform_point((self._last_touch_pos[event][0]-self.pos[0], self._last_touch_pos[event][1]-self.pos[1]))
dx = xdata - xpress
dy = ydata - ypress
xleft,xright=self.axes.get_xlim()
ybottom,ytop=self.axes.get_ylim()
#check inverted data
inverted_x = False
if xleft>xright:
inverted_x=True
cur_xlim=(xright,xleft)
else:
cur_xlim=(xleft,xright)
inverted_y = False
if ybottom>ytop:
inverted_y=True
cur_ylim=(ytop,ybottom)
else:
cur_ylim=(ybottom,ytop)
if self.interactive_axis and self.touch_mode=='pan' and not self.first_touch_pan=='pan':
if (ydata < cur_ylim[0] and not inverted_y) or (ydata > cur_ylim[1] and inverted_y):
left_anchor_zone= (cur_xlim[1] - cur_xlim[0])*.2 + cur_xlim[0]
right_anchor_zone= (cur_xlim[1] - cur_xlim[0])*.8 + cur_xlim[0]
if xdata < left_anchor_zone or xdata > right_anchor_zone:
mode = 'adjust_x'
else:
mode = 'pan_x'
self.touch_mode = mode
elif (xdata < cur_xlim[0] and not inverted_x) or (xdata > cur_xlim[1] and inverted_x):
bottom_anchor_zone= (cur_ylim[1] - cur_ylim[0])*.2 + cur_ylim[0]
top_anchor_zone= (cur_ylim[1] - cur_ylim[0])*.8 + cur_ylim[0]
if ydata < bottom_anchor_zone or ydata > top_anchor_zone:
mode = 'adjust_y'
else:
mode= 'pan_y'
self.touch_mode = mode
else:
self.touch_mode = 'pan'
if not mode=='pan_y' and not mode=='adjust_y':
if mode=='adjust_x':
if self.anchor_x is None:
midpoint= (cur_xlim[1] + cur_xlim[0])/2
if xdata>midpoint:
self.anchor_x='left'
else:
self.anchor_x='right'
if self.anchor_x=='left':
if xdata> cur_xlim[0]:
cur_xlim -= dx/2
if inverted_x:
ax.set_xlim(cur_xlim[1],None)
else:
ax.set_xlim(None,cur_xlim[1])
else:
if xdata< cur_xlim[1]:
cur_xlim -= dx/2
if inverted_x:
ax.set_xlim(None,cur_xlim[0])
else:
ax.set_xlim(cur_xlim[0],None)
else:
cur_xlim -= dx/2
if self.stop_x_scroll is not None:
if cur_xlim[0] > self.stop_x_scroll[0] and cur_xlim[1] < self.stop_x_scroll[1]:
if inverted_x:
ax.set_xlim(cur_xlim[1],cur_xlim[0])
else:
ax.set_xlim(cur_xlim)
else:
if inverted_x:
ax.set_xlim(cur_xlim[1],cur_xlim[0])
else:
ax.set_xlim(cur_xlim)
if not mode=='pan_x' and not mode=='adjust_x':
if mode=='adjust_y':
if self.anchor_y is None:
midpoint= (cur_ylim[1] + cur_ylim[0])/2
if ydata>midpoint:
self.anchor_y='top'
else:
self.anchor_y='bottom'
if self.anchor_y=='top':
if ydata> cur_ylim[0]:
cur_ylim -= dy/2
if inverted_y:
ax.set_ylim(cur_ylim[1],None)
else:
ax.set_ylim(None,cur_ylim[1])
else:
if ydata< cur_ylim[1]:
cur_ylim -= dy/2
if inverted_y:
ax.set_ylim(None,cur_ylim[0])
else:
ax.set_ylim(cur_ylim[0],None)
else:
cur_ylim -= dy/2
if inverted_y:
ax.set_ylim(cur_ylim[1],cur_ylim[0])
else:
ax.set_ylim(cur_ylim)
if self.first_touch_pan is None:
self.first_touch_pan=self.touch_mode
if self.fast_draw:
#use blit method
if self.background is None:
self.background_patch_copy.set_visible(True)
ax.figure.canvas.draw_idle()
ax.figure.canvas.flush_events()
self.background = ax.figure.canvas.copy_from_bbox(ax.figure.bbox)
self.background_patch_copy.set_visible(False)
ax.figure.canvas.restore_region(self.background)
for line in ax.lines:
ax.draw_artist(line)
ax.figure.canvas.blit(ax.bbox)
ax.figure.canvas.flush_events()
else:
ax.figure.canvas.draw_idle()
ax.figure.canvas.flush_events()
I don't know your purpose, but for me it's a specific case (not a general feature).
I use it for Spectrum Graph.
This is a very important feature. Sometimes it is very important to disable scrolling and zooming beyond the given limits (the user may lose sight of the data)
And thanks. I created a new widget based on your class, which disables scroll and zoom if additional limits are specified
@GvozdevLeonid Feel free to closed this issue if your new widget solve the problem.
I still available if you have any question regarding my code. I'm not good with commenting my code and sometime it can be hard understand what I'm doing.
class MatplotFigure(Widget): stop_x_scroll = ListProperty(None, allownone=True) stop_y_scroll = ListProperty(None, allownone=True)
And same with Y axis