Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions examples/example_velocity_pan/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""Velocity pan example for kivy_matplotlib_widget


Note:
MatplotFigure is used when you have only 1 axis with lines only.
Velocity pan is managed for xaxis only
Velocity pan may not work on inverted axis

"""


from kivy.utils import platform
from kivy.config import Config

#avoid conflict between mouse provider and touch (very important with touch device)
#no need for android platform
if platform != 'android':
Config.set('input', 'mouse', 'mouse,disable_on_activity')
else:
#for android, we remove mouse input to not get extra touch
Config.remove_option('input', 'mouse')

from kivy.lang import Builder
from kivy.app import App

import matplotlib.pyplot as plt
import numpy as np
import kivy_matplotlib_widget #register all widgets to kivy register

# Fixing random state for reproducibility
np.random.seed(19680801)

dt = 0.01
t = np.arange(0, 10, dt)
nse = np.random.randn(len(t))
r = np.exp(-t / 0.05)

cnse = np.convolve(nse, r) * dt
cnse = cnse[:len(t)]
s = 0.1 * np.sin(2 * np.pi * t) + cnse

KV = '''

Screen
figure_wgt:figure_wgt
BoxLayout:
orientation:'vertical'
BoxLayout:
size_hint_y:0.2
Button:
text:"home"
on_release:app.home()
ToggleButton:
group:'touch_mode'
state:'down'
text:"velocity pan (x axis only)"
on_release:
app.set_touch_mode('pan')
self.state='down'
figure_wgt.velocity_panx=True
ToggleButton:
group:'touch_mode'
text:"normal pan"
on_release:
app.set_touch_mode('pan')
self.state='down'
figure_wgt.velocity_panx=False
ToggleButton:
group:'touch_mode'
text:"zoom box"
on_release:
app.set_touch_mode('zoombox')
self.state='down'
MatplotFigure:
id:figure_wgt
fast_draw:False #recommanded for velocity pan
velocity_panx:True
auto_zoom:True
'''


class Test(App):
lines = []

def build(self):
self.screen=Builder.load_string(KV)
return self.screen

def on_start(self, *args):

#generate the plot
fig, ax1 = plt.subplots(1, 1)
ax1.plot(t,s)
ax1.set_xlim(t[0],t[50])
ax1.set_ylim(min(s)-0.05,max(s)+0.05)

self.screen.figure_wgt.figure = fig

#set x lim (can pan or zoom over these values)
self.screen.figure_wgt.stop_xlimits = [min(t),max(t)]

def set_touch_mode(self,mode):
self.screen.figure_wgt.touch_mode=mode

def home(self):
self.screen.figure_wgt.home()

Test().run()
79 changes: 79 additions & 0 deletions kivy_matplotlib_widget/tools/pan_effects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from time import time
from kivy.effects.kinetic import KineticEffect
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ObjectProperty


class PanEffect(KineticEffect):
'''PanEffect class for velocity pan
'''

drag_threshold = NumericProperty('20sp')
'''Minimum distance to travel before the movement is considered as a drag.

:attr:`drag_threshold` is a :class:`~kivy.properties.NumericProperty` and
defaults to 20sp.
'''

pan = NumericProperty(0)
'''Computed value for panning. This value is different from
:py:attr:`kivy.effects.kinetic.KineticEffect.value`
in that it will return to one of the min/max bounds.

:attr:`pan` is a :class:`~kivy.properties.NumericProperty` and defaults
to 0.
'''

target_widget = ObjectProperty(None, allownone=True, baseclass=Widget)
'''Widget to attach to this effect. Even if this class doesn't make changes
to the `target_widget` by default, subclasses can use it to change the
graphics or apply custom transformations.

:attr:`target_widget` is a :class:`~kivy.properties.ObjectProperty` and
defaults to None.
'''

displacement = NumericProperty(0)
'''Cumulative distance of the movement during the interaction. This is used
to determine if the movement is a drag (more than :attr:`drag_threshold`)
or not.

:attr:`displacement` is a :class:`~kivy.properties.NumericProperty` and
defaults to 0.
'''

def reset(self, pos):
'''(internal) Reset the value and the velocity to the `pos`.
Mostly used when the bounds are checked.
'''
self.value = pos
self.velocity = 0
self.is_manual = True
self.displacement = 0

if (history := self.history):
val = history[-1][1]
history.clear()
history.append((time(), val))

def on_value(self, *args):

if not self.is_manual:
self.target_widget.apply_pan_w_vel(self.value)

def start(self, val, t=None):
self.is_manual = True
self.displacement = 0
return super(PanEffect, self).start(val, t)

def update(self, val, t=None):
self.displacement += abs(val - self.history[-1][1])
return super(PanEffect, self).update(val, t)

def stop(self, val, t=None):
self.is_manual = False
self.displacement += abs(val - self.history[-1][1])
if self.displacement <= self.drag_threshold:
self.velocity = 0
return
return super(PanEffect, self).stop(val, t)
Loading