Kivy: Drawing to widget - kivy

I collect experiencies with kivy and I have problem with using widget center. My problem shows next script:
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from functools import partial
from kivy.graphics import Color, Ellipse
class CanvasApp(App):
def draw(self,wid, color):
with wid.canvas:
Color(color[0], color[1], color[2])
Ellipse(size= (100, 100), pos=(wid.center_x, wid.center_y))
return
def painter(self, wid, *largs):
self.draw(wid, (0, 1, 0))
return
def build(self):
wid = Widget()
btn_painter = Button(text='Painter',
on_press=partial(self.painter, wid))
ctrl = BoxLayout(size_hint=(1, None), height=50)
ctrl.add_widget(btn_painter)
root = BoxLayout(orientation='vertical')
root.add_widget(wid)
root.add_widget(ctrl)
self.draw(wid, (1, 0, 0))
return root
if __name__ == '__main__':
CanvasApp().run()
I want to draw circle in the widget center (black rectangle) without using button painter. I don't able obtain actually center coordinates of center. Without using button are available only default center coordinates. How is OK process?
Thank you for answers.

The problem is that the wid size and position are not yet set inside the build() method. So you need to delay the call of draw() a bit. Try replacing the call to draw() with:
Clock.schedule_once(partial(self.draw, wid, (1,0,0)))
You will also need to change the draw() method slightly to handle the dt argument that gets added:
def draw(self,wid, color, *args):

I found solution of my problem. All explains follow script:
from kivy.app import App
from kivy.graphics import Color, Rectangle, Ellipse
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class DrawArea(FloatLayout):
def __init__(self, **kwargs):
# make sure we aren't overriding any important functionality
super(DrawArea, self).__init__(**kwargs)
self.rect1 = None
self.li_item = []
return
def clear_area(self):
for item in self.li_item:
self.canvas.remove(item)
self.li_item.clear()
def draw_rect(self):
with self.canvas:
Color(0, 1, 0, 1) # green; colors range from 0-1 not 0-255
self.rect1 = Rectangle(size=self.size, pos=self.pos)
Color(1, 0, 0, 1) # green; colors range from 0-1 not 0-255
width = self.width/2
height = self.height/2
pos_x = self.center_x -width/2
pos_y = self.center_y - height/2
self.rect2 = Rectangle(size=(width, height), pos=(pos_x, pos_y))
self.li_item.append(self.rect1)
self.li_item.append(self.rect2)
return
def draw_circle(self):
Color(1, 0, 0)
d = 120
with self.canvas:
Color(0, 1, 0)
self.circle1 = Ellipse(size = (d,d), pos = (self.center_x-d/2, self.center_y-d/2))
Color(0, 0, 1)
self.circle2 = Ellipse(size = (d/2,d/2), pos = (self.center_x - self.size[0]/2, self.center_y - self.size[1]/2))
self.li_item.append(self.circle1)
self.li_item.append(self.circle2)
return
class MainApp(App):
def build(self):
self.task = 'circle'
root = BoxLayout(orientation = 'vertical')
self.dra = DrawArea()
button = Button(text = 'painter',size_hint=(1, None), height=50, on_press = self.but_paint)
root.add_widget(self.dra)
root.add_widget(button)
if self.task == 'circle':
self.dra.bind(pos = self._update_circ, size = self._update_circ)
self.dra.draw_circle()
else:
self.dra.bind(size=self._update_rect, pos=self._update_rect)
self.dra.draw_rect()
return root
def but_paint(self, instance):
#self.dra.clear_area()
if self.task == 'circle':
self.task = 'rectangle'
else:
self.task = 'circle'
if self.task == 'circle':
self.dra.bind(pos = self._update_circ, size = self._update_circ)
self.dra.draw_circle()
else:
self.dra.bind(size=self._update_rect, pos=self._update_rect)
self.dra.draw_rect()
return
def _update_circ(self, instance, value):
d = 120
pos1_x = instance.center[0] -d/2
pos1_y = instance.center[1] -d/2
self.dra.circle1.pos = (pos1_x, pos1_y)
pos2_x = instance.center[0] - instance.size[0]/2
pos2_y = instance.center[1] - instance.size[1]/2
self.dra.circle2.pos = (pos2_x, pos2_y)
return
def _update_rect(self, instance, value):
if self.dra.rect1 != None:
self.dra.rect1.pos = instance.pos
self.dra.rect1.size = instance.size
width = instance.size[0]/2
height = instance.size[1]/2
pos_x = instance.center[0] -width/2
pos_y = instance.center[1] - height/2
self.dra.rect2.pos = (pos_x, pos_y)
self.dra.rect2.size = (width, height)
def draw_rect(self):
with self.dra.canvas.before:
Color(0, 1, 0, 1) # green; colors range from 0-1 not 0-255
self.rect1 = Rectangle(size=self.dra.size, pos=self.dra.pos)
Color(1, 0, 0, 1) # green; colors range from 0-1 not 0-255
width = self.width/2
height = self.height/2
self.rect2 = Rectangle(size=(width, height), pos=(self.center_x -width/2, self.center_y - height/2))
return
if __name__ == '__main__':
MainApp().run()

Related

Can't auto-resize layout & image added to it when resizing window, why? How to limit draggable image to layout area? All in Kivy

The dream goal is to make a chessboard where I can mouse-drag pieces and have the whole board with pieces auto-resize when the app window is user-resized manually with the mouse.
I don't get why the FloatLayout is not resizing & why the Image isn't either. Additionally, I'd like to know if there's a built-in way to limit movement of the image so it doesn't exceed layout boundaries.
Here is the code I have thus far:
Config.set('graphics', 'resizable', True)
Config.write()
kivy.require('2.0.0')
class DraggableArea(FloatLayout):
def __init__(self, **kwargs):
super(DraggableArea, self).__init__(**kwargs)
self.size_hint = (None, None)
self.orientation = "vertical"
with self.canvas.before:
Color(0, 1, 0, 1) # green; colors range from 0-1 instead of 0-255
self.rect = Rectangle(size=(200, 200),
pos=(300, 200))
self.add_widget(MoveableImage(source="./data/images/chess-pieces/WhiteQueen.png", pos=(350, 250), ))
class MoveableImage(DragBehavior, Image):
def __init__(self, **kwargs):
super(MoveableImage, self).__init__(**kwargs)
self.drag_timeout = 10000000
self.drag_distance = 0
self.drag_rectangle = [self.x, self.y, self.width, self.height]
self.size_hint = (self.image_ratio, 1)
self.keep_ratio = True
self.allow_stretch = True
def on_pos(self, *args):
self.drag_rectangle = [self.x, self.y, self.width, self.height]
def on_size(self, *args):
self.drag_rectangle = [self.x, self.y, self.width, self.height]
class egGameApp(App):
def build(self):
c = DraggableArea()
print("layout size = ", c.size)
print("layout position = ", c.pos)
return c
if __name__ == '__main__':
egGameApp().run()

Image in canvas with opacity using kivy module

I can see the effects from the CanvasWidget() class but not any effects from the MyApp() class. Why is it so
#Importing kivy module
import kivy
kivy.require("1.10.1")
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
from kivy.uix.image import Image
from kivy.config import Config
#Creating the canvas
class CanvasWidget(Widget):
def __init__(self, **kwargs):
super(CanvasWidget, self).__init__(**kwargs)
with self.canvas:
Color(1, 1, 1, 1)
self.rect = Rectangle(pos = self.center, size = (self.width / 2., self.height / 2.))
self.bind(pos = self.update_rect, size = self.update_rect)
def update_rect(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
class CanvasApp(App):
def build(self):
return CanvasWidget()
CanvasApp().run()
#Putting the image
Config.set("graphics", "resizable", True)
class MyApp(App):
def build(self):
self.img = Image(source = "voithos.jpg")
self.img.allow_stretch = True
self.img.keep_ratio = False
self.img.size_hint_x = 1
self.img.size_hint_y = 1
self.img.pos = (200, 100)
self.img.opacity = 1
s = Widget()
s.add_widget(self.img)
return s
MyApp().run()
This is the output i get
The line:
CanvasApp().run()
will not return until the CanvasApp is shut down. So nothing after that line will be executed until you kill CanvasApp.

KIVY zindex dynamic change

I am trying to add 4 boxes in Relativelayout. Able to horizontally move only the box which gets added last in relativelayout. All other box gets strucked up when it is moved closer to its box which got added later. In given sample code below 'Actor 400' freely moves horizontally across the scene. 'Actor 300' able to move freely over 'Actor 200' and 'Actor 100'. But when I try to move Actor 300 above Actor 400, it get strucked and only after mouse moves beyond Actor 400, I am able to move.
Can I change the zindex of the widget dynamically when I touch it.
from kivy.app import App
from kivy.graphics import Line, Color
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
KV = '''
<Actor>:
canvas:
Color:
rgba: 0,1,0,.8
Rectangle:
id: _rect_
size: 100, 30
pos: 0, root.height - 30
Line:
points: 50, root.height - 30, 50, 20
width:2
Label:
id: _actr_lbl
text: 'Hello World'
color: 0,0,0,1
size_hint: None, None
size: 100, 30
pos: 0, root.height - 30
'''
Builder.load_string(KV)
class Actor(Scatter):
def __init__(self, **kwargs) :
Name = kwargs.pop('Name', 'Actor-NoName')
super(Actor, self).__init__(**kwargs)
self.Name = Name
self.ids._actr_lbl.text = Name
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.selected = True
self.pos_hint = {'top':1}
self.hint_diff_touch_to_x = self.right - touch.x
return True
return super(Actor, self).on_touch_down(touch)
def on_touch_move(self, touch):
if (self.selected) :
future_right = touch.x + self.hint_diff_touch_to_x
if (future_right <= self.parent.size[0]) and ((future_right - self.width) >= 0) :
self.right = future_right
self.pos_hint = {'top':1}
return True
return super(Actor, self).on_touch_move(touch)
def on_touch_up(self, touch):
if (self.selected) :
self.selected = False
self.hint_diff_touch_to_x = 0
self.iy = 0
return True
return super(Actor, self).on_touch_up(touch)
class MyPaintApp(App):
def build(self):
root = RelativeLayout()
(ix, iy) = (100,100)
clr = Color(0.2, 0.2, 1)
for ix in [100, 200, 300, 400 ] :
root.add_widget(Actor(Name = 'Actor ' + str(ix), pos=(ix,0), size_hint=(None,1)))
return root
if __name__ == '__main__':
Window.clearcolor = (1, 1, 1, 1)
MyPaintApp().run()
When one of your Actor Widgets is selected, you can move it to the top by removing it and re-adding it, like this:
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.selected = True
self.pos_hint = {'top':1}
self.hint_diff_touch_to_x = self.right - touch.x
parent = self.parent
# move this Actor to the front
parent.remove_widget(self)
parent.add_widget(self)
return True
return super(Actor, self).on_touch_down(touch)

Kivy Placing a FloatLayout inside of a BoxLayout

I'm trying to place a Float Layout inside a Boxlayout. When I try this the labels inside get stacked on each other. What am I doing wrong?
Thank you!
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
def add_entry(bl):
fl = FloatLayout()
# add label left
_lbl = Label()
_lbl.id = '_lbl0'
_lbl.text = 'LEFT'
_lbl.pos_hint = {'x': 0, 'center_y': .5}
fl.add_widget(_lbl)
# add label center
_lbl1 = Label()
_lbl1.id = '_lbl1'
_lbl1.text = 'CENTER'
_lbl1.pos_hint = {'center_x': .5, 'center_y': .5}
fl.add_widget(_lbl1)
# add label right
_lbl2 = Label()
_lbl2.id = '_lbl2'
_lbl2.text = 'RIGHT'
_lbl2.pos_hint = {'right': 1, 'center_y': .5}
fl.add_widget(_lbl2)
bl.add_widget(fl)
class MyApp(App):
def build(self):
bl = BoxLayout()
bl.orientation = 'vertical'
for g in range(3):
add_entry(bl)
return bl
if __name__ == '__main__':
MyApp().run()
I think the reason is somehow the size of the FloatLayout. It seems to have size 0: this would explain, why the labels are above each other.
That's how I want it to look like: 
That's how it appears:
You just need to add a size_hint_x to each Label. Something like:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
def add_entry(bl):
fl = FloatLayout()
# add label left
_lbl = Label()
_lbl.id = '_lbl0'
_lbl.text = 'LEFT'
_lbl.size_hint_x = 0.3
_lbl.pos_hint = {'x': 0, 'center_y': .5}
fl.add_widget(_lbl)
# add label center
_lbl1 = Label()
_lbl1.id = '_lbl1'
_lbl1.text = 'CENTER'
_lbl1.size_hint_x = 0.3
_lbl1.pos_hint = {'center_x': .5, 'center_y': .5}
fl.add_widget(_lbl1)
# add label right
_lbl2 = Label()
_lbl2.id = '_lbl2'
_lbl2.text = 'RIGHT'
_lbl2.size_hint_x = 0.3
_lbl2.pos_hint = {'right': 1, 'center_y': .5}
fl.add_widget(_lbl2)
bl.add_widget(fl)
class MyApp(App):
def build(self):
bl = BoxLayout()
bl.orientation = 'vertical'
for g in range(3):
add_entry(bl)
return bl
if __name__ == '__main__':
MyApp().run()
The default size_hint is (1, 1), so each Label tries to fill the entire width of the FloatLayout. By setting size_hint_x to 0.3, each Label takes up only about a third of the width.

Increase the linewidth of a MeshLinePlot in Kivy?

Does anyone know how to increase the linewidth of a MeshLinePlot in Kivy?
Thank you
Update
I got the answer from #Ikolim to work modifying the LinePLot function from kivy.graph
class LinePlot(Plot):
'''LinePlot draws using a standard Line object.
'''
'''Args:
line_width (float) - the width of the graph line
'''
def __init__(self, line_width=1, **kwargs):
self._line_width = line_width # instead of kwargs.get('line_width', 1)
super(LinePlot, self).__init__(**kwargs)
Base on the method __init__ for Kivy garden.graph at https://github.com/kivy-garden/garden.graph/blob/master/init.py, MeshLinePlot currently does not support line width. But LinePlot do support line width.
snippet
self.plot = LinePlot(line_width=4, color=[1, 0, 0, 1])
Example
main.py
import kivy
kivy.require("1.10.0")
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.garden.graph import Graph, MeshLinePlot, MeshStemPlot, LinePlot, SmoothLinePlot, ContourPlot
import matplotlib.pyplot as plt
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvas
from kivy.utils import get_color_from_hex as rgb
from numpy import sin
kv = """
<GraphCustom>:
<Test>:
orientation: 'vertical'
GraphCustom:
size: self.parent.size
pos: self.parent.pos
"""
Builder.load_string(kv)
class GraphCustom(Graph):
def __init__(self, **kwargs):
super(GraphCustom, self).__init__(**kwargs)
self.label_options = {'color': rgb('#FF0000'), 'bold': True}
self.background_color = rgb('f8f8f2')
self.tick_color = rgb('808080')
self.border_color = rgb('808080')
self.xlabel = 'X'
self.ylabel = 'Y'
self.x_ticks_minor = 5
self.x_ticks_major = 25
self.y_ticks_major = 1
self.y_grid_label = True
self.x_grid_label = True
self.padding = 5
self.x_grid = True
self.y_grid = True
self.xmin = -0
self.xmax = 100
self.ymin = -1
self.ymax = 1
self.stop = False
self.plot = LinePlot(line_width=4, color=[1, 0, 0, 1])
self.plot.points = [(x, sin(x / 10.)) for x in range(0, 101)]
self.add_plot(self.plot)
class Test(BoxLayout):
def __init__(self, *args, **kwargs):
super(Test, self).__init__(*args, **kwargs)
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
wid = FigureCanvas(fig1)
self.add_widget(wid)
class TestApp(App):
title = "Kivy Garden Graph - LinePlot Demo"
def build(self):
return Test()
if __name__ == '__main__':
TestApp().run()
Output

Resources