Skip to content

Commit 576179e

Browse files
committed
add back/foward options. add pan_x and pan_y options. add adjust_x and adjust_y options.
1 parent bd0f440 commit 576179e

File tree

2 files changed

+282
-20
lines changed

2 files changed

+282
-20
lines changed

example_cursor_scatter_experimental/graph_widget_scatter.py

Lines changed: 141 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from kivy.uix.widget import Widget
1616
from kivy.vector import Vector
1717
from matplotlib.backends.backend_agg import FigureCanvasAgg
18+
from matplotlib import cbook
19+
from weakref import WeakKeyDictionary
1820
from kivy.metrics import dp
1921
import numpy as np
2022

@@ -48,6 +50,10 @@ class MatplotFigureScatter(Widget):
4850
legend_instance = ObjectProperty(None)
4951
legend_do_scroll_x = BooleanProperty(True)
5052
legend_do_scroll_y = BooleanProperty(True)
53+
do_pan_x = BooleanProperty(True)
54+
do_pan_y = BooleanProperty(True)
55+
do_zoom_x = BooleanProperty(True)
56+
do_zoom_y = BooleanProperty(True)
5157

5258
def on_figure(self, obj, value):
5359
self.figcanvas = _FigureCanvas(self.figure, self)
@@ -116,6 +122,14 @@ def __init__(self, **kwargs):
116122
#background
117123
self.background=None
118124
self.background_patch_copy=None
125+
126+
#manage adjust x and y
127+
self.anchor_x = None
128+
self.anchor_y = None
129+
130+
#manage back and next event
131+
self._nav_stack = cbook.Stack()
132+
self.set_history_buttons()
119133

120134
self.bind(size=self._onSize)
121135

@@ -392,6 +406,66 @@ def home(self) -> None:
392406
ax.figure.canvas.draw_idle()
393407
ax.figure.canvas.flush_events()
394408

409+
def back(self, *args):
410+
"""
411+
Move back up the view lim stack.
412+
For convenience of being directly connected as a GUI callback, which
413+
often get passed additional parameters, this method accepts arbitrary
414+
parameters, but does not use them.
415+
"""
416+
self._nav_stack.back()
417+
self.set_history_buttons()
418+
self._update_view()
419+
420+
def forward(self, *args):
421+
"""
422+
Move forward in the view lim stack.
423+
For convenience of being directly connected as a GUI callback, which
424+
often get passed additional parameters, this method accepts arbitrary
425+
parameters, but does not use them.
426+
"""
427+
self._nav_stack.forward()
428+
self.set_history_buttons()
429+
self._update_view()
430+
431+
def push_current(self):
432+
"""Push the current view limits and position onto the stack."""
433+
self._nav_stack.push(
434+
WeakKeyDictionary(
435+
{ax: (ax._get_view(),
436+
# Store both the original and modified positions.
437+
(ax.get_position(True).frozen(),
438+
ax.get_position().frozen()))
439+
for ax in self.figure.axes}))
440+
self.set_history_buttons()
441+
442+
def update(self):
443+
"""Reset the Axes stack."""
444+
self._nav_stack.clear()
445+
self.set_history_buttons()
446+
447+
def _update_view(self):
448+
"""
449+
Update the viewlim and position from the view and position stack for
450+
each Axes.
451+
"""
452+
nav_info = self._nav_stack()
453+
if nav_info is None:
454+
return
455+
# Retrieve all items at once to avoid any risk of GC deleting an Axes
456+
# while in the middle of the loop below.
457+
items = list(nav_info.items())
458+
for ax, (view, (pos_orig, pos_active)) in items:
459+
ax._set_view(view)
460+
# Restore both the original and modified positions
461+
ax._set_position(pos_orig, 'original')
462+
ax._set_position(pos_active, 'active')
463+
self.figure.canvas.draw_idle()
464+
self.figure.canvas.flush_events()
465+
466+
def set_history_buttons(self):
467+
"""Enable or disable the back/forward button."""
468+
395469
def reset_touch(self) -> None:
396470
""" reset touch
397471
@@ -450,13 +524,23 @@ def transform_with_touch(self, event):
450524

451525
if len(self._touches) == self.translation_touches:
452526
if self.touch_mode=='pan':
527+
if self._nav_stack() is None:
528+
self.push_current()
453529
self.apply_pan(self.axes, event)
530+
531+
if self.touch_mode=='pan_x' or self.touch_mode=='pan_y' \
532+
or self.touch_mode=='adjust_x' or self.touch_mode=='adjust_y':
533+
if self._nav_stack() is None:
534+
self.push_current()
535+
self.apply_pan(self.axes, event, mode=self.touch_mode)
454536

455537
elif self.touch_mode=='drag_legend':
456538
if self.legend_instance:
457539
self.apply_drag_legend(self.axes, event)
458540

459541
elif self.touch_mode=='zoombox':
542+
if self._nav_stack() is None:
543+
self.push_current()
460544
real_x, real_y = event.x - self.pos[0], event.y - self.pos[1]
461545
self.draw_box(event, self.x_init,self.y_init, event.x, real_y)
462546

@@ -601,7 +685,11 @@ def on_touch_up(self, event):
601685
event.ungrab(self)
602686
del self._last_touch_pos[event]
603687
self._touches.remove(event)
604-
688+
if self.touch_mode=='pan' or self.touch_mode=='zoombox' or \
689+
self.touch_mode=='pan_x' or self.touch_mode=='pan_y' \
690+
or self.touch_mode=='adjust_x' or self.touch_mode=='adjust_y':
691+
self.push_current()
692+
605693
x, y = event.x, event.y
606694
if abs(self._box_size[0]) > 1 or abs(self._box_size[1]) > 1 or self.touch_mode=='zoombox':
607695
self.reset_box()
@@ -619,6 +707,9 @@ def on_touch_up(self, event):
619707
if self.do_update:
620708
self.update_lim()
621709

710+
self.anchor_x=None
711+
self.anchor_y=None
712+
622713
ax=self.axes
623714
self.background=None
624715
ax.figure.canvas.draw_idle()
@@ -644,8 +735,10 @@ def apply_zoom(self, scale_factor, ax, anchor=(0, 0),new_line=None):
644735
relx = (cur_xlim[1] - xdata) / (cur_xlim[1] - cur_xlim[0])
645736
rely = (cur_ylim[1] - ydata) / (cur_ylim[1] - cur_ylim[0])
646737

647-
ax.set_xlim([xdata - new_width * (1 - relx), xdata + new_width * (relx)])
648-
ax.set_ylim([ydata - new_height * (1 - rely), ydata + new_height * (rely)])
738+
if self.do_zoom_x:
739+
ax.set_xlim([xdata - new_width * (1 - relx), xdata + new_width * (relx)])
740+
if self.do_zoom_y:
741+
ax.set_ylim([ydata - new_height * (1 - rely), ydata + new_height * (rely)])
649742

650743
if self.fast_draw:
651744
#use blit method
@@ -675,7 +768,7 @@ def apply_zoom(self, scale_factor, ax, anchor=(0, 0),new_line=None):
675768
ax.figure.canvas.draw_idle()
676769
ax.figure.canvas.flush_events()
677770

678-
def apply_pan(self, ax, event):
771+
def apply_pan(self, ax, event, mode='pan'):
679772
""" pan method """
680773

681774
trans = ax.transData.inverted()
@@ -686,10 +779,46 @@ def apply_pan(self, ax, event):
686779

687780
cur_xlim = ax.get_xlim()
688781
cur_ylim = ax.get_ylim()
689-
cur_xlim -= dx/2
690-
cur_ylim -= dy/2
691-
ax.set_xlim(cur_xlim)
692-
ax.set_ylim(cur_ylim)
782+
if not mode=='pan_y' and not mode=='adjust_y':
783+
if mode=='adjust_x':
784+
if self.anchor_x is None:
785+
midpoint= (cur_xlim[1] - cur_xlim[0])/2
786+
if xdata>midpoint:
787+
self.anchor_x='left'
788+
else:
789+
self.anchor_x='right'
790+
if self.anchor_x=='left':
791+
if xdata> cur_xlim[0]:
792+
cur_xlim -= dx/2
793+
ax.set_xlim(None,cur_xlim[1])
794+
else:
795+
if xdata< cur_xlim[1]:
796+
cur_xlim -= dx/2
797+
ax.set_xlim(cur_xlim[0],None)
798+
else:
799+
cur_xlim -= dx/2
800+
ax.set_xlim(cur_xlim)
801+
802+
if not mode=='pan_x' and not mode=='adjust_x':
803+
if mode=='adjust_y':
804+
if self.anchor_y is None:
805+
midpoint= (cur_ylim[1] - cur_ylim[0])/2
806+
if ydata>midpoint:
807+
self.anchor_y='top'
808+
else:
809+
self.anchor_y='bottom'
810+
811+
if self.anchor_y=='top':
812+
if ydata> cur_ylim[0]:
813+
cur_ylim -= dy/2
814+
ax.set_ylim(None,cur_ylim[1])
815+
else:
816+
if ydata< cur_ylim[1]:
817+
cur_ylim -= dy/2
818+
ax.set_ylim(cur_ylim[0],None)
819+
else:
820+
cur_ylim -= dy/2
821+
ax.set_ylim(cur_ylim)
693822

694823
if self.fast_draw:
695824
#use blit method
@@ -786,8 +915,10 @@ def zoom_factory(self, event, ax, base_scale=1.1):
786915
relx = (cur_xlim[1] - xdata) / (cur_xlim[1] - cur_xlim[0])
787916
rely = (cur_ylim[1] - ydata) / (cur_ylim[1] - cur_ylim[0])
788917

789-
ax.set_xlim([xdata - new_width * (1 - relx), xdata + new_width * (relx)])
790-
ax.set_ylim([ydata - new_height * (1 - rely), ydata + new_height * (rely)])
918+
if self.do_zoom_x:
919+
ax.set_xlim([xdata - new_width * (1 - relx), xdata + new_width * (relx)])
920+
if self.do_zoom_y:
921+
ax.set_ylim([ydata - new_height * (1 - rely), ydata + new_height * (rely)])
791922

792923
ax.figure.canvas.draw_idle()
793924
ax.figure.canvas.flush_events()

0 commit comments

Comments
 (0)