2

Hello I want to create zoom effect in graph plot in kivy (i use kivy 1.10 in python 3.6 64bits on windows)

I want to detect mousewheel event in my graph widget, but I couldn't find how to do this.

My code :

import itertools
from math import sin, cos, pi
from random import randrange
from kivy.utils import get_color_from_hex as rgb
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from graph import Graph,MeshLinePlot

from kivy.uix.popup import Popup
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout




class Visu(GridLayout):
    def __init__(self, **kwargs):
        super(Visu, self).__init__(**kwargs)
        self.cols = 2
        self.row = 2

        b = BoxLayout(orientation='vertical', on_press=self.zoom)


        graph = Graph(xlabel='X', ylabel='Y', x_ticks_minor=5,
        x_ticks_major=25, y_ticks_major=1,
        y_grid_label=True, x_grid_label=True, padding=5,
        x_grid=True, y_grid=True, xmin=-0, xmax=50, ymin=-1, ymax=1)


        #graph.add_x_axis(0,10)

        plot1 = MeshLinePlot(color=[1, 0, 0, 1])
        plot1.points = [(x, sin(x / 10.)) for x in range(0, 65000)]
        graph.add_plot(plot1)
        plot2 = MeshLinePlot(color=[1, 0, 0, 1])
        plot2.points = [(x, sin(x / 10.)) for x in range(65000, 120000)]
        graph.add_plot(plot2)

        b.add_widget(graph)
        graph.xmax=1000
        graph.xmax=40

        self.add_widget(b)

    def zoom(self):
        print("if mousewheel i change graph.xmin and graph.xmax")



class MyApp(App):
    def build(self):
        return Visu()


if __name__ == '__main__':
    MyApp().run()

I use this code https://github.com/kivy-garden/garden.graph/blob/master/init.py for creating the graph with kivy, but with this code I can't zoom in my graph.

I want to detect mousewheel and run my function self.zoom

Venkatesh Wadawadagi
  • 2,793
  • 21
  • 34
ilapasle
  • 349
  • 4
  • 16

2 Answers2

3

You have to implement the on_touch_down event, and check if there is a scroll and what type it is through is_mouse_scrolling and button.

class Visu(GridLayout):
    def __init__(self, **kwargs):
        ...

    def on_touch_down(self, touch):
        if touch.is_mouse_scrolling:
            if touch.button == 'scrolldown':
                print('down')
            elif touch.button == 'scrollup':
                print('up')
         GridLayout.on_touch_down(self, touch)

    def zoom(self):
        print("if mousewheel i change graph.xmin and graph.xmax")
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
1

Button Events: on_press or on_release

on_press and on_release events are bindable to Button widget. We can use on_touch_down to simulate on_press event and on_touch_up to simulate on_release event.

Event: on_touch_down

Use on_touch_down event, check for collision of the touch with our widget, and check for button profile. If button profile, check for mouse button click, or mouse wheel scrolling (scrollup or scrolldown). If button clicked, grab touch event, increment touch event counter, and start repeat zoom.

Event: on_touch_up

Use on_touch_up event, check the touch event has been grabbed, ungrab touch event if it was grabbed, decrement touch event counter, and if touch event counter is zero, stop repeat zoom.

Snippets

def on_touch_down(self, touch):
    if self.collide_point(*touch.pos):
        if 'button' in touch.profile:
            if touch.button in ("right", "left"):
                ...
                dist = 1 if touch.button == 'left' else -1
                touch.grab(self)
                self._touch_count += 1
                ...
                return True
            elif touch.is_mouse_scrolling:
                dist = 1 if touch.button == 'scrollup' else -1
                ...
                return True
    return super(..., self).on_touch_down(touch)

def on_touch_up(self, touch):
    if touch.grab_current == self:
        touch.ungrab(self)
        self._touch_count -= 1
        if self._touch_count == 0:
            print("\tanimate to the closest zoom")
        return True
    return super(RootWidget, self).on_touch_up(touch)

Programming Guide » Input management » Touch event basics

By default, touch events are dispatched to all currently displayed widgets. This means widgets receive the touch event whether it occurs within their physical area or not.

In order to provide the maximum flexibility, Kivy dispatches the events to all the widgets and lets them decide how to react to them. If you only want to respond to touch events inside the widget, you simply check for collision.

Motion Event » Profiles

Profile value: button
Description: Mouse button (‘left’, ‘right’, ‘middle’, ‘scrollup’ or ‘scrolldown’). Accessed via the button property.

is_mouse_scrolling
Returns True if the touch is a mousewheel scrolling

Example

main.py

from math import sin
from kivy.garden.graph import Graph, MeshLinePlot

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty


class RootWidget(BoxLayout):
    _touch_count = NumericProperty(0)

    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(**kwargs)

        self.graph = Graph(xlabel='X', ylabel='Y', x_ticks_minor=5,
                           x_ticks_major=25, y_ticks_major=1,
                           y_grid_label=True, x_grid_label=True, padding=5,
                           x_grid=True, y_grid=True, xmin=-0, xmax=100, ymin=-1, ymax=1)

        plot = MeshLinePlot(color=[1, 0, 0, 1])
        plot.points = [(x, sin(x / 10.)) for x in range(0, 101)]
        self.graph.add_plot(plot)
        self.add_widget(self.graph)

    def on_touch_down(self, touch):
        """
        If the touch falls inside of our widget, we check button profile. If mouse button click, we set dist to 1 for
        left mouse button clicked else -1 for right mouse button clicked. If not mouse button click, we check for mouse
        wheel scrolling. If mouse is scrolling, we set dist to 1 if scrollup else -1 for scrolldown. If mouse button 
        clicked or mouse wheel scrolling, we return True, indicating that we have consumed the touch and don’t want it
        to propagate any further.

        Finally, if the touch falls outside our widget, not mouse button clicked (left / right button clicked), or not
        mouse wheel scrolling, we call the original event using super(…) and return the result. This allows the touch 
        event propagation to continue as it would normally have occurred.

        :param touch:
        :return:
"""

        print("\non_touch_down:")

        if self.collide_point(*touch.pos):
            if 'button' in touch.profile:
                if touch.button in ("right", "left"):
                    print("\t", touch.button, "mouse clicked")
                    print("\ttouch.pos =", touch.pos)
                    dist = 1 if touch.button == 'left' else -1
                    self.zoom(dist)
                    return True
                elif touch.is_mouse_scrolling:
                    print("\tmouse wheel", touch.button)
                    print("\ttouch.pos =", touch.pos)
                    dist = 1 if touch.button == 'scrollup' else -1
                    self.zoom(dist)
                    return True
        return super(RootWidget, self).on_touch_down(touch)

    def on_touch_up(self, touch):
        print("\non_touch_up:")
        if touch.grab_current == self:
            print("\ttouch.button is", touch.button)
            print("\ttouch.pos is", touch.pos)
            touch.ungrab(self)
            self._touch_count -= 1
            if self._touch_count == 0:
                # TODO
                print("\tanimate to the closest zoom")
            return True
        return super(RootWidget, self).on_touch_up(touch)

    def zoom(self, dist):
        self.graph.xmax += dist * 10
        self.graph.xmin += dist


class GraphDemo(App):

    def build(self):
        return RootWidget()


if __name__ == "__main__":
    GraphDemo().run()

Output

Img01 - AppStartup Img02 - Left Mouse Button Clicked Img03 - Right Mouse Button Clicked Img04 - Mouse Wheel ScrollDown Img05 - Mouse Wheel ScrollUp

ikolim
  • 15,721
  • 2
  • 19
  • 29
  • thnak you for your help, your solution work ! i foudn the documentation for mouse event https://kivy.org/docs/api-kivy.input.motionevent.html With zoom, i want too move in my graph when i press left button mouse, but i don't want found event for "while left button of mouse is pressed" i have this code : if touch.is_touch: if touch.button=="left": print(touch.x) print(touch.y) i detect left click but not "while left click is pressed" – ilapasle Apr 19 '18 at 11:04
  • *on_press* and *on_release* events are only bindable to *Button* widget. Therefore, we cannot detect for *on_press* event from a *BoxLayout*. We will use the *on_touch_down* event to detect for *mouse button clicks* to simulate the *on_press* event. I have updated my post with detection for mouse button clicks. – ikolim Apr 19 '18 at 15:46
  • thanks for your help, I misspoke about my problem I can detect a left or right click, but I would like to detect the event as while the left click is maintained repeat zoom function – ilapasle Apr 20 '18 at 08:30
  • Use *on_touch_down* and *on_touch_up* events to detect release of button click. Please refer to my updated post for details. – ikolim Apr 20 '18 at 15:13