2013-12-09 78 views
9

我正在製作一個可以旋轉像大表盤一樣的圓。目前,我在頂部有一個箭頭來顯示撥號盤朝向哪個方向。我希望它的行爲類似於一個老式的旋轉式手機,這樣,當你的手指/光標停下時,你可以旋轉它,但它會緩慢地在你放開之後再次上移。在kivy的一個觸摸事件上旋轉一個對象

這裏是我的對象是什麼樣子:

enter image description here

這裏是我的代碼:

#!/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() 

而且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 

零件從借用kivy touchtracer演示,以及from this SO question

你可以看到我有一個正確的打印圓的原點和觸摸事件之間的角度的計算(不知道這將如何響應多個手指,沒有想到這麼遠),但沒有確定如何將其整合到界面中的「旋轉」反饋事件中。

回答

4

您可以將畫布的角度與NumericProperty綁定,以便在代碼中對其進行更改。你所需要做的就是正確計算這些角度。打了一下與它後我創建下面的代碼:在on_touch_move

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

我計算初始差異(按壓後鼠標)和后角。由於角度是一個屬性,我也可以使用kivy.animation來修改它,使撥號在釋放鼠標按鈕後旋轉回來。

編輯

on_touch_down事件爲孩子圓:

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

這可能是超出範圍對於這個問題,但你知道我怎麼能觸摸事件添加到小圈在頂部?我爲它分配了一個on_touch聲音事件,以確保我知道我在做什麼,但似乎我可以有旋轉/或/我可以有兒童圈的on_touch ---我似乎無法同時擁有與此同時。 – Mittenchops

+0

您必須使用'super'進一步調度'on_touch_down'事件。我添加了這個例子。 – Nykakin

+0

這似乎是雙重打印這兩個事件? – Mittenchops

3

您可以使用GearTick從花園,是一個旋轉的滑塊。這不完全是你所需要的,但可以根據你的需要進行調整。 「默認情況下,它允許逆時針旋轉,你可能需要它順時針旋轉」(更新:該部件現在有一個orientation屬性,可以設置爲'順時針'或'逆時針')。

您需要管理回彈並停在「手指停止」位置。

末端示例使用動畫管理彈簧回彈,但您仍需要管理/實現手指停止功能。

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

用法::

的Python ::

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

千伏::

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

要安裝::

pip install kivy-garden 
garden install geartick 

工作的例子,你可以複製粘貼::

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

看起來非常酷,但是當我試圖運行,即使安裝模塊後,我從'''kivy.uix.behaviors導入ButtonBehavior ImportError:沒有模塊命名行爲 ''' – Mittenchops

+0

您需要最新的kivy 。看看這裏的安裝說明http://kivy.org/docs/installation/installation.html#development-version –

+0

嗯,很奇怪,是的,我確實從主人安裝,但仍然沒有運氣。 – Mittenchops