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?
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'm trying to have a white background in window and I'm using Kivy modules and Kv language but the background still black.
main.py :
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
class MyUI(FloatLayout):
pass
class MyApp(App):
def build(self):
return MyUI()
def main():
MyApp().run()
if __name__ == '__main__':
main()
my.kv :
<MyUI>:
canvas:
Color:
rgba: 1,1,1,1
Color instructions are applied to shapes that you draw, e.g. if you write the following:
<MyUI>:
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
now you will see a white Rectangle filling the screen (as that's what its pos and size are set to do).
The animation I currently have for opening and closing a button, is simply a sub button comes out from the main button and expands using size_hint. This animation simply enlarges the sublayout. I would like to 'unveil' the sublayout instead. What I mean by that, is to not mess with size_hint of the sub and instead of expanding itself, I want it to 'unveil' itself which makes for a much cleaner animation. Heres a link of a button animation that uses the same effect. https://codemyui.com/button-to-a-list-animation/.
In the linked animation, the bottom border goes down and reveals the list underneath, the same effect I want.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import *
from kivy.animation import Animation
from kivy.lang.builder import Builder
from kivy.uix.behaviors import *
from kivy.properties import *
Builder.load_string("""
<Root>
Button1:
size_hint: 0.2,0.1
pos_hint: {'top':0.9,'center_x':0.5}
canvas:
Color:
rgba: [0.4,0.4,0.4,1]
Rectangle
size: self.size
pos: self.pos
SubLayout:
pos_hint: {'top':1,'center_x':0.5}
canvas:
Color:
rgba: [0.7,0.7,0.7,1]
Rectangle
size: self.size
pos: self.pos
Color:
rgba: [1,1,1,1]
Ellipse:
pos: self.pos
size: self.size
""")
class Root(FloatLayout):
pass
class Button1(ToggleButtonBehavior, FloatLayout):
def __init__(self,**kwargs):
super(Button1,self).__init__(**kwargs)
def on_state(self, *args):
if self.state == 'down':
anim = Animation(pos_hint={'top':0},d=0.2)
anim += Animation(size_hint=(2,2),d=0.2) #: expanding sublayout, how do I unveil instead?
anim.start(self.children[0])
else:
anim = Animation(size_hint=(1,1),d=0.2)
anim += Animation(pos_hint={'top':1},d=0.2)
anim.start(self.children[0])
class SubLayout(FloatLayout):
pass
class TestApp(App):
def build(self):
return Root()
if __name__ == '__main__':
TestApp().run()
As you can see, SubLayout expands and you can see the ellipse inside expanding with the button. What I would like is for the borders to move away, revealing whats underneath them while leaving the ellipse's size the same, which is the 'unveiling' effect. Any ideas on how to do this would be appreciated. Maybe something to do with an Fbo?
This is the .kv string:
Builder.load_string('''
<Paint>:
Button:
text: 'clear'
on_release: app.instance.cleaner()
''')
This is the class
class Paint(Widget):
def cleaner(self, *args):
self.canvas.clear()
print('Cleared')
The print('Cleared') prints perfectly fine when the button is clicked,but self.canvas.clear() doesn't do anything
class MainApppp(App):
def build(self):
self.instance = Paint()
return Background()
In the following example it illustrates using canvas.clear() function to erase a widget.
Example
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
kv_string = '''
<Paint>:
Button:
text: 'clear'
on_release:
# root.cleaner()
app.root.erase()
<MyWidget>:
canvas:
Color:
rgb: 0.1, 0.6, 0.3
Ellipse:
size: self.size
pos: self.pos
Color:
rgb: 0.6, 0.2, 0.1
Ellipse:
size: self.size
pos: self.center
<Background>:
MyWidget:
id: mywidget
Paint:
id: btn
'''
Builder.load_string(kv_string)
class Paint(Widget):
def cleaner(self, *args):
self.canvas.clear()
print('Cleared')
class MyWidget(Widget):
pass
class Background(BoxLayout):
def erase(self):
self.ids.mywidget.canvas.clear()
class TestApp(App):
title = "Kivy Widget's canvas.clear() Demo"
def build(self):
return Background()
if __name__ == '__main__':
TestApp().run()
Output
I'm trying to copy this gif, which is done in Kivy (here's the link to the full page )
Just as I started, I noticed a black screen between transition (link to what it looks like so you don't have to copy-paste and run)
Why does that black screen appear?
EDIT: I must work without buttons.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
# Create both screens. Please note the root.manager.current: this is how
# you can control the ScreenManager from kv. Each screen has by default a
# property manager that gives you the instance of the ScreenManager used.
Builder.load_string("""
<MenuScreen>:
canvas.before:
Color:
rgba: 122,255,0,2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'hello'
<SettingsScreen>:
canvas.before:
Color:
rgba: 0,255,0,2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'hello'
""")
# Declare both screens
class MenuScreen(Screen):
def on_touch_down(self, touch):
sm.current = 'settings'
class SettingsScreen(Screen):
def on_touch_down(self, touch):
sm.current = 'menu'
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
EDIT: I've tried this but still not working
<sm>:
canvas:
Color:
rgb: (0, 255, 255)
Rectangle:
size: self.size
pos: self.pos
You are not supposed to use the Screen subclasses directly. Instead you must add a component first (e.g. Button or Layout), for example use RelativeLayout:
Builder.load_string("""
<MenuScreen>:
RelativeLayout:
canvas.before:
Color:
rgba: 122,255,0,2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'hello'
<SettingsScreen>:
RelativeLayout:
canvas.before:
Color:
rgba: 0,255,0,2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'hello'
""")
That black area is a canvas of the screen manager. If you don't like it black, then you can paint it, just like you did with screens; or change transition type to NoTransition to hide it.
Also, you should consider building your screen manager inside that kv lang string.
Old, but in case anyone runs into this issue:
To clarify the vague responses in the comments, you need to paint your screen manager the same way you would paint a screen.
Example in kvlang:
ScreenManagement:
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
size: self.size
pos: self.pos
id: screen_manager
transition: NoTransition()
Screen1:
Screen2:
Settings_: