2013-12-09 22 views
9

Pracuję nad utworzeniem koła, które obróci się jak duża tarcza. Obecnie mam strzałkę u góry, aby pokazać, w którym kierunku znajduje się tarcza. Chciałbym, aby jego zachowanie było trochę jak stary czasowy telefon obrotowy, tak, że podczas gdy palec/kursor jest w dół, możesz go obracać, ale po powrocie zwolni (powoli) z powrotem do góry.Obracanie obiektu na imprezie dotykowej w kivy

Oto co mój obiekt wygląda tak:

enter image description here

A oto mój kod:

#!/usr/bin/kivy 
import kivy 
kivy.require('1.7.2') 
import math 

from random import random 
from kivy.app import App 
from kivy.uix.widget import Widget 
from kivy.uix.gridlayout import GridLayout 
from kivy.uix.anchorlayout import AnchorLayout 
from kivy.uix.relativelayout import RelativeLayout 
from kivy.graphics import Color, Ellipse, Rectangle 

class MinimalApp(App): 
    title = 'My App' 
    def build(self): 
     root = RootLayout() 
     return(root) 

class RootLayout(AnchorLayout): 
    pass 

class Circley(RelativeLayout): 
    angle = 0 
    def on_touch_down(self, touch): 
     ud = touch.ud 
     ud['group'] = g = str(touch.uid) 
     return True 
    def on_touch_move(self, touch): 
     ud = touch.ud 
#  print(touch.x, 0) 
#  print(self.center) 
#  print(0, touch.y) 
#  print(touch.x - self.center[0], touch.y - self.center[1]) 
     y = (touch.y - self.center[1]) 
     x = (touch.x - self.center[0]) 
     calc = math.degrees(math.atan2(y,x)) 
     angle = calc if calc > 0 else (360 + calc) 
     print(angle) 
    def on_touch_up(self, touch): 
     touch.ungrab(self) 
     ud = touch.ud 
     return True 

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

I KV:

#:kivy 1.7.2 
#:import kivy kivy 

<RootLayout>: 
    anchor_x: 'center'        # I think this /is/ centered 
    anchor_y: 'center' 
    canvas.before: 
     Color: 
      rgba: 0.4, 0.4, 0.4, 1 
     Rectangle: 
      pos: self.pos 
      size: self.size 
    Circley: 
     anchor_x: 'center'       # this is /not/ centered. 
     anchor_y: 'center' 
     canvas.before: 
      PushMatrix 
      Color: 
       rgba: 0.94, 0.94, 0.94, 1 
      Rotate: 
       angle: self.angle 
       axis: 0, 0, 1 
       origin: self.center 
      Ellipse: 
       source: 'arrow.png' 
       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) 
       Label: 
        text: unicode(self.size) # this is /not/ appearing 
        color: 1,0,0,1 
     canvas.after: 
      PopMatrix 

Części, które są zapożyczone z demonstracja kivy touchtracer i from this SO question.

Możesz zobaczyć, że mam obliczenia, które poprawnie drukują kąt między początkiem koła a zdarzeniem dotykowym (nie wiesz, jak zareaguje na wiele palców, nie pomyślał o tym zbyt daleko), ale nie pewnie, jak zintegrować to ze zdarzeniem sprzężenia zwrotnego "wirującym" w interfejsie.

Odpowiedz

4

Możesz związać kąt obrazu z numerem NumericProperty, aby zmienić go z wnętrza kodu. Wszystko, co musisz zrobić, to poprawnie obliczyć te kąty. Po odtworzeniu trochę z nim stworzyłem poniższy kod:

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() 

jestem obliczania różnicy pomiędzy początkową (po naciśnięciu przycisku myszy), a później w on_touch_move kąt. Ponieważ kąt jest właściwością, mogę również zmodyfikować go za pomocą kivy.animation, aby przywrócić obroty pokrętła po zwolnieniu przycisku myszy.

EDIT

on_touch_down wydarzeniem dla dziecka koła:

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() 
+0

Prawdopodobnie jest poza zakresem na to pytanie, ale czy wiesz, jak mogę dodać dotykowy zdarzenia do wąskiego kręgu na górze? Przypisałem to zdarzenie dźwiękowe on_touch tylko po to, aby upewnić się, że wiem, co robię, ale wydaje mi się, że mogę albo mieć rotację/albo/mogę mieć opcję on_touch dla kółka z dzieckiem --- nie mogę mieć obu w tym samym czasie. – Mittenchops

+0

Musisz wysłać zdarzenie 'on_touch_down' dalej używając' super'. Dodałem przykład. – Nykakin

+0

To wydaje się być podwójnym drukowaniem obu wydarzeń? – Mittenchops

3

Można użyć GearTick z ogrodu, który jest suwak obracania. Nie jest to dokładnie to, czego potrzebujesz, ale może być dostosowane do Twoich potrzeb. "Domyślnie umożliwia obrót w kierunku przeciwnym do ruchu wskazówek zegara, prawdopodobnie będzie to konieczne, aby przejść zgodnie z ruchem wskazówek zegara" (Aktualizacja: widżet ma teraz właściwość orientation, która może być ustawiona na "zgodnie z ruchem wskazówek zegara" lub "przeciwnie do ruchu wskazówek zegara").

Konieczne będzie przywrócenie sprężyny i zatrzymanie się na "zatrzymaniu palca".

Przykład na końcach zarządza powrotem sprężyny za pomocą animacji, jednak nadal trzeba zarządzać/wdrażać funkcję zatrzymania palca.

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

Wykorzystanie ::

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) 

enter image description here

Aby zainstalować ::

pip install kivy-garden 
garden install geartick 

przykład roboczych, które można skopiować pasty ::

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) 
''')) 
+0

Wygląda super fajnie, ale kiedy próbowałem uruchomić, nawet po zainstalowaniu modułu, dostałem '' 'od kivy.uix.behaviors import ButtonBehavior ImportError: Żaden moduł o nazwie zachowania ' '' – Mittenchops

+0

Potrzebujesz najnowszego kivy od mistrza . Zapoznaj się z instrukcjami instalacji tutaj http://kivy.org/docs/installation/installation.html#development-version –

+0

Hmm, dziwne, tak, zainstalowałem od mistrza i wciąż nie mam szczęścia. – Mittenchops