I am trying to plot various line segments using kivy. The line appears really small (at one end of the screen) and I would like to scale it up. Ideally, I would like to specify coordinates from the center of screen and specify width and height so that the line segments appear well. Here is the code I wrote:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.graphics import Color, Ellipse, Line
class MyWidget(Widget):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
with self.canvas:
for obstacle in obstacles:
print obstacle
Line(points=[20, 5, 40, 5],width=1)
pass
# add your instruction for main canvas here
class MotionPlanningApp(App):
def build(self):
root = GridLayout(cols=1, padding=5, spacing=1)
root.add_widget(MyWidget())
return root
if __name__ == '__main__':
MotionPlanningApp().run()
Is there some way to do this in kivy?
You can use Translate instructions to move the line (effectively moving the origin of the canvas) and Scale instructions to stretch it.
For instance, you could have:
from kivy.core.window import Window
with self.canvas:
self.translate = Translate(Window.width / 2, Window.height / 2.)
You would also need to modify the translate instruction later if the window size changed, by binding a function to that.
Regardless, though, the coordinates are in screen pixels so the line is currently small just because you only made it 20 pixels long.
Related
I'm creating an Kivy App for Desktop. I've created most of the app but I want to add a background image to the app. I've not use the KV language but created all the widgets using Python code only.
Can anybody please help me adding a background image in the kivy app using Python.
You can use with canvas: to draw a background image. Here is a simple example:
from kivy.app import App
from kivy.clock import Clock
from kivy.graphics.vertex_instructions import Rectangle
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class TestApp(App):
def build(self):
theRoot = FloatLayout()
# draw the background
with theRoot.canvas:
self.rect = Rectangle(source='background.png')
# use binding to insure that the background stay matched to theRoot
theRoot.bind(on_size=self.update)
theRoot.add_widget(Label(text="Hi", size_hint=(None, None), size=(100, 50), pos=(100,100)))
# need to call update() to get background sized correctly at start
Clock.schedule_once(self.update, -1)
return theRoot
def update(self, *args):
# set the size and position of the background image
self.rect.size = self.root.size
self.rect.pos = self.root.pos
TestApp().run()
I have made an easy app where i try to show my issue. When using python to draw a line in kivy (using the with self.canvas method) the line gets drawn from a center_x and center_y of 50.
Using kivy Lang draws the line correctly in the center. Here is a simple code example:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line
class TestWidget(Widget):
def draw(self):
with self.canvas:
Line(points=[self.center_x,self.center_y,self.center_x,self.center_y+100], width=2)
class TestApp(App):
def build(self):
test = TestWidget()
test.draw()
return test
if __name__ == '__main__':
TestApp().run()
and the corresponding test.kv file:
#:kivy 1.11.1
<TestWidget>:
canvas:
Line:
width: 5
points: (self.center_x,self.center_y,self.center_x,self.center_y+100)
result is like this:
Any idea why using python code is not working?
Both codes are not equivalent, in the case of python you are only establishing that the position of the line is based on the center of the widget at that time, that is, at the beginning, instead in .kv it is indicating that the line position is always it will be with respect to the center of the Widget.
TL; DR;
Explanation:
In kv the binding is a natural and implicit process that in python is not doing it, besides that its implementation is simpler, for example the following code:
Foo:
property_i: other_object.property_j
In that case its equivalent in python is:
foo = ...
other_object = ...
other_object.bind(property_j=foo.setter("property_i"))
And it is not:
foo.property_i = other_object.property_j
Since with the bind you are indicating before a change of property_j update property_i with that value, but in the last code you only indicate at this moment copy the value of property_j in property_i.
In your particular case, the center you take is before displaying the widget and kivy taking default configurations and other properties into consideration, changes the geometry of the widget after it is displayed.
Solution
Making the necessary modifications taking into account the previous explanation the following code is an equivalent of the .kv code
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line
class TestWidget(Widget):
def __init__(self, **kwargs):
super(TestWidget, self).__init__(**kwargs)
with self.canvas:
self.line = Line(width=2)
self.bind(center=lambda *args: self.draw())
def draw(self):
self.line.points = [
self.center_x,
self.center_y,
self.center_x,
self.center_y + 100,
]
class TestApp(App):
def build(self):
test = TestWidget()
return test
if __name__ == "__main__":
TestApp().run()
With kv language your declarations automatically update when their dependencies change. That means that when the widget's centre goes from (50, 50) to something else, the line is redrawn in the new location.
Your Python code doesn't do this, it just draws the line once using whatever its centre is at the moment the code runs. That value is (50, 50), since that's the default before positioning has taken place.
The solution is to write code in Python that updates the line when the widget centre changes, something along the lines of declaring the line with self.line = Line(...) and self.bind(centre=some_function_that_updates_self_dot_line).
I just started making a Kivy program which pretty much just opens a new black window on start. When I right click or click with a scroll wheel on the screen a draggable red circle is added. How can I disable this feature?
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager
from kivy.uix.screenmanager import Screen
from kivy.uix.floatlayout import FloatLayout
class HomeScreen(FloatLayout):
def __init__(self):
super().__init__()
class AppController(App):
def __init__(self):
super().__init__()
self.screen_manager = ScreenManager()
def build(self):
return self.screen_manager
if __name__ == '__main__':
app_controller = AppController()
app_controller.run()
Kivy is made to support multitouch devices too. Therefore it create those circles to tell us where the touch will be simulated and help us simulate multitouch.
See this answer to know how you can disable this: why does right-clicking create an orange dot in the center of the circle?
Or just go to : https://kivy.org/doc/stable/api-kivy.input.providers.mouse.html
I am making a Kivy application right now, and in one part of it, I am getting data as an array of floating point numbers and I want to draw a line in Kivy using the data.
The problem is, I want it to constantly run, so I used threading, but Kivy would not draw the line. here is a stripped down version of the code that illustrates the problem:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line
from threading import Thread
class MyWidget(Widget):
def Draw(self):
with self.canvas:
Line(points=[100, 200, 300, 400])
class MainApp(App):
def build(self):
return MyWidget()
Thread(target=MyWidget().Draw).start()
MainApp().run()
I want this code to draw a line with points 100, 200, 300, 400.
but instead, the app opens and does nothing, help will be appreciated!
I modified your example a bit.
Try start the thread in the init method instead. Because when you do MyWidget().Draw, you do that with a new MyWidget object, and not the one you returned in your build method. So that line will never be drawn. But the line in another widget which is not on the screen.
Try like this:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line, InstructionGroup
from threading import Thread
from random import randint
import time
class MyWidget(Widget):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.ig = InstructionGroup()
self.line = Line(points=[100, 200, 300, 400])
self.ig.add(self.line)
self.canvas.add(self.ig)
Thread(target=self.draw).start()
def draw(self):
while True:
self.line.points = [randint(0,400) for i in range(4)]
time.sleep(0.5)
class MainApp(App):
def build(self):
return MyWidget()
MainApp().run()
I have a FloatLayout as a child of a ScrollView with size_hint_y set to None. I want to be able to extend it as I add more and more content. The problem is that since Kivy's coordinate system starts at the bottom-left, when I add to the FloatLayout height, all the content stays at the bottom. Can I somehow make it extend down? Because I don't think that moving all widgets up is efficient, especially if there's a lot of them and I need to handle the position of all children as well.
Here is a snippet that explains the problematic behaviour:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
class TestApp(App):
def extend_h(self, *args):
global msg_float
msg_float.height += 50
def build(self):
global msg_float
msg_float = FloatLayout(size_hint_y = None)
bt1_main = Button(on_press = self.extend_h)
bl = BoxLayout()
sc = ScrollView()
sc.add_widget(msg_float)
bl.add_widget(sc)
bl.add_widget(bt1_main)
lb = Label(text = "Test",
size=(100,200),
size_hint = (None, None))
msg_float.add_widget(lb)
return bl
TestApp().run()
With a press of a button, the view extends and the "Test" label stays at the bottom, but I'd want it to stay on top.
You could use a relative layout instead of a float layout to fix the coords, but instead you should just omit using any of these, and add labels to a grid layout. Check examples at kivy repo:
https://github.com/kivy/kivy/blob/master/examples/widgets/scrollview.py
https://github.com/kivy/kivy/blob/master/examples/widgets/scrollview.kv