I want an image to be displayed in the center of the layout. I could not figure it out. Is there any way to display the image on the center of the layout and be there till the animation is stopped?
Here is my code.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.animation import Animation
from kivy.properties import NumericProperty
Builder.load_string('''
<Loading>:
canvas:
Rotate:
angle: root.angle
origin: self.center
Color:
rgb: .0, 0, -.1
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: .5, 0, 1
Ellipse:
size: 30, 30
pos: 0.5*root.size[0]+25, 0.8*root.size[1]-25
''')
class Loading(FloatLayout):
angle = NumericProperty(0)
def __init__(self, **kwargs):
super(Loading, self).__init__(**kwargs)
anim = Animation(angle = 1009, duration=2.5)
anim += Animation(angle = 1900, duration=2.5)
anim.repeat = True
anim.start(self)
def on_angle(self, item, angle):
if angle == 360:
item.angle = 0
class TestApp(App):
def build(self):
return Loading()
TestApp().run()
I don't think your canvas should be "loose" like that. Place it inside of a Widget, this will let you use Kivy's various layouts to arrange things. It will also let you insert an Image above the widget and without making the image spin around.
A second thing, use rgba so you can specify transparencies for the background. This example below works for me, just replace the Builder string with this:
"""<Loading>:
FloatLayout:
Image:
source: 'logo.png'
Widget:
canvas:
Rotate:
angle: root.angle
origin: self.center
Color:
rgba: 1, 1, 1, 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:
rgba: .5, 0, 1, 1
Ellipse:
size: 30, 30
pos: 0.5*root.size[0]+25, 0.8*root.size[1]-25
Color:
rgba: 0.5, 0, 1"""
I also attached our Departments logo that I used for the example. I leave it to you to tweak sizes and add the appropriate resizing calls.logo
Related
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()
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()
I would like to create a widget with a fixed size and background. It is to be added first in BoxLayout. I would like to draw a line inside this widget so that it is visible only in it and placed in relation to it.
By entering (0,0) the position of the line I mean the beginning of the widget and not the entire application window.
How to achieve this effect?
from random import random
from kivy.app import App
from kivy.graphics import Color, Ellipse, Line
from kivy.uix.button import Button
from kivy.uix.widget import Widget
class CombWidget(Widget):
pass
class MyPaintWidget(Widget):
def __init__(self, **kwargs):
super(MyPaintWidget, self).__init__(**kwargs)
class MyPaintApp(App):
def build(self):
return CombWidget()
if __name__ == '__main__':
MyPaintApp().run()
and kv file
<CombWidget>:
BoxLayout:
orientation: 'vertical'
size: root.size
padding: 20
spacing: 50
MyPaintWidget:
size: 400, 400
size_hint: 400, 400
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
canvas:
Color:
rgba: 0, 0, 0, 1
Line:
points: 0, 0, 200, 200
Button:
text: "Hallo"
Button:
text: "Hallo 1"
Button:
text: "Hallo 2"
Right now I have something like this:
But I would like to get something like this:
I would like to be able to draw only in this widget and provide positions of the drawn elements in relation to it.
You just need to adjust the points of the Line:
canvas:
Color:
rgba: 0, 0, 0, 1
Line:
points: self.x, self.y, self.x + 200, self.y + 200
As shown in the image above I need to code a .kv file in the Kivy GUI so I can show a circle filling with color as a value increase. For 0 value it is empty as value increase circle is fill with color.
How can I do that?
That kind of effect can be done by creating a mask for this you can use Stencil instructions as shown below:
from kivy.app import App
from kivy.lang import Builder
main_widget_kv = '''
BoxLayout:
border: 10
WaterFill:
level: slider.value_normalized
color: 1, 1, 0
Slider:
id: slider
orientation: 'vertical'
value: 50
<WaterFill#Widget>:
level: 0.1
width: self.height
size_hint: None, 1
color: 0, 0, 1
canvas:
StencilPush
Ellipse:
pos: root.pos
size: root.size
StencilUse
Color:
rgb: root.color
Rectangle:
pos: root.pos
size: (root.width, root.level*root.height)
StencilUnUse
StencilPop
'''
class TestApp(App):
def build(self):
return Builder.load_string(main_widget_kv)
if __name__ == '__main__':
TestApp().run()
Update:
The following example shows an example of how to update the height value using a method with the help of Clock:
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.uix.anchorlayout import AnchorLayout
from kivy.lang import Builder
Builder.load_string("""
<WaterFill>:
level: 0.0
width: self.height
size_hint: None, 1
color: 0, 0, 1
canvas:
StencilPush
Ellipse:
pos: root.pos
size: root.size
StencilUse
Color:
rgb: root.color
Rectangle:
pos: root.pos
size: (root.width, root.level*root.height)
StencilUnUse
StencilPop
""")
class WaterFill(Widget):
def __init__(self, *args, **kwargs):
Widget.__init__(self, *args, **kwargs)
self.delta = 0.01
Clock.schedule_interval(self.on_timeout, 0.05)
def on_timeout(self, *args):
self.level += self.delta
if self.level >= 1:
self.delta = -0.01
elif self.level <= 0:
self.delta = 0.01
class TestApp(App):
def build(self):
lay = AnchorLayout(anchor_x='center', anchor_y='center')
lay.add_widget(WaterFill())
return lay
if __name__ == "__main__":
TestApp().run()
I'm just starting to use Kivy and want to place an ellipse in the centre of of a canvas. Currently I'm using the code below, however only the bottom left 'corner' of the ellipse is at the centre, as the position seems to be determined by the bottom left of each shape.
How can I centre of the canvas in KV language? Thank you in advance.
<Widget#BoxLayout>:
canvas.before:
Color:
rgb: [0.8, 0.8, 1]
Rectangle:
pos: self.pos
size: self.size
canvas:
Color:
rgb: [0, 0, 0]
Ellipse
id: 'El'
size: [100, 100]
pos: [self.center_x, self.center_y]
Yes, in kivy the pos always refers to the bottom left corner. Here's a really simple solution:
from kivy.base import runTouchApp
from kivy.lang import Builder
runTouchApp(Builder.load_string("""
BoxLayout:
canvas.before:
Color:
rgb: [0.8, 0.8, 1]
Rectangle:
pos: self.pos
size: self.size
canvas:
Color:
rgb: [0, 0, 0]
Ellipse
id: 'El'
size: [100, 100]
pos: [self.center_x - 100/2, self.center_y - 100/2]
"""))
We just subtract half of the width from the x and half of the height from the y. This is the simplest solution, but this can be done with FloatLayout by using pos_hint, for example.
You can call widget's canvas from kivy language using canvas[.before|.after] member like this.
<MyWidget>:
canvas:
Rectangle:
source: 'mylogo.png'
pos: self.pos
size: self.size
How can I clear the canvas before I put the instructions?
Use Clear:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
kv_string = '''
<MyWidget>:
canvas:
Color:
rgb: 0.1, 0.6, 0.3
Ellipse:
size: self.size
pos: self.pos
Clear
Color:
rgb: 0.6, 0.2, 0.1
Ellipse:
size: self.size
pos: self.center
'''
Builder.load_string(kv_string)
class MyWidget(Widget):
pass
class TestApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
TestApp().run()
In example above only one ellipse will be drawn since first one gets erased with Clear command. You can call it from Python using code like:
class SomeWidget(Widget):
def some_method(self):
self.canvas.clear()
with self.canvas:
# ...
i used
Window.clearcolor = (x,y,z,w)
and it works... so why use canvas?