首页 > 解决方案 > Kivy 小部件坐标与 collide_point 不正确

问题描述

显然这只是我在这里缺少一些知识,但无论如何,我会问我的问题。

我正在尝试在屏幕上制作一个圆形触摸点,以模拟操纵杆。为了实现这一点,我从一个圆形的小部件开始。所以我在一个小部件中画了一个圆圈,并覆盖了Widget.collide_point().

但是在测试中:
(a)我的小部件从不使用右下角的位置提示,并且......
(b)似乎认为它的中心点位置不在屏幕上。

我认为这是在构建期间未最终确定 Widget 坐标的某种问题(因为我假设容器小部件移动它),但我真的不明白如何进一步解决这个问题。

self.center_x[旁白]如果我创建以&为中心的圆圈self.center_y,它会在左下角部分脱离屏幕。我完全不明白。

在容器小部件定位完成后,我尝试使用时钟设置回调以重新调整圆圈,但这也无济于事。

#! /usr/bin/env python3
import kivy
kivy.require('1.9.1')

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Rectangle, Color, Rotate, PushMatrix, PopMatrix, Line, Ellipse
from kivy.core.window import Window
from kivy.clock import Clock

import random

WINDOW_WIDTH, WINDOW_HEIGHT = Window.size

class JoyStick( Widget ):

    def __init__( self, dial_width, **kwargs ):
        super( JoyStick, self ).__init__( **kwargs )
        self.radius     = dial_width // 2
        self.radius_sq  = self.radius * self.radius

        with self.canvas:
            Color( 200, 200, 20 )
            #Ellipse( pos=( self.center_x , self.center_y ), size=(self.radius * 2, self.radius * 2))
            Line( circle=( dial_width, dial_width, self.radius ) )

        self.size_hint  = ( None, None )  
        self.pos_hint   = { 'right':1, 'top':0 } 

    def collide_point( self, x, y ):
        centre_x = self.center_x
        centre_y = self.center_x
        print("JoyStick.collide_point( %d, %d ) -> cx=%d, cy=%d, r=%d" % ( x, y, centre_x, centre_y, self.radius ) )
        # Point-in-Circle Formula: if ((x-centre_x)^2 + (y - centre_y)^2 < radius^2) -> TRUE
        x_minus_cx = x - centre_x
        y_minus_cy = y - centre_y
        result = ( ( x_minus_cx * x_minus_cx ) + ( y_minus_cy * y_minus_cy ) < self.radius_sq ) 
        print("JoyStick.collide_point( %d, %d ) -> %s" % ( x, y, str( result ) ) )
        return result


class Screen( FloatLayout ):
    def __init__(self, **kwargs):
        super( Screen, self).__init__(**kwargs)
        # Controller
        self.joystick = JoyStick( 150 )
        self.add_widget( self.joystick )

    def on_touch_down( self, touch ):
        if ( self.joystick.collide_point( *touch.pos ) ):
            print("Joystick Handled point")

    def update( self, dt ):
        pass


class MainApp( App ):
    def build( self ):
        screen = Screen()
        Clock.schedule_interval( screen.update, 1.0 / 60.0 )
        return screen



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

标准输出 - 在这种情况下,我必须拉伸窗口以实际点击 750x750。

JoyStick.collide_point( 531, 582 ) -> cx=750, cy=750, r=75
JoyStick.collide_point( 531, 582 ) -> False
JoyStick.collide_point( 672, 712 ) -> cx=750, cy=750, r=75
JoyStick.collide_point( 672, 712 ) -> False
JoyStick.collide_point( 737, 721 ) -> cx=750, cy=750, r=75
JoyStick.collide_point( 737, 721 ) -> True
Joystick Handled point

主要.png

标签: pythonkivy

解决方案


只是一些使这项工作的建议。我会把布局放在 kv 中。并on_touch_move改为使用。好吧,至少这就是我期望操纵杆工作的方式,在移动中。
然后有一些错别字,比如centre_y = self.center_x
Well let me just throw the example here。它看起来仍然有点像您正在做的事情,只是添加了一些用于调试而不是打印的标签。

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty

class JoyStick(Widget):
    radius = 70

    def collide_point( self, x, y ):
        result = (x-self.center_x) ** 2 + (y-self.center_y) ** 2 < self.radius ** 2
        return result

class MyLayout(FloatLayout):
    handling = StringProperty("")
    xt = StringProperty("")
    yt = StringProperty("")

    def on_touch_move( self, touch ):
        self.xt, self.yt = str(round(touch.pos[0])), str(round(touch.pos[1]))
        if ( self.js.collide_point( *touch.pos ) ):
            self.handling = "True"
        else:
            self.handling = "False"


KV = """

MyLayout:
    js: js
    JoyStick:
        id: js
        canvas:
            Line:
                circle: root.center_x, root.center_y, self.radius

    Label:
        font_size: "30sp"
        text: root.handling
    BoxLayout:
        orientation: "vertical"
        Label:
            text: "x: {}".format(root.xt)
        Label:
            text: "y: {}".format(root.yt)
"""


class MyApp(App):
    def build(self):
        return Builder.load_string(KV)


MyApp().run()

推荐阅读