How to fix the length of a line in kivy - python

As you all know kivy Line take points with (x1,y1.x2,y2,x3,y3). I want to create a geometric compass like it is in here. Considering the compass here I created a basic framework of this, which doesn't seems to be good enough.. (though isn't even close to match the compass there but is just the basic idea)
import kivy
from kivy.uix.widget import Widget
from kivy.uix.widget import Canvas
from kivy.graphics import Color
from kivy.graphics import Line
from kivy.uix.floatlayout import FloatLayout
from kivymd.app import MDApp
from kivy.uix.button import Button
from kivymd.uix.card import MDCard
class Main(FloatLayout):
def __init__(self,**kwargs):
super(Main, self).__init__(**kwargs)
self.my_widget = Widget(size_hint= (0.6,0.6),pos_hint = {'x':0.5,'top' : 0.8})
self.add_widget(self.my_widget)
with self.my_widget.canvas:
Color(rgba = (0,0,0,1))
def on_touch_downah(self,touch):
self.line = Line(points = (touch.x,touch.y),width = 4)
self.canvas.add(self.line)
def on_touch_moveah(self,touch):
self.line.points += touch.x,touch.y
self.my_widget.bind(on_touch_down=on_touch_downah)
self.my_widget.bind(on_touch_move=on_touch_moveah)
def on_touch_downeh(self, touch):
self.x_ = touch.x
self.y_ = touch.y
self.lines = Line(points=(touch.x,touch.y),width = 5)
self.canvas.add(self.lines)
def on_touch_moveeh(self, touch):
self.x2 = self.x_ + 0.1
self.y2 = self.y_ + 0.1
self.lines.points = (self.x_,self.y_,touch.x,touch.y,touch.x,touch.y)
self.my_widget.bind(on_touch_down=on_touch_downeh)
self.my_widget.bind(on_touch_move=on_touch_moveeh)
class Mysapp(MDApp):
def build(self):
return Main()
Mysapp().run()
It is just a framework but the only problem here is the length of the straight _line is not getting fixed, meaning it keep changing because of x3,y3. Is there any way to fix the length of this line so that under no circumstances its length would get modified but it should still be able to change its position?
Or is there any better widget for this purpose other than the circle widget? I want the user to be able to create curves and arcs as well as circles therefore can't switch to circle widget. The user should be able to use it just as the geometric compass as you can see the provided link..

Related

Lines are not drawn in Kivy python

This is the code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color
from kivy.graphics import Line
class MainWidget(Widget):
v_l_s=.1
v_l_n=7
Vertical_lines=[]
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.init_vertical_lines()
def on_size(self,*args):
self.update_vertical_lines()
def init_vertical_lines(self):
for num in range(0,self.v_l_n):
self.Vertical_lines.append(Line())
def update_vertical_lines(self):
cener_x=int(self.width/2)
before_lines=-(int(self.width/2))
line_x=cener_x+before_lines*self.v_l_s
for numb in range(0,self.v_l_n):
with self.canvas:
self.Vertical_lines[numb].points=[line_x,0,line_x,self.height]
before_lines+=1
class GameApp(App):
def build(self):
return MainWidget()
GameApp().run()
This code should return 7 vertical lines but it is showing nothing. What should I do to solve it ?(Note-An error is coming in publishing the question so don't care about this Note)
When you initially create the Line() instance, you are not doing in a canvas context. Just add with self.canvas: to the init_vertical_lines() method:
def init_vertical_lines(self):
with self.canvas:
for num in range(0, self.v_l_n):
self.Vertical_lines.append(Line())
Your code will then draw the 7 lines, but they are all in the same place (that's a different issue).

Updating multiple Labels with kivy

Hello i'm relatively new to python and kivy and I've been looking around but i cant quite find the answer, i'm trying to make a hub where I can display live data from my rasberry-pi. I made a clock widget using a Label, and it updates time, but when i try to add my CPU usage only one label shows up. I thought the widgets might be covering each other but I used size_hint and it wont help. When i run the code below, only the timex will show up and only if i delete the Clock.schedule_interval(display32.timex, (1)) will the *updatex * display.
Thanks alot for the help
from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
import psutil
from kivy.lang import Builder
import time
class Display(Label, FloatLayout):
def __init__(self):
super(Display, self).__init__()
def updatex(self, *args):
self.cpu2 = psutil.cpu_percent()
self.text=str(self.cpu2)
self.size_hint=(.5, .25)
self.pos=(500, 500)
self.texture_size=(0.1,0.1)
def timex(self, *args):
self.time2 = time.asctime()
self.text = str(self.time2)
self.size_hint = (.5, .25)
self.pos = (30, 500)
self.size_hint=(0.1,0.1)
class TimeApp(App):
def build(self):
display32 = Display()
Clock.schedule_interval(display32.updatex, (1))
Clock.schedule_interval(display32.timex, (1))
return display32
if __name__ == "__main__":
TimeApp().run()
Instead of passing both labels to the clock, you can define a widget, add both labels and start the clock on the widget.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.clock import Clock
import psutil
import time
class Display(Widget):
def draw(self, *args):
self.clear_widgets()
self.add_widget(Label(text=str(psutil.cpu_percent()), pos=(500, 500)))
self.add_widget(Label(text=str(time.asctime()), pos=(30, 500)))
class TimeApp(App):
def build(self):
display32 = Display()
Clock.schedule_interval(display32.draw, 1)
return display32
if __name__ == '__main__':
TimeApp().run()

Kivy update label dynamically on canvas

I am trying update Label on canvas , but it changed only if I call update() method from MyApp(APP).
And what is interesting that method obj Slider (on touch) works perfectly
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import (Color, Ellipse)
from kivy.uix.slider import Slider
import random
from functools import partial
class Canvas_w(Widget):
def __init__(self, **kwargs):
self.i = 0
super(Canvas_w, self).__init__(**kwargs)
self.label_text =Label(text= "0",color = (255,0,0))
def update_text(self, text):
self.label_text.text = text
def on_touch_down(self, touch):
with self.canvas:
d=30
self.i+=1
Color(1., 0, 0)
Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(20,20))
self.label_text=str(self.i)
class Label_l(GridLayout):
bones = 0
def __init__(self, **kwargs):
super(Label_l, self).__init__(**kwargs)
self.label_text =Label(text= "0")
self.add_widget(self.label_text)
#self.label_text.text=self.C.i
def update_text(self, text):
Label_l.bones=text
self.label_text.text = str(Label_l().bones)#
Label_l.text=str(Label_l().bones)
class WidgetContainer(GridLayout):
sld_value = 0.00001
def __init__(self, **kwargs):
super(WidgetContainer, self).__init__(**kwargs)
self.cols = 1
self.speedControl = Slider(min=0.00001, max=3, step=0.0001)
self.add_widget(Label(text="Speed Iteration"))
self.add_widget(self.speedControl)
self.add_widget(Label(text=''))
self.speedValue = Label(text="0.00001")
self.add_widget(self.speedValue)
self.speedControl.bind(value=self.on_value)
def on_value(self, instance, speed):
self.sld_value = speed
WidgetContainer.sld_value = self.sld_value
self.speedValue.text = str(round(speed,5))
class MyApp(App):
def build(self):
b3 = BoxLayout(orientation="horizontal")
b1 = FloatLayout()
self.painter = Canvas_w()
self.label_l= Label_l()
b1.add_widget(self.label_l)
b1.add_widget(self.painter)
b1.add_widget(WidgetContainer())
b3.add_widget(b1)
return(b3)
if __name__=="__main__":
MyApp().run()
I am trying update Label on canvas , but it changed only if I call update() method from MyApp(APP).
And what is interesting that method obj Slider (on touch) works perfectly
A few problems with your code:
The color attribute of a Label requires a list of four values with each value in the range 0 to 1. Change self.label_text =Label(text= "0",color = (255,0,0)) to self.label_text =Label(text= "0",color = (1,0,0,1)) in the Canvas_w class.
The above Label is never getting drawn. You need to add self.add_widget(self.label_text) to the __init__() method of Canvas_w.
You have another Label in the Label_l class that is being drawn on top of the Label in the Canvas_w.
After fixing the above problems, I believe your code will work as you expect.
If you call self.canvas.clear(), it appears as though the label_text child of Canvas_w is removed. Actually it is not removed, but is just not visible (perhaps overdrawn). One way to make it visible is to just make sure it is drawn last by removing it and then re-adding it (last added child is drawn last), like this:
self.canvas.clear()
self.remove_widget(self.label_text)
self.add_widget(self.label_text)
After a bit more research, I believe that the canvas.clear() is removing the drawing of the Label from the Canvas. You can, however, do your drawing on canvas.before by using:
with self.canvas.before:
in the on_touch_down() method. And rather than using:
self.canvas.clear()
use:
self.canvas.before.clear()
I believe that your Label will be displayed correctly after doing the above.

Kivy - First Widget of Class not getting color

I have some simple code below that is just creating some rectangles with a color assigned to them, and then storing them into a FloatLayout. For some reason, the very first rectangle 'Brick' that I create, doesnt get a color, but all subsequent ones do. I have issues with my game also where when another widget collides with a brick, it updates the attributes of the brick to its left, and not itself. I think the two issues are related.
What is going on with the first instance of Brick (brick1) that is being added to the FloatLayout that it doesnt get the color created?
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.core.window import Window
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.clock import Clock
from kivy.graphics import Rectangle, Ellipse, Color
from kivy.uix.floatlayout import FloatLayout
from kivy.vector import Vector
from kivy.utils import get_color_from_hex
import random
from kivy.config import Config
Window.size = (300,600)
class Brick(Widget):
def __init__(self, xloc, yloc, **kwargs):
super().__init__(**kwargs)
with self.canvas:
self.size = (25,25)
self.x = xloc
self.y = yloc
self.pos = (self.x,self.y)
self.body = Rectangle(pos=self.pos,size = self.size)
self.c = Color(1,0,1)
class Game(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.brick_container = FloatLayout(size = (25,25))
brick1 = Brick(50,100)
brick2 = Brick(100,100)
self.brick_container.add_widget(brick1)
self.brick_container.add_widget(brick2)
self.add_widget(self.brick_container)
def update(self,dt):
self.name = 'nothing'
class MyApp(App):
def build(self):
game = Game()
Clock.schedule_interval(game.update, 1.0/60.0)
return game
if __name__ == '__main__':
MyApp().run()
You have to set the color first and then the rectangle
class Brick(Widget):
def __init__(self, xloc, yloc, **kwargs):
super().__init__(**kwargs)
self.size = (25,25)
self.pos = (xloc , yloc)
with self.canvas:
self.c = Color(1,0,1)
self.body = Rectangle(pos=self.pos,size = self.size)
According to the docs when using a Drawing Instruction, use the color set above.
Drawing instructions
Drawing instructions range from very simple
ones, like drawing a line or a polygon, to more complex ones, like
meshes or bezier curves:
with self.canvas:
# draw a line using the default color
Line(points=(x1, y1, x2, y2, x3, y3))
# lets draw a semi-transparent red square
Color(1, 0, 0, .5, mode='rgba')
Rectangle(pos=self.pos, size=self.size)
For that reason in your original code the first Brick used the color by default(white), and the others if they had the correct color.

adding widgets in kivy

I am trying to construct a widget in the middle of the screen. I am not using pos_hint or size_hint because I will be altering the widget's position later but when I construct the widget, its size and position is not correct. Here is my code:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.graphics import *
from kivy.clock import Clock
class Sprite(Widget):
def __init__(self, **kwargs):
super(Sprite, self).__init__(**kwargs)
Clock.schedule_interval(self.update_canvas, 1.0/60)
def update_canvas(self, dt):
with self.canvas:
Rectangle(size=self.size, pos=self.pos)
class RootWidget(FloatLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
self.add_widget(Sprite(
size=(Window.width*0.1, Window.height*0.1),
center=(Window.width*0.5, Window.height*0.5)
))
class MyApp(App):
def build(self):
app = RootWidget()
return app
if __name__=="__main__":
MyApp().run()
Why is the widget's size not equal to 1/10th of the window size and why is it's center at the top right corner of the window?
The size hint defaults to a value unless you define it explicitly and it takes priority over the size property. You may not want to want to use the size hint functionality but unless you disable it, the size that you set manually will be overridden by default. Try adding the following modification and compare the result with and without the size_hint line:
self.add_widget(Sprite(
size_hint=(None,None),
size=(Window.width*0.1, Window.height*0.1),
center=(Window.width*0.3, Window.height*0.5)
))
You can see the actual dimensions set by using the inspector: python myscript.py -m inspector and hitting control+E in the kivy window
reference: http://kivy.org/docs/api-kivy.modules.inspector.html
center is an alias for (x + width/.2, y + height/.2) but the width and height default to 100. At the time of creation these default measurements are what are used for the reverse calculation to set x and y when you make your call so if your height and width are different from 100 then you'll be offset by the difference. Use x and y directly (i.e. pos=(..)) when instantiating, not center.

Categories