Python >> Tutoriel Python >  >> Python GUI >> Kivy GUI

Faire pivoter un objet sur un événement tactile dans kivy

Vous pouvez lier l'angle de la toile à NumericProperty , pour le modifier depuis votre code. Tout ce que vous avez à faire est de calculer ces angles correctement. Après avoir joué un peu avec, j'ai créé le code suivant :

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.animation import Animation
from kivy.properties import NumericProperty

import math 

kv = '''
<Dial>:
    canvas:
        Rotate:
            angle: root.angle
            origin: self.center
        Color:
            rgb: 1, 0, 0
        Ellipse:    
            size: min(self.size), min(self.size)
            pos: 0.5*self.size[0] - 0.5*min(self.size), 0.5*self.size[1] - 0.5*min(self.size)
        Color:
            rgb: 0, 0, 0
        Ellipse:    
            size: 50, 50
            pos: 0.5*root.size[0]-25, 0.9*root.size[1]-25
'''
Builder.load_string(kv)

class Dial(Widget):
    angle = NumericProperty(0)

    def on_touch_down(self, touch):
        y = (touch.y - self.center[1])
        x = (touch.x - self.center[0])
        calc = math.degrees(math.atan2(y, x))
        self.prev_angle = calc if calc > 0 else 360+calc
        self.tmp = self.angle

    def on_touch_move(self, touch):
        y = (touch.y - self.center[1])
        x = (touch.x - self.center[0])
        calc = math.degrees(math.atan2(y, x))
        new_angle = calc if calc > 0 else 360+calc

        self.angle = self.tmp + (new_angle-self.prev_angle)%360

    def on_touch_up(self, touch):
        Animation(angle=0).start(self)

class DialApp(App):
    def build(self):
        return Dial()

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

Je calcule la différence entre l'angle initial (après avoir appuyé sur la souris) et l'angle ultérieur en on_touch_move . Puisque l'angle est une propriété, je peux aussi le modifier en utilisant kivy.animation pour faire revenir le cadran après avoir relâché le bouton de la souris.

MODIFIER

on_touch_down événement pour le cercle d'enfants :

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.animation import Animation
from kivy.properties import NumericProperty

import math 

kv = '''
<Dial>:
    circle_id: circle_id
    size: root.size
    pos: 0, 0
    canvas:
        Rotate:
            angle: self.angle
            origin: self.center
        Color:
            rgb: 1, 0, 0
        Ellipse:    
            size: min(self.size), min(self.size)
            pos: 0.5*self.size[0] - 0.5*min(self.size), 0.5*self.size[1] - 0.5*min(self.size)
    Circle:
        id: circle_id   
        size_hint: 0, 0
        size: 50, 50
        pos: 0.5*root.size[0]-25, 0.9*root.size[1]-25
        canvas:
            Color:
                rgb: 0, 1, 0
            Ellipse:    
                size: 50, 50
                pos: self.pos              
'''
Builder.load_string(kv)

class Circle(Widget):
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            print "small circle clicked"

class Dial(Widget):
    angle = NumericProperty(0)

    def on_touch_down(self, touch):
        if not self.circle_id.collide_point(*touch.pos):
            print "big circle clicked"

        y = (touch.y - self.center[1])
        x = (touch.x - self.center[0])
        calc = math.degrees(math.atan2(y, x))
        self.prev_angle = calc if calc > 0 else 360+calc
        self.tmp = self.angle

        return super(Dial, self).on_touch_down(touch) # dispatch touch event futher

    def on_touch_move(self, touch):
        y = (touch.y - self.center[1])
        x = (touch.x - self.center[0])
        calc = math.degrees(math.atan2(y, x))
        new_angle = calc if calc > 0 else 360+calc

        self.angle = self.tmp + (new_angle-self.prev_angle)%360

    def on_touch_up(self, touch):
        Animation(angle=0).start(self)


class DialApp(App):
    def build(self):
        return Dial()

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

Vous pouvez utiliser GearTick du jardin qui est un curseur rotatif. Ce n'est pas exactement ce dont vous avez besoin mais peut être adapté à vos besoins. "Par défaut, il permet la rotation dans le sens inverse des aiguilles d'une montre, vous en auriez probablement besoin pour aller dans le sens des aiguilles d'une montre" (mise à jour :le widget a maintenant un orientation propriété qui peut être définie sur 'sens horaire' ou 'sens anti-horaire').

Vous auriez besoin de gérer le retour élastique et l'arrêt au "doigt d'arrêt".

L'exemple aux extrémités gère le retour élastique à l'aide d'une animation, mais vous devez toujours gérer/implémenter la fonctionnalité d'arrêt du doigt.

https://github.com/kivy-garden/garden.geartick

Utilisation ::

Python ::

from kivy.garden.geartick import GearTick
parent.add_widget(GearTick(range=(0, 100)))

kv ::

BoxLayout:
    orientation: 'vertical'
    GearTick:
        id: gear_tick
        zoom_factor: 1.1
        # uncomment the following to use non default values
        #max: 100
        #background_image: 'background.png'
        #overlay_image: 'gear.png'
        #orientation: 'anti-clockwise'
        on_release:
            Animation.stop_all(self)
            Animation(value=0).start(self)
    Label:
        size_hint: 1, None
        height: '22dp'
        color: 0, 1, 0, 1
        text: ('value: {}').format(gear_tick.value)

Pour installer ::

pip install kivy-garden
garden install geartick

Exemple de travail que vous pouvez copier coller ::

from kivy.lang import Builder
from kivy.app import runTouchApp
from kivy.garden.geartick import GearTick
runTouchApp(Builder.load_string('''
#:import Animation kivy.animation.Animation
GridLayout:
    cols: 2
    canvas.before:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            size: self.size
            pos: self.pos
    BoxLayout:
        orientation: 'vertical'
        GearTick:
            id: gear_tick
            zoom_factor: 1.1
            # uncomment the following to use non default values
            #max: 100
            #background_image: 'background.png'
            #overlay_image: 'gear.png'
            #orientation: 'anti-clockwise'
            on_release:
                Animation.stop_all(self)
                Animation(value=0).start(self)
        Label:
            size_hint: 1, None
            height: '22dp'
            color: 0, 1, 0, 1
            text: ('value: {}').format(gear_tick.value)
'''))

Post précédent