Reputation: 1
I am totally new to Kivy, I google many articles for "floating rectangles", I need this rectangles
I test "Scatter" layout, but it seems that "re-scale" behaviour not easy to use by Mouse click.
so, I would like to see any example(s) similar with I mentioned above. many thanks in advance!!!!
Upvotes: 0
Views: 937
Reputation: 319
Luckily I faced the exact same problem.
Here is the code to do so. It's actually self-explanatory + interesting lines are commented. Of course, you can ask me any question! Note: If you want to see changes in realtime move the code from on_touch_up
to on_touch_move
(at least most parts of it).
from kivy.core.window import Window
Window.clearcolor = (1, 1, 1, 1)
Window.size = (500, 500)
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.uix.scatterlayout import ScatterLayout
from kivy.graphics.transformation import Matrix
from kivy.graphics import Color, Line
class BaseScatter(ScatterLayout):
def __init__(self, **kwargs):
super(BaseScatter, self).__init__(**kwargs)
self.corner = None
self.start_pos = [0, 0]
def on_kv_post(self, base_widget):
# makes rect visible
with self.canvas.before:
Color(0, 0, 0, 1)
self.l = Line(rectangle=(0, 0, self.width, self.height))
def on_touch_down(self, touch):
x, y = self.to_local(*touch.pos)
self.start_pos = x, y
# check which corner is dragged
# 20 is basically the width/height if the draggable area
if 0 <= x <= 20 and 0 <= y <= 20:
self.corner = "bottomleft"
elif 0 <= x <= 20 and self.height - 20 <= y <= self.height:
self.corner = 'topleft'
elif self.width - 20 <= x <= self.width and self.height - 20 <= y <= self.height:
self.corner = 'topright'
elif self.width - 20 <= x <= self.width and 0 <= y <= 20:
self.corner = 'bottomright'
else:
self.corner = 'drag'
def on_touch_up(self, touch):
# transform touch to local space
x, y = self.to_local(*touch.pos)
# calc mouse rel
relx = self.start_pos[0] - x
rely = self.start_pos[1] - y
# resize depending on the corner pressed
# this does not work with rotation yet since the rel value does not take that into account
# ill update that maybe soon
if self.corner == 'bottomleft':
# apply size changes
self.size[0] += relx
self.size[1] += rely
# repos widget (since size changes increase in x and y direction)
self.x -= relx
self.y -= rely
elif self.corner == 'topleft':
# apply size changes
self.size[0] += relx
self.size[1] -= rely # reverse y value
# repos widget (since size changes increase in x and y direction)
self.x -= relx
# y is not needed since we're not moving downwards
elif self.corner == 'topright':
# apply size changes
# we need to reverse both
self.size[0] -= relx
self.size[1] -= rely
# repos widget (since size changes increase in x and y direction)
# we don't need that since we expand our rect in the xy direction
elif self.corner == 'bottomright':
# apply size changes
self.size[0] -= relx # reverse x
self.size[1] += rely
# repos widget (since size changes increase in x and y direction)
# we don't need x
self.y -= rely
else:
# here goes the drag and drop
self.x -= relx
self.y -= rely
# update rectangle
self.l.rectangle = (0, 0, self.width, self.height)
# reset self.corner
self.corner = None
class TestApp(App):
def build(self):
root = FloatLayout()
# i suppose this should be done separately
base = BaseScatter(size=(200, 200), size_hint=(None, None), pos=(50, 50))
lbl = Label(text="Hello!", color=(0, 0, 0, 1))
base.add_widget(lbl)
root.add_widget(base)
return root
TestApp().run()
BaseScatter
is like the parent rectangle while Label is the child that does nothing but displaying its content.
I hope I didn't misunderstand your question and this helps you out!
Edit: The scatter may break by this approach the values should stay correct never the less
Upvotes: 0
Reputation: 39072
Here is a behavior class that can be used in place of DragBehavior
to provide both drag and resize:
"""
DragAndResize Behavior
=============
The :class:`~dragandresize.DragAndResize`
`mixin <https://en.wikipedia.org/wiki/Mixin>`_ class provides Drag and Resize behavior.
When combined with a widget, dragging in the rectangle defined by the
:attr:`~kivy.uix.behaviors.drag.DragBehavior.drag_rectangle` will drag the
widget, and dragging in the border will resize the widget
Example
-------
The following example creates a draggable and resizable label::
from kivy.uix.label import Label
from kivy.app import App
from kivy.lang import Builder
from dragandresize import DragAndResize
# You could also put the following in your kv file...
kv = '''
<DragAndResizeLabel>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
draw_resize_border: True
resize_border_width: 5
resize_border_color: [0,1,0,1]
FloatLayout:
# Define the root widget
DragAndResizeLabel:
size_hint: 0.25, 0.2
text: 'Drag me'
'''
class DragAndResizeLabel(DragAndResize, Label):
pass
class TestApp(App):
def build(self):
return Builder.load_string(kv)
TestApp().run()
"""
__all__ = ('DragAndResize', )
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.properties import NumericProperty, BooleanProperty, ListProperty
from kivy.uix.behaviors import DragBehavior
Builder.load_string('''
<DragAndResize>:
canvas.after:
Color:
rgba: self.resize_border_color
Line:
cap: 'square'
joint: 'miter'
width: self.resize_border_width
rectangle: \
(self.x + self.resize_border_width, self.y + self.resize_border_width,\
self.width - 2 * self.resize_border_width, self.height - 2 * self.resize_border_width)\
if self.draw_resize_border else (0,0,0,0)
''')
class DragAndResize(DragBehavior):
resize_border_width = NumericProperty(5)
'''
width of the lines used to draw thw border. Note that the border width is actually twice this value.
'''
draw_resize_border = BooleanProperty(True)
'''
if True, the border will be drawn
'''
resize_border_color = ListProperty([1, 0, 0, 1])
'''
The color of the drawn border
'''
def on_touch_down(self, touch):
if not self.collide_point(*touch.pos):
return super(DragAndResize, self).on_touch_up(touch)
delta = self.resize_border_width * 2 # the drawn border is actually twice the specified width
if touch.button == 'left':
edit_type = 'pos'
xx, yy = self.to_widget(*touch.pos, relative=True)
if self.height - yy < delta:
edit_type = 'top'
if self.width - xx < delta:
edit_type = 'ne'
Window.set_system_cursor('crosshair')
elif xx < delta:
edit_type = 'nw'
Window.set_system_cursor('crosshair')
else:
Window.set_system_cursor('size_ns')
elif yy < delta:
edit_type = 'bottom'
if self.width - xx < delta:
edit_type = 'se'
Window.set_system_cursor('crosshair')
elif xx < delta:
edit_type = 'sw'
Window.set_system_cursor('crosshair')
else:
Window.set_system_cursor('size_ns')
elif self.width - xx < delta:
edit_type = 'right'
Window.set_system_cursor('size_we')
elif xx < delta:
edit_type = 'left'
Window.set_system_cursor('size_we')
else:
Window.set_system_cursor('crosshair')
touch.ud['edit_type'] = edit_type
if edit_type != 'pos':
touch.ud['size_node'] = self
return True
return super(DragAndResize, self).on_touch_down(touch)
def do_top_size(self, xx, yy):
if yy > 0:
if self.size_hint_y is None:
self.height = yy
else:
self.size_hint_y = yy / self.parent.height
def do_bottom_size(self, xx, yy):
if self.height - yy > 0:
if self.size_hint_y is None:
self.height -= yy
self.y += yy
else:
self.size_hint_y = (self.height - yy) / self.parent.height
self.y += yy
def do_left_size(self, xx, yy):
if self.width - xx > 0:
if self.size_hint_x is None:
self.width -= xx
self.x += xx
else:
self.size_hint_x = (self.width - xx) / self.parent.width
self.x += xx
def do_right_size(self, xx, yy):
if xx > 0:
if self.size_hint_x is None:
self.width = xx
else:
self.size_hint_x = xx / self.parent.width
def on_touch_move(self, touch):
if 'size_node' in touch.ud.keys():
if touch.ud['size_node'] == self:
xx, yy = self.to_widget(*touch.pos, relative=True)
if touch.ud['edit_type'] == 'top':
self.do_top_size(xx, yy)
elif touch.ud['edit_type'] == 'right':
self.do_right_size(xx, yy)
elif touch.ud['edit_type'] == 'bottom':
self.do_bottom_size(xx, yy)
elif touch.ud['edit_type'] == 'left':
self.do_left_size(xx, yy)
elif touch.ud['edit_type'] == 'ne':
self.do_top_size(xx, yy)
self.do_right_size(xx, yy)
elif touch.ud['edit_type'] == 'se':
self.do_bottom_size(xx, yy)
self.do_right_size(xx, yy)
elif touch.ud['edit_type'] == 'sw':
self.do_bottom_size(xx, yy)
self.do_left_size(xx, yy)
elif touch.ud['edit_type'] == 'nw':
self.do_top_size(xx, yy)
self.do_left_size(xx, yy)
return True
return super(DragAndResize, self).on_touch_move(touch)
def on_touch_up(self, touch):
Window.set_system_cursor('arrow')
return super(DragAndResize, self).on_touch_up(touch)
Upvotes: 1