Using KV langaue of Kivy to compute points on a canvas - layout

I want to draw a few triangles in a canvas, which will resize as I resize the window. The code use is as follow
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.uix.textinput import TextInput
from kivy.uix.stacklayout import StackLayout
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
Builder.load_string("""
<ScreenUI>:
radius: 0.9 * min(self.center_x, self.center_y)
tside: 2 * (min(self.center_x, self.center_y) - self.radius / 1.4)
r_width: self.center_x + self.radius
r_x: self.center_x - self.radius
canvas:
Triangle:
points: root.r_x, 0, root.tside, 0, 0, root.tside
Triangle:
points: root.r_width, 0, root.r_x - root.tside, 0, root.r_width, root.tside
""")
class ScreenUI(BoxLayout):
pass
class WidgetApp(App):
def build(self):
app = ScreenUI()
return app
if __name__ == '__main__':
WidgetApp().run()
The errors I get are:
kivy.lang.BuilderException: Parser: File "<inline>", line 13:
...
11: points: root.r_x, 0, root.tside, 0, 0, root.tside
12: Triangle:
>>13: points: root.r_width, 0, root.r_x - root.tside, 0, root.r_width, root.tside
...
BuilderException: Parser: File "<inline>", line 13:
...
11: points: root.r_x, 0, root.tside, 0, 0, root.tside
12: Triangle:
>>13: points: root.r_width, 0, root.r_x - root.tside, 0, root.r_width, root.tside
...
TypeError: unsupported operand type(s) for -: 'NoneType' and 'NoneType'
How can I use just the KV language to compute al the corners of the triangles?

You need to add the properties to the ScreenUI Python class:
class ScreenUI(BoxLayout):
radius = NumericProperty(0)
tside = NumericProperty(0)
r_width = NumericProperty(0)
r_x = NumericProperty(0)
Don't forget to import the NumericProperty:
from kivy.properties import NumericProperty

Related

How to Use Rounded Buttons in Kivy With all Functionalities of Regular Button

How to Add Rounded Buttons in Kivy .
I Want to Make My Buttons Rounded in Kivy I Have Done This By Using Canvas But The Problem is That If I Click on The Buttons There is No Animation Present I The Click
main.kv File
<Button>:
font_size : 32
background_normal : ""
background_color : (1,0,0,1)
<MyLayout>:
BoxLayout:
orientation: 'vertical'
size : root.width, root.height
spacing: 20
padding : 50
Button:
text:'Hello World!'
RoundedButton:
text:'Goodbye World!'
pos_hint : {"center_x":0.5}
size_hint : (1, .3)
<RoundedButton#Button>
background_color : (0,0,0,0)
background_normal : ""
canvas.before:
Color:
rgba : (0, 0, 1, 1)
RoundedRectangle:
size : self.size
pos : self.pos
radius : [58]
main.py file
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.core.window import Window
Builder.load_file("rbuttons.kv")
class MyLayout(Widget):
pass
class RButtonsApp(App):
def build(self):
Window.clearcolor = (1,1,1,1)
return MyLayout()
if __name__ == "__main__":
RButtonsApp().run()
If you are just talking about the color change when you press the RoundedButton, you can just modify the rgba in the <RoundedButton#Button>:
rgba : (0, 0, 1, 1) if self.state == 'normal' else (1, 0, 0, 1)

kivy widget always updating to the size of the parent widget

Using kivy, I am trying to create my own dropdown menu to solve some issues I have with the built in DropDown widget. However, when resizing my widget to animate an opening sequence, the widget size remains the same. Upon further investigation I realised that the widget is always the same size as the parent RelativeLayout but I can't find a reason why. Here is a minimum working code
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.uix.relativelayout import RelativeLayout
from kivy.lang import Builder
import kivy.properties as props
KV = '''
#:import MaterialWidget materialwidget.MaterialWidget
<DropDown#RelativeLayout>
_background: background
canvas:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: root._pos
size: root._size
Button:
id: background
size: root._size
pos: root._pos
on_size: print("size_changed", self.size)
'''
class DropDown(RelativeLayout):
opened = props.BooleanProperty(False)
anchor = props.OptionProperty("tl", options=["tr", "tl", "br", "bl"])
_size = props.ListProperty([0, 0])
_pos = props.ListProperty([0, 0])
_background = props.ObjectProperty(None)
def __init__(self, **kwargs):
super(DropDown, self).__init__(**kwargs)
#self.add_widget(self._background)
trigger = Clock.create_trigger(lambda *args: self.on_pos())
trig = Clock.create_trigger(lambda *args: self.on_size())
self.bind(
anchor=trigger,
_size=trigger,
opened=trig
)
def on_pos(self, *args):
# calculate new pos
invert_x = "r" in self.anchor
invert_y = "t" in self.anchor
self._pos = (
self.pos[0] - self._size[0] if invert_x else 0,
self.pos[1] - self._size[1] if invert_y else 0
)
def on_size(self, *args):
anim = Animation(
_size=self.size if self.opened else (0, 0),
duration=0.5,
transition="in_out_circ"
)
anim.start(self)
def open(self, *args):
# toggle the value of open state
self.opened = not self.opened
Builder.load_string(KV)
if __name__ == "__main__":
from kivy.app import runTouchApp
from kivy.uix.button import Button
from kivy.core.window import Window
class Test(Button):
def __init__(self, **kwargs):
super(Test, self).__init__(**kwargs)
self.size = (100, 100)
self.size_hint = (None, None)
self.dropdown = DropDown(pos=(200, 100), size=(400, 300))
self.add_widget(self.dropdown)
self.bind(on_release=lambda *args: self.dropdown.open(self))
Window.clearcolor = (1, 1, 1, 1)
runTouchApp(Test())
You probably need to set size_hint: None, None on your DropDown. If size_hint is not None, it will over-ride any size setting (including animations of the size property).

How can i add new position to my ball - Kivy

As it can be understood from the code, I want to assign a new position to the ball on the screen when a certain pixel range is clicked, that is, I want to change its current position. I tried this with self.ids.widget.pos and things like that but didn't get any results.
There is also one more thing I want to ask regardless of the subject. While I define this ball on the screen, the ball does not appear in the center of the screen, I adjusted it myself. Normally I use a 300 width and 500 height screen for the application, but I was able to achieve this by giving 175 width and 275 height values to get this ball right in the middle. How can I better adjust this?
I have removed the parts I don't need to shorten the code, but I can add them later if you want.
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen
from kivymd.app import MDApp
from time import sleep, time
from random import randint
from kivy.properties import NumericProperty
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.graphics import Color
Window.size = (300, 500)
helper = """
ScreenManager:
MenuScreen:
ReflexScreen:
MainScreen:
AimScreen:
<Ball>:
canvas:
Ellipse:
pos: self.pos
size: 50,50
<AimWidget>
ball: pong_ball
Ball:
id: pong_ball
center_x: 175
center_y: 275
<AimScreen>
id: aim
name: 'aim'
AimWidget:
id: widget
"""
class Ball(Widget):
pos_x = NumericProperty()
pos_y = NumericProperty()
class AimWidget(Widget):
def update(self, dt):
pass
class AimScreen(Screen):
aim = AimWidget()
Clock.schedule_interval(aim.update, 1.0/60.0)
def on_pre_enter(self, *args):
pass
def on_touch_down(self, touch):
if (touch.x >= 125 and touch.x <= 175) and (touch.y >= 225 and touch.y <= 275):
pass
Here is a modified version of your code:
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen
from kivy.uix.widget import Widget
from kivy.clock import Clock
Window.size = (300, 500)
helper = """
ScreenManager:
# MenuScreen:
# ReflexScreen:
# MainScreen:
AimScreen:
<Ball>:
size_hint: None, None
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<AimWidget>
ball: pong_ball
Ball:
id: pong_ball
center: root.center
<AimScreen>
id: aim
name: 'aim'
AimWidget:
id: widget
"""
class Ball(Widget):
pass
class AimWidget(Widget):
def update(self, dt):
pass
class AimScreen(Screen):
aim = AimWidget()
Clock.schedule_interval(aim.update, 1.0 / 60.0)
def on_pre_enter(self, *args):
pass
def on_touch_down(self, touch):
if (touch.x >= 125 and touch.x <= 175) and (touch.y >= 225 and touch.y <= 275):
ball = self.ids.widget.ball
ball.center = touch.pos
The Ball widget size was the default (100, 100), so the canvas instructions were drawing the circle in the lower left corner of the Ball widget. Added size to make it (50,50), the desired size of the circle.
The canvas instructions for the Ball, now just reference the size and pos of the Ball widget.
The Ball in the AimWidget is initially positioned using center. This works because the Ball is now the same size as the Ellipse.
The on_touch_down() method can now just set the center of the Ball to the touch.pos.

How to use "mesh" in kivy file instead on python file

I am trying to draw a custom shape with kivy using "mesh" in python.
I did some research on this but most of the result is just write the code in the python file
The code from here and here shows the way to construct mesh object in the python file but i found a problem when i try to translate it into kivy file
this is the code in my main file(main.py):
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.graphics import Mesh
from kivy.properties import ObjectProperty
class MainScreen(Screen):
Mesh = ObjectProperty(None)
class TestApp(App):
def build(self):
return Builder.load_file("health.kv")
sample_app = TestApp()
sample_app.run()
and this is the code in my kivy file(test.kv):
<MainScreen>:
name: "main"
Mesh:
vertices: [0, 0, 0, 0, 100, 0, 0, 0, 100, 100, 0, 0]
indices: [0, 1, 2]
The error goes as below:
File "C:\Users\kelv1\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\floatlayout.py", line 135, in add_widget
widget.bind(
AttributeError: 'kivy.graphics.vertex_instructions.Mesh' object has no attribute 'bind'
Why does it happens and how to solve it??
You just forgot to put it into a canvas. And I added the triangle_fan mode, to make it into polygon. Just guessing that is what you want.
Try this:
from kivy.app import App
from kivy.lang import Builder
KV = """
<MainScreen#Screen>:
name: "main"
canvas:
Mesh:
mode: "triangle_fan"
vertices: [0, 0, 0, 0, 100, 0, 0, 0, 100, 100, 0, 0]
indices: [0, 1, 2]
MainScreen:
"""
class MyApp(App):
def build(self):
return Builder.load_string(KV)
MyApp().run()

kivy: FloatLayout widget doesn't update its position/size when i resize the window

In my exercise, the widget "TextInput" doesn't update its position and its size, when i resize the window; instead the widget "Label" updates correctly its size and its position. Why? Can someone help me, please?
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.graphics import Color, Rectangle
class MyWidget(FloatLayout):
def __init__(self, *args):
super(MyWidget, self).__init__(*args)
with self.canvas.before:
Color(255, 255, 255, 1)
self.rect = Rectangle(size=self.size, pos=self.pos)
def update_rect(instance, value):
instance.rect.pos = instance.pos
instance.rect.size = instance.size
self.bind(pos=update_rect, size=update_rect)
self.add_widget(TextInput(text='InsertText', multiline=False, size_hint=(1, 0.04),
pos_hint={'right': 1, 'y': 0.879}, font_size='12sp'))
self.add_widget(Label(text='label example', color=(0,0,255,0.5), pos_hint={'center_x': 0.5, 'center_y': 0.5}))
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()

Resources