Skip to content

Commit bd0f440

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

File tree

9 files changed

+1278
-90
lines changed

9 files changed

+1278
-90
lines changed

example_basic/graph_widget.py

Lines changed: 142 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 MatplotFigure(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)
@@ -110,6 +116,14 @@ def __init__(self, **kwargs):
110116
#background
111117
self.background=None
112118
self.background_patch_copy=None
119+
120+
#manage adjust x and y
121+
self.anchor_x = None
122+
self.anchor_y = None
123+
124+
#manage back and next event
125+
self._nav_stack = cbook.Stack()
126+
self.set_history_buttons()
113127

114128
self.bind(size=self._onSize)
115129

@@ -274,6 +288,66 @@ def home(self) -> None:
274288
ax.figure.canvas.draw_idle()
275289
ax.figure.canvas.flush_events()
276290

291+
def back(self, *args):
292+
"""
293+
Move back up the view lim stack.
294+
For convenience of being directly connected as a GUI callback, which
295+
often get passed additional parameters, this method accepts arbitrary
296+
parameters, but does not use them.
297+
"""
298+
self._nav_stack.back()
299+
self.set_history_buttons()
300+
self._update_view()
301+
302+
def forward(self, *args):
303+
"""
304+
Move forward in the view lim stack.
305+
For convenience of being directly connected as a GUI callback, which
306+
often get passed additional parameters, this method accepts arbitrary
307+
parameters, but does not use them.
308+
"""
309+
self._nav_stack.forward()
310+
self.set_history_buttons()
311+
self._update_view()
312+
313+
def push_current(self):
314+
"""Push the current view limits and position onto the stack."""
315+
self._nav_stack.push(
316+
WeakKeyDictionary(
317+
{ax: (ax._get_view(),
318+
# Store both the original and modified positions.
319+
(ax.get_position(True).frozen(),
320+
ax.get_position().frozen()))
321+
for ax in self.figure.axes}))
322+
self.set_history_buttons()
323+
324+
def update(self):
325+
"""Reset the Axes stack."""
326+
self._nav_stack.clear()
327+
self.set_history_buttons()
328+
329+
def _update_view(self):
330+
"""
331+
Update the viewlim and position from the view and position stack for
332+
each Axes.
333+
"""
334+
nav_info = self._nav_stack()
335+
if nav_info is None:
336+
return
337+
# Retrieve all items at once to avoid any risk of GC deleting an Axes
338+
# while in the middle of the loop below.
339+
items = list(nav_info.items())
340+
for ax, (view, (pos_orig, pos_active)) in items:
341+
ax._set_view(view)
342+
# Restore both the original and modified positions
343+
ax._set_position(pos_orig, 'original')
344+
ax._set_position(pos_active, 'active')
345+
self.figure.canvas.draw_idle()
346+
self.figure.canvas.flush_events()
347+
348+
def set_history_buttons(self):
349+
"""Enable or disable the back/forward button."""
350+
277351
def reset_touch(self) -> None:
278352
""" reset touch
279353
@@ -331,14 +405,25 @@ def transform_with_touch(self, event):
331405
changed = False
332406

333407
if len(self._touches) == self.translation_touches:
408+
334409
if self.touch_mode=='pan':
410+
if self._nav_stack() is None:
411+
self.push_current()
335412
self.apply_pan(self.axes, event)
336-
413+
414+
if self.touch_mode=='pan_x' or self.touch_mode=='pan_y' \
415+
or self.touch_mode=='adjust_x' or self.touch_mode=='adjust_y':
416+
if self._nav_stack() is None:
417+
self.push_current()
418+
self.apply_pan(self.axes, event, mode=self.touch_mode)
419+
337420
elif self.touch_mode=='drag_legend':
338421
if self.legend_instance:
339422
self.apply_drag_legend(self.axes, event)
340423

341424
elif self.touch_mode=='zoombox':
425+
if self._nav_stack() is None:
426+
self.push_current()
342427
real_x, real_y = event.x - self.pos[0], event.y - self.pos[1]
343428
self.draw_box(event, self.x_init,self.y_init, event.x, real_y)
344429

@@ -483,6 +568,10 @@ def on_touch_up(self, event):
483568
event.ungrab(self)
484569
del self._last_touch_pos[event]
485570
self._touches.remove(event)
571+
if self.touch_mode=='pan' or self.touch_mode=='zoombox' or \
572+
self.touch_mode=='pan_x' or self.touch_mode=='pan_y' \
573+
or self.touch_mode=='adjust_x' or self.touch_mode=='adjust_y':
574+
self.push_current()
486575

487576
x, y = event.x, event.y
488577
if abs(self._box_size[0]) > 1 or abs(self._box_size[1]) > 1 or self.touch_mode=='zoombox':
@@ -501,6 +590,9 @@ def on_touch_up(self, event):
501590
if self.do_update:
502591
self.update_lim()
503592

593+
self.anchor_x=None
594+
self.anchor_y=None
595+
504596
ax=self.axes
505597
self.background=None
506598
ax.figure.canvas.draw_idle()
@@ -526,8 +618,10 @@ def apply_zoom(self, scale_factor, ax, anchor=(0, 0),new_line=None):
526618
relx = (cur_xlim[1] - xdata) / (cur_xlim[1] - cur_xlim[0])
527619
rely = (cur_ylim[1] - ydata) / (cur_ylim[1] - cur_ylim[0])
528620

529-
ax.set_xlim([xdata - new_width * (1 - relx), xdata + new_width * (relx)])
530-
ax.set_ylim([ydata - new_height * (1 - rely), ydata + new_height * (rely)])
621+
if self.do_zoom_x:
622+
ax.set_xlim([xdata - new_width * (1 - relx), xdata + new_width * (relx)])
623+
if self.do_zoom_y:
624+
ax.set_ylim([ydata - new_height * (1 - rely), ydata + new_height * (rely)])
531625

532626
if self.fast_draw:
533627
#use blit method
@@ -547,7 +641,7 @@ def apply_zoom(self, scale_factor, ax, anchor=(0, 0),new_line=None):
547641
ax.figure.canvas.draw_idle()
548642
ax.figure.canvas.flush_events()
549643

550-
def apply_pan(self, ax, event):
644+
def apply_pan(self, ax, event, mode='pan'):
551645
""" pan method """
552646

553647
trans = ax.transData.inverted()
@@ -558,10 +652,46 @@ def apply_pan(self, ax, event):
558652

559653
cur_xlim = ax.get_xlim()
560654
cur_ylim = ax.get_ylim()
561-
cur_xlim -= dx/2
562-
cur_ylim -= dy/2
563-
ax.set_xlim(cur_xlim)
564-
ax.set_ylim(cur_ylim)
655+
if not mode=='pan_y' and not mode=='adjust_y':
656+
if mode=='adjust_x':
657+
if self.anchor_x is None:
658+
midpoint= (cur_xlim[1] - cur_xlim[0])/2
659+
if xdata>midpoint:
660+
self.anchor_x='left'
661+
else:
662+
self.anchor_x='right'
663+
if self.anchor_x=='left':
664+
if xdata> cur_xlim[0]:
665+
cur_xlim -= dx/2
666+
ax.set_xlim(None,cur_xlim[1])
667+
else:
668+
if xdata< cur_xlim[1]:
669+
cur_xlim -= dx/2
670+
ax.set_xlim(cur_xlim[0],None)
671+
else:
672+
cur_xlim -= dx/2
673+
ax.set_xlim(cur_xlim)
674+
675+
if not mode=='pan_x' and not mode=='adjust_x':
676+
if mode=='adjust_y':
677+
if self.anchor_y is None:
678+
midpoint= (cur_ylim[1] - cur_ylim[0])/2
679+
if ydata>midpoint:
680+
self.anchor_y='top'
681+
else:
682+
self.anchor_y='bottom'
683+
684+
if self.anchor_y=='top':
685+
if ydata> cur_ylim[0]:
686+
cur_ylim -= dy/2
687+
ax.set_ylim(None,cur_ylim[1])
688+
else:
689+
if ydata< cur_ylim[1]:
690+
cur_ylim -= dy/2
691+
ax.set_ylim(cur_ylim[0],None)
692+
else:
693+
cur_ylim -= dy/2
694+
ax.set_ylim(cur_ylim)
565695

566696
if self.fast_draw:
567697
#use blit method
@@ -649,8 +779,10 @@ def zoom_factory(self, event, ax, base_scale=1.1):
649779
relx = (cur_xlim[1] - xdata) / (cur_xlim[1] - cur_xlim[0])
650780
rely = (cur_ylim[1] - ydata) / (cur_ylim[1] - cur_ylim[0])
651781

652-
ax.set_xlim([xdata - new_width * (1 - relx), xdata + new_width * (relx)])
653-
ax.set_ylim([ydata - new_height * (1 - rely), ydata + new_height * (rely)])
782+
if self.do_zoom_x:
783+
ax.set_xlim([xdata - new_width * (1 - relx), xdata + new_width * (relx)])
784+
if self.do_zoom_y:
785+
ax.set_ylim([ydata - new_height * (1 - rely), ydata + new_height * (rely)])
654786

655787
ax.figure.canvas.draw_idle()
656788
ax.figure.canvas.flush_events()

0 commit comments

Comments
 (0)