Reputation: 52
Below I have a code that displays 2 scatter widgets in a kivy screen. If one widget is dragged, the other also drags and vice versa.
What I want is in addition to movement, if one is rotated, the other rotates in the same way. In other words, they are exactly mimicking each other with both rotation and position.
The problem I am facing is that kivy's default position is the bottom left of the widget. So, if the user rotates one widget from some random axis point, the other widget rotates at the bottom left. When introducing rotation while the positions are locked, it becomes all messed up, and I cannot find a way to overcome this bug in order to match both rotation and movement.
Here is code with positions locked.
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
class ScatterWidget(Scatter):
pass
class SquareWidget(Widget):
pass
class WindowManager(ScreenManager):
pass
class GeneralWindow(Screen,RelativeLayout):
start_position_1=(200,200)
start_position_2=(300,400)
def on_touch_move(self, touch):
app=self.manager.get_screen('General')
pos1=app.ids['widget10'].parent.to_parent(*self.pos)
pos2=app.ids['widget20'].parent.to_parent(*self.pos)
mov1=(pos1[0]-self.start_position_1[0],pos1[1]-self.start_position_1[1])
mov2=(pos2[0]-self.start_position_2[0],pos2[1]-self.start_position_2[1])
if self.ids.one.collide_point(*touch.pos):
app.ids['two'].pos=(mov1[0]+self.start_position_2[0],mov1[1]+self.start_position_2[1])
if self.ids.two.collide_point(*touch.pos):
app.ids['one'].pos =(mov2[0]+self.start_position_1[0],mov2[1]+self.start_position_1[1])
KV = Builder.load_string("""
WindowManager:
GeneralWindow:
<GeneralWindow>:
name: 'General'
RelativeLayout:
canvas:
Color:
rgba: 0,0,0,0.3
Rectangle:
size: self.size
pos: self.pos
ScatterWidget:
do_scale: False
id: one
size_hint: None,None
size: widget10.size
rotation: 0
pos: root.start_position_1
SquareWidget:
id: widget10
size: (200,20)
canvas:
Color:
rgba: 1,1,0,0.7
Rectangle:
size: self.size
pos: self.pos
ScatterWidget:
do_scale: False
id: two
size_hint: None,None
size: widget20.size
rotation: 0
pos: root.start_position_2
SquareWidget:
id: widget20
size: (200,20)
canvas:
Color:
rgba: 0,1,0,0.7
Rectangle:
size: self.size
pos: self.pos
""")
class TApp(App):
def build(self):
return KV
if __name__ == '__main__':
TApp().run()
Upvotes: 0
Views: 285
Reputation: 38837
The Scatter
widget has a rotation
property, that performs a rotation about the center of the widget. By using this property, and specifying do_rotation: False
for both of your ScatterWidgets
, I think your problem is simplified. Also, modifying your movement code to work on the center
property of the widgets adds another simplification.
Here is a modified version of your code that does this. In this code, the left button is used to move the widgets and the right button is used to rotate the widgets:
from kivy.config import Config
Config.set('input', 'mouse', 'mouse,disable_multitouch')
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.vector import Vector
class ScatterWidget(Scatter):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and touch.button == 'right':
touch.grab(self)
return True
return super(ScatterWidget, self).on_touch_down(touch)
def on_touch_move(self, touch):
if touch.grab_current is self and touch.button == 'right':
self.rotation = -Vector(1, 0).angle(Vector(touch.pos) - Vector(self.center))
return True
return super(ScatterWidget, self).on_touch_move(touch)
def on_touch_up(self, touch):
if touch.grab_current is self:
touch.ungrab(self)
return super(ScatterWidget, self).on_touch_up(touch)
class SquareWidget(Widget):
pass
class WindowManager(ScreenManager):
pass
class GeneralWindow(Screen, RelativeLayout):
start_center_1 = (200, 200)
start_center_2 = (300, 400)
def on_touch_move(self, touch):
app = self.manager.get_screen('General')
scatter_one = app.ids['one']
scatter_two = app.ids['two']
center1 = scatter_one.center
center2 = scatter_two.center
mov1 = (center1[0] - self.start_center_1[0], center1[1] - self.start_center_1[1])
mov2 = (center2[0] - self.start_center_2[0], center2[1] - self.start_center_2[1])
# convert touch.grab_list of WeakReferences to a grab_list of actual objects
grab_list = []
for wr in touch.grab_list:
grab_list.append(wr())
if scatter_one in grab_list:
if touch.button == 'right':
scatter_two.rotation = scatter_one.rotation
else:
scatter_two.center = (mov1[0] + self.start_center_2[0], mov1[1] + self.start_center_2[1])
elif scatter_two in grab_list:
if touch.button == 'right':
scatter_one.rotation = scatter_two.rotation
else:
scatter_one.center = (mov2[0] + self.start_center_1[0], mov2[1] + self.start_center_1[1])
return super(GeneralWindow, self).on_touch_move(touch)
KV = Builder.load_string("""
WindowManager:
GeneralWindow:
<GeneralWindow>:
name: 'General'
RelativeLayout:
canvas:
Color:
rgba: 0,0,0,0.3
Rectangle:
size: self.size
pos: self.pos
ScatterWidget:
do_scale: False
do_rotation: False
id: one
size_hint: None,None
size: widget10.size
rotation: 0
center: root.start_center_1
SquareWidget:
id: widget10
size: (200,20)
canvas:
Color:
rgba: 1,1,0,0.7
Rectangle:
size: self.size
pos: self.pos
ScatterWidget:
do_scale: False
do_rotation: False
id: two
size_hint: None,None
size: widget20.size
rotation: 0
center: root.start_center_2
SquareWidget:
id: widget20
size: (200,20)
canvas:
Color:
rgba: 0,1,0,0.7
Rectangle:
size: self.size
pos: self.pos
""")
class TApp(App):
def build(self):
return KV
if __name__ == '__main__':
TApp().run()
Upvotes: 1