first commit

This commit is contained in:
Yura 2024-09-15 15:12:16 +03:00
commit 417e54da96
5696 changed files with 900003 additions and 0 deletions

View file

@ -0,0 +1,105 @@
'''
Bezier Example
==============
This example shows a closed Bezier curve computed from a polygon. You
should see a purple polygon, a red bezier curve computed from the polygon,
and two sliders. You can drag points on the polygon to recompute the curve.
The two sliders control the dash length of the dashed lines making up the two
shapes.
'''
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.slider import Slider
from kivy.graphics import Color, Bezier, Line
class BezierTest(FloatLayout):
def __init__(self, points=[], loop=False, *args, **kwargs):
super(BezierTest, self).__init__(*args, **kwargs)
self.d = 10 # pixel tolerance when clicking on a point
self.points = points
self.loop = loop
self.current_point = None # index of point being dragged
with self.canvas:
Color(1.0, 0.0, 0.0)
self.bezier = Bezier(
points=self.points,
segments=150,
loop=self.loop,
dash_length=100,
dash_offset=10)
Color(1.0, 0.0, 1.0)
self.line = Line(
points=self.points + self.points[:2],
dash_offset=10,
dash_length=100)
s = Slider(y=0, pos_hint={'x': .3}, size_hint=(.7, None), height=50)
s.bind(value=self._set_bezier_dash_offset)
self.add_widget(s)
s = Slider(y=50, pos_hint={'x': .3}, size_hint=(.7, None), height=50)
s.bind(value=self._set_line_dash_offset)
self.add_widget(s)
def _set_bezier_dash_offset(self, instance, value):
# effect to reduce length while increase offset
self.bezier.dash_length = 100 - value
self.bezier.dash_offset = value
def _set_line_dash_offset(self, instance, value):
# effect to reduce length while increase offset
self.line.dash_length = 100 - value
self.line.dash_offset = value
def on_touch_down(self, touch):
if self.collide_point(touch.pos[0], touch.pos[1]):
for i, p in enumerate(list(zip(self.points[::2],
self.points[1::2]))):
if (abs(touch.pos[0] - self.pos[0] - p[0]) < self.d and
abs(touch.pos[1] - self.pos[1] - p[1]) < self.d):
self.current_point = i + 1
return True
return super(BezierTest, self).on_touch_down(touch)
def on_touch_up(self, touch):
if self.collide_point(touch.pos[0], touch.pos[1]):
if self.current_point:
self.current_point = None
return True
return super(BezierTest, self).on_touch_up(touch)
def on_touch_move(self, touch):
if self.collide_point(touch.pos[0], touch.pos[1]):
c = self.current_point
if c:
self.points[(c - 1) * 2] = touch.pos[0] - self.pos[0]
self.points[(c - 1) * 2 + 1] = touch.pos[1] - self.pos[1]
self.bezier.points = self.points
self.line.points = self.points + self.points[:2]
return True
return super(BezierTest, self).on_touch_move(touch)
class Main(App):
def build(self):
from math import cos, sin, radians
x = y = 150
z = 100
# Pacman !
points = [x, y]
for i in range(45, 360, 45):
i = radians(i)
points.extend([x + cos(i) * z, y + sin(i) * z])
return BezierTest(points=points, loop=True)
if __name__ == '__main__':
Main().run()

View file

@ -0,0 +1,72 @@
'''
Canvas stress
=============
This example tests the performance of our Graphics engine by drawing large
numbers of small squares. You should see a black canvas with buttons and a
label at the bottom. Pressing the buttons adds small colored squares to the
canvas.
'''
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.graphics import Color, Rectangle
from random import random as r
from functools import partial
class StressCanvasApp(App):
def add_rects(self, label, wid, count, *largs):
label.text = str(int(label.text) + count)
with wid.canvas:
for x in range(count):
Color(r(), 1, 1, mode='hsv')
Rectangle(pos=(r() * wid.width + wid.x,
r() * wid.height + wid.y), size=(20, 20))
def double_rects(self, label, wid, *largs):
count = int(label.text)
self.add_rects(label, wid, count, *largs)
def reset_rects(self, label, wid, *largs):
label.text = '0'
wid.canvas.clear()
def build(self):
wid = Widget()
label = Label(text='0')
btn_add100 = Button(text='+ 100 rects',
on_press=partial(self.add_rects, label, wid, 100))
btn_add500 = Button(text='+ 500 rects',
on_press=partial(self.add_rects, label, wid, 500))
btn_double = Button(text='x 2',
on_press=partial(self.double_rects, label, wid))
btn_reset = Button(text='Reset',
on_press=partial(self.reset_rects, label, wid))
layout = BoxLayout(size_hint=(1, None), height=50)
layout.add_widget(btn_add100)
layout.add_widget(btn_add500)
layout.add_widget(btn_double)
layout.add_widget(btn_reset)
layout.add_widget(label)
root = BoxLayout(orientation='vertical')
root.add_widget(wid)
root.add_widget(layout)
return root
if __name__ == '__main__':
StressCanvasApp().run()

View file

@ -0,0 +1,84 @@
'''
Circle Example
==============
This example exercises circle (ellipse) drawing. You should see sliders at the
top of the screen with the Kivy logo below it. The sliders control the
angle start and stop and the height and width scales. There is a button
to reset the sliders. The logo used for the circle's background image is
from the kivy/data directory. The entire example is coded in the
kv language description.
'''
from kivy.app import App
from kivy.lang import Builder
kv = '''
BoxLayout:
orientation: 'vertical'
BoxLayout:
size_hint_y: None
height: sp(100)
BoxLayout:
orientation: 'vertical'
Slider:
id: e1
min: -360.
max: 360.
Label:
text: 'angle_start = {}'.format(e1.value)
BoxLayout:
orientation: 'vertical'
Slider:
id: e2
min: -360.
max: 360.
value: 360
Label:
text: 'angle_end = {}'.format(e2.value)
BoxLayout:
size_hint_y: None
height: sp(100)
BoxLayout:
orientation: 'vertical'
Slider:
id: wm
min: 0
max: 2
value: 1
Label:
text: 'Width mult. = {}'.format(wm.value)
BoxLayout:
orientation: 'vertical'
Slider:
id: hm
min: 0
max: 2
value: 1
Label:
text: 'Height mult. = {}'.format(hm.value)
Button:
text: 'Reset ratios'
on_press: wm.value = 1; hm.value = 1
FloatLayout:
canvas:
Color:
rgb: 1, 1, 1
Ellipse:
pos: 100, 100
size: 200 * wm.value, 201 * hm.value
source: 'data/logo/kivy-icon-512.png'
angle_start: e1.value
angle_end: e2.value
'''
class CircleApp(App):
def build(self):
return Builder.load_string(kv)
CircleApp().run()

View file

@ -0,0 +1,91 @@
'''
FBO Canvas
==========
This demonstrates a layout using an FBO (Frame Buffer Off-screen)
instead of a plain canvas. You should see a black canvas with a
button labelled 'FBO' in the bottom left corner. Clicking it
animates the button moving right to left.
'''
__all__ = ('FboFloatLayout', )
from kivy.graphics import Color, Rectangle, Canvas, ClearBuffers, ClearColor
from kivy.graphics.fbo import Fbo
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty, NumericProperty
from kivy.app import App
from kivy.core.window import Window
from kivy.animation import Animation
from kivy.factory import Factory
class FboFloatLayout(FloatLayout):
texture = ObjectProperty(None, allownone=True)
alpha = NumericProperty(1)
def __init__(self, **kwargs):
self.canvas = Canvas()
with self.canvas:
self.fbo = Fbo(size=self.size)
self.fbo_color = Color(1, 1, 1, 1)
self.fbo_rect = Rectangle()
with self.fbo:
ClearColor(0, 0, 0, 0)
ClearBuffers()
# wait that all the instructions are in the canvas to set texture
self.texture = self.fbo.texture
super(FboFloatLayout, self).__init__(**kwargs)
def add_widget(self, *args, **kwargs):
# trick to attach graphics instruction to fbo instead of canvas
canvas = self.canvas
self.canvas = self.fbo
ret = super(FboFloatLayout, self).add_widget(*args, **kwargs)
self.canvas = canvas
return ret
def remove_widget(self, *args, **kwargs):
canvas = self.canvas
self.canvas = self.fbo
super(FboFloatLayout, self).remove_widget(*args, **kwargs)
self.canvas = canvas
def on_size(self, instance, value):
self.fbo.size = value
self.texture = self.fbo.texture
self.fbo_rect.size = value
def on_pos(self, instance, value):
self.fbo_rect.pos = value
def on_texture(self, instance, value):
self.fbo_rect.texture = value
def on_alpha(self, instance, value):
self.fbo_color.rgba = (1, 1, 1, value)
class ScreenLayerApp(App):
def build(self):
f = FboFloatLayout()
b = Factory.Button(text="FBO", size_hint=(None, None))
f.add_widget(b)
def anim_btn(*args):
if b.pos[0] == 0:
Animation(x=f.width - b.width).start(b)
else:
Animation(x=0).start(b)
b.bind(on_press=anim_btn)
return f
if __name__ == "__main__":
ScreenLayerApp().run()

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -0,0 +1,296 @@
'''
Line (SmoothLine) Experiment
============================
This demonstrates the experimental and unfinished SmoothLine feature
for fast line drawing. You should see a multi-segment
path at the top of the screen, and sliders and buttons along the bottom.
You can click to add new points to the segment, change the transparency
and width of the line, or hit 'Animate' to see a set of sine and cosine
animations. The Cap and Joint buttons don't work: SmoothLine has not
implemented these features yet.
'''
from kivy.app import App
from kivy.properties import OptionProperty, NumericProperty, ListProperty, \
BooleanProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.clock import Clock
from math import cos, sin
Builder.load_string('''
<LinePlayground>:
canvas:
Color:
rgba: .4, .4, 1, root.alpha
Line:
points: self.points
joint: self.joint
cap: self.cap
width: self.linewidth
close: self.close
dash_length: self.dash_length
dash_offset: self.dash_offset
dashes: self.dashes
Color:
rgba: .8, .8, .8, root.alpha_controlline
Line:
points: self.points
close: self.close
dash_length: self.dash_length
dash_offset: self.dash_offset
dashes: self.dashes
Color:
rgba: 1, .4, .4, root.alpha
Line:
points: self.points2
joint: self.joint
cap: self.cap
width: self.linewidth
close: self.close
dash_length: self.dash_length
dash_offset: self.dash_offset
dashes: self.dashes
GridLayout:
cols: 2
size_hint: 1, None
height: 44 * 5
GridLayout:
cols: 2
Label:
text: 'Alpha'
Slider:
value: root.alpha
on_value: root.alpha = float(args[1])
min: 0.
max: 1.
Label:
text: 'Alpha Control Line'
Slider:
value: root.alpha_controlline
on_value: root.alpha_controlline = float(args[1])
min: 0.
max: 1.
Label:
text: 'Width'
Slider:
value: root.linewidth
on_value: root.linewidth = args[1]
min: 1
max: 40
Label:
text: 'Cap'
GridLayout:
rows: 1
ToggleButton:
group: 'cap'
text: 'none'
on_press: root.cap = self.text
ToggleButton:
group: 'cap'
text: 'round'
on_press: root.cap = self.text
ToggleButton:
group: 'cap'
text: 'square'
on_press: root.cap = self.text
Label:
text: 'Joint'
GridLayout:
rows: 1
ToggleButton:
group: 'joint'
text: 'none'
on_press: root.joint = self.text
ToggleButton:
group: 'joint'
text: 'round'
on_press: root.joint = self.text
ToggleButton:
group: 'joint'
text: 'miter'
on_press: root.joint = self.text
ToggleButton:
group: 'joint'
text: 'bevel'
on_press: root.joint = self.text
Label:
text: 'Close'
ToggleButton:
text: 'Close line'
on_press: root.close = self.state == 'down'
Label:
text: 'Dashes'
GridLayout:
rows: 1
ToggleButton:
group: 'dashes'
text: 'none'
state: 'down'
allow_no_selection: False
size_hint_x: None
width: self.texture_size[0]
padding_x: '5dp'
on_state:
if self.state == 'down': root.dashes = []
if self.state == 'down': root.dash_length = 1
if self.state == 'down': root.dash_offset = 0
ToggleButton:
id: constant
group: 'dashes'
text: 'Constant: '
allow_no_selection: False
size_hint_x: None
width: self.texture_size[0]
padding_x: '5dp'
on_state:
if self.state == 'down': root.dashes = []
if self.state == 'down': root.dash_length = \
int(dash_len.text or 1)
if self.state == 'down': root.dash_offset = \
int(dash_offset.text or 0)
Label:
text: 'len'
size_hint_x: None
width: self.texture_size[0]
padding_x: '5dp'
TextInput:
id: dash_len
size_hint_x: None
width: '30dp'
input_filter: 'int'
multiline: False
text: '1'
on_text: if constant.state == 'down': \
root.dash_length = int(self.text or 1)
Label:
text: 'offset'
size_hint_x: None
width: self.texture_size[0]
padding_x: '5dp'
TextInput:
id: dash_offset
size_hint_x: None
width: '30dp'
input_filter: 'int'
multiline: False
text: '0'
on_text: if constant.state == 'down': \
root.dash_offset = int(self.text or 0)
ToggleButton:
id: dash_list
group: 'dashes'
text: 'List: '
allow_no_selection: False
size_hint_x: None
width: self.texture_size[0]
padding_x: '5dp'
on_state:
if self.state == 'down': root.dashes = list(map(lambda\
x: int(x or 0), dash_list_in.text.split(',')))
if self.state == 'down': root.dash_length = 1
if self.state == 'down': root.dash_offset = 0
TextInput:
id: dash_list_in
size_hint_x: None
width: '180dp'
multiline: False
text: '4,3,10,15'
on_text: if dash_list.state == 'down': root.dashes = \
list(map(lambda x: int(x or 0), self.text.split(',')))
AnchorLayout:
GridLayout:
cols: 1
size_hint: None, None
size: self.minimum_size
ToggleButton:
size_hint: None, None
size: 100, 44
text: 'Animate'
on_state: root.animate(self.state == 'down')
Button:
size_hint: None, None
size: 100, 44
text: 'Clear'
on_press: root.points = root.points2 = []
''')
class LinePlayground(FloatLayout):
alpha_controlline = NumericProperty(1.0)
alpha = NumericProperty(0.5)
close = BooleanProperty(False)
points = ListProperty([(500, 500),
[300, 300, 500, 300],
[500, 400, 600, 400]])
points2 = ListProperty([])
joint = OptionProperty('none', options=('round', 'miter', 'bevel', 'none'))
cap = OptionProperty('none', options=('round', 'square', 'none'))
linewidth = NumericProperty(10.0)
dt = NumericProperty(0)
dash_length = NumericProperty(1)
dash_offset = NumericProperty(0)
dashes = ListProperty([])
_update_points_animation_ev = None
def on_touch_down(self, touch):
if super(LinePlayground, self).on_touch_down(touch):
return True
touch.grab(self)
self.points.append(touch.pos)
return True
def on_touch_move(self, touch):
if touch.grab_current is self:
self.points[-1] = touch.pos
return True
return super(LinePlayground, self).on_touch_move(touch)
def on_touch_up(self, touch):
if touch.grab_current is self:
touch.ungrab(self)
return True
return super(LinePlayground, self).on_touch_up(touch)
def animate(self, do_animation):
if do_animation:
self._update_points_animation_ev = Clock.schedule_interval(
self.update_points_animation, 0)
elif self._update_points_animation_ev is not None:
self._update_points_animation_ev.cancel()
def update_points_animation(self, dt):
cy = self.height * 0.6
cx = self.width * 0.1
w = self.width * 0.8
step = 20
points = []
points2 = []
self.dt += dt
for i in range(int(w / step)):
x = i * step
points.append(cx + x)
points.append(cy + cos(x / w * 8. + self.dt) * self.height * 0.2)
points2.append(cx + x)
points2.append(cy + sin(x / w * 8. + self.dt) * self.height * 0.2)
self.points = points
self.points2 = points2
class TestLineApp(App):
def build(self):
return LinePlayground()
if __name__ == '__main__':
TestLineApp().run()

View file

@ -0,0 +1,183 @@
'''
Lines Extended Demo
===================
This demonstrates how to use the extended line drawing routines such
as circles, ellipses, and rectangles. You should see a static image of
labelled shapes on the screen.
'''
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_string('''
<LineEllipse1>:
canvas:
Color:
rgba: 1, .1, .1, .9
Line:
width: 2.
ellipse: (self.x, self.y, self.width, self.height)
Label:
center: root.center
text: 'Ellipse'
<LineEllipse2>:
canvas:
Color:
rgba: 1, .1, .1, .9
Line:
width: 2.
ellipse: (self.x, self.y, self.width, self.height, 90, 180)
Label:
center: root.center
text: 'Ellipse from 90 to 180'
# fun result with low segments!
<LineEllipse3>:
canvas:
Color:
rgba: 1, .1, .1, .9
Line:
width: 2.
ellipse: (self.x, self.y, self.width, self.height, 90, 720, 10)
Label:
center: root.center
text: 'Ellipse from 90 to 720\\n10 segments'
halign: 'center'
<LineCircle1>:
canvas:
Color:
rgba: .1, 1, .1, .9
Line:
width: 2.
circle:
(self.center_x, self.center_y, min(self.width, self.height)
/ 2)
Label:
center: root.center
text: 'Circle'
<LineCircle2>:
canvas:
Color:
rgba: .1, 1, .1, .9
Line:
width: 2.
circle:
(self.center_x, self.center_y, min(self.width, self.height)
/ 2, 90, 180)
Label:
center: root.center
text: 'Circle from 90 to 180'
<LineCircle3>:
canvas:
Color:
rgba: .1, 1, .1, .9
Line:
width: 2.
circle:
(self.center_x, self.center_y, min(self.width, self.height)
/ 2, 90, 180, 10)
Label:
center: root.center
text: 'Circle from 90 to 180\\n10 segments'
halign: 'center'
<LineCircle4>:
canvas:
Color:
rgba: .1, 1, .1, .9
Line:
width: 2.
circle:
(self.center_x, self.center_y, min(self.width, self.height)
/ 2, 0, 360)
Label:
center: root.center
text: 'Circle from 0 to 360'
halign: 'center'
<LineRectangle>:
canvas:
Color:
rgba: .1, .1, 1, .9
Line:
width: 2.
rectangle: (self.x, self.y, self.width, self.height)
Label:
center: root.center
text: 'Rectangle'
<LineBezier>:
canvas:
Color:
rgba: .1, .1, 1, .9
Line:
width: 2.
bezier:
(self.x, self.y, self.center_x - 40, self.y + 100,
self.center_x + 40, self.y - 100, self.right, self.y)
Label:
center: root.center
text: 'Bezier'
''')
class LineEllipse1(Widget):
pass
class LineEllipse2(Widget):
pass
class LineEllipse3(Widget):
pass
class LineCircle1(Widget):
pass
class LineCircle2(Widget):
pass
class LineCircle3(Widget):
pass
class LineCircle4(Widget):
pass
class LineRectangle(Widget):
pass
class LineBezier(Widget):
pass
class LineExtendedApp(App):
def build(self):
root = GridLayout(cols=2, padding=50, spacing=50)
root.add_widget(LineEllipse1())
root.add_widget(LineEllipse2())
root.add_widget(LineEllipse3())
root.add_widget(LineCircle1())
root.add_widget(LineCircle2())
root.add_widget(LineCircle3())
root.add_widget(LineCircle4())
root.add_widget(LineRectangle())
root.add_widget(LineBezier())
return root
if __name__ == '__main__':
LineExtendedApp().run()

View file

@ -0,0 +1,57 @@
'''
Mesh test
=========
This demonstrates the use of a mesh mode to distort an image. You should see
a line of buttons across the bottom of a canvas. Pressing them displays
the mesh, a small circle of points, with different mesh.mode settings.
'''
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.graphics import Mesh
from functools import partial
from math import cos, sin, pi
class MeshTestApp(App):
def change_mode(self, mode, *largs):
self.mesh.mode = mode
def build_mesh(self):
""" returns a Mesh of a rough circle. """
vertices = []
indices = []
step = 10
istep = (pi * 2) / float(step)
for i in range(step):
x = 300 + cos(istep * i) * 100
y = 300 + sin(istep * i) * 100
vertices.extend([x, y, 0, 0])
indices.append(i)
return Mesh(vertices=vertices, indices=indices)
def build(self):
wid = Widget()
with wid.canvas:
self.mesh = self.build_mesh()
layout = BoxLayout(size_hint=(1, None), height=50)
for mode in ('points', 'line_strip', 'line_loop', 'lines',
'triangle_strip', 'triangle_fan'):
button = Button(text=mode)
button.bind(on_release=partial(self.change_mode, mode))
layout.add_widget(button)
root = BoxLayout(orientation='vertical')
root.add_widget(wid)
root.add_widget(layout)
return root
if __name__ == '__main__':
MeshTestApp().run()

View file

@ -0,0 +1,101 @@
'''
Mesh Manipulation Example
=========================
This demonstrates creating a mesh and using it to deform the texture (the
kivy log). You should see the kivy logo with a five sliders to right.
The sliders change the mesh points' x and y offsets, radius, and a
'wobble' deformation's magnitude and speed.
This example is developed in gabriel's blog post at
http://kivy.org/planet/2014/01/kivy-image-manipulations-with-mesh-and-textures/
'''
from kivy.app import App
from kivy.lang import Builder
from kivy.core.image import Image as CoreImage
from kivy.properties import ListProperty, ObjectProperty, NumericProperty
from kivy.clock import Clock
from kivy.core.window import Window
from math import sin, cos, pi
kv = '''
BoxLayout:
Widget:
canvas:
Color:
rgba: 1, 1, 1, 1
Mesh:
vertices: app.mesh_points
indices: range(len(app.mesh_points) // 4)
texture: app.mesh_texture
mode: 'triangle_fan'
BoxLayout:
orientation: 'vertical'
size_hint_x: None
width: 100
Slider:
value: app.offset_x
on_value: app.offset_x = args[1]
min: -1
max: 1
Slider:
value: app.offset_y
on_value: app.offset_y = args[1]
min: -1
max: 1
Slider:
value: app.radius
on_value: app.radius = args[1]
min: 10
max: 1000
Slider:
value: app.sin_wobble
on_value: app.sin_wobble = args[1]
min: -50
max: 50
Slider:
value: app.sin_wobble_speed
on_value: app.sin_wobble_speed = args[1]
min: 0
max: 50
step: 1
'''
class MeshBallApp(App):
mesh_points = ListProperty([])
mesh_texture = ObjectProperty(None)
radius = NumericProperty(500)
offset_x = NumericProperty(.5)
offset_y = NumericProperty(.5)
sin_wobble = NumericProperty(0)
sin_wobble_speed = NumericProperty(0)
def build(self):
self.mesh_texture = CoreImage('data/logo/kivy-icon-512.png').texture
Clock.schedule_interval(self.update_points, 0)
return Builder.load_string(kv)
def update_points(self, *args):
""" replace self.mesh_points based on current slider positions.
Called continuously by a timer because this only sample code.
"""
points = [Window.width / 2, Window.height / 2, .5, .5]
i = 0
while i < 2 * pi:
i += 0.01 * pi
points.extend([
Window.width / 2 + cos(i) * (self.radius + self.sin_wobble *
sin(i * self.sin_wobble_speed)),
Window.height / 2 + sin(i) * (self.radius + self.sin_wobble *
sin(i * self.sin_wobble_speed)),
self.offset_x + sin(i),
self.offset_y + cos(i)])
self.mesh_points = points
if __name__ == '__main__':
MeshBallApp().run()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,110 @@
'''
Multitexture Example
====================
This example blends two textures: the image mtexture1.png of the letter K
and the image mtexture2.png of an orange circle. You should see an orange
K clipped to a circle. It uses a custom shader, written in glsl
(OpenGL Shading Language), stored in a local string.
Note the image mtexture1.png is a white 'K' on a transparent background, which
makes it hard to see.
'''
from kivy.clock import Clock
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.graphics import RenderContext, Color, Rectangle, BindTexture
fs_multitexture = '''
$HEADER$
// New uniform that will receive texture at index 1
uniform sampler2D texture1;
void main(void) {
// multiple current color with both texture (0 and 1).
// currently, both will use exactly the same texture coordinates.
gl_FragColor = frag_color * \
texture2D(texture0, tex_coord0) * \
texture2D(texture1, tex_coord0);
}
'''
kv = """
<MultitextureLayout>:
Image:
source: "mtexture1.png"
size_hint: .3,.3
id: 1
pos: 0,200
Image:
source: "mtexture2.png"
size_hint: .3,.3
id: 2
pos: 200,200
MultitextureWidget:
"""
Builder.load_string(kv)
class MultitextureWidget(Widget):
def __init__(self, **kwargs):
self.canvas = RenderContext()
# setting shader.fs to new source code automatically compiles it.
self.canvas.shader.fs = fs_multitexture
with self.canvas:
Color(1, 1, 1)
# here, we are binding a custom texture at index 1
# this will be used as texture1 in shader.
# The filenames are misleading: they do not correspond to the
# index here or in the shader.
BindTexture(source='mtexture2.png', index=1)
# create a rectangle with texture (will be at index 0)
Rectangle(size=(150, 150), source='mtexture1.png', pos=(500, 200))
# set the texture1 to use texture index 1
self.canvas['texture1'] = 1
# call the constructor of parent
# if they are any graphics objects, they will be added on our new
# canvas
super(MultitextureWidget, self).__init__(**kwargs)
# We'll update our glsl variables in a clock
Clock.schedule_interval(self.update_glsl, 0)
def update_glsl(self, *largs):
# This is needed for the default vertex shader.
self.canvas['projection_mat'] = Window.render_context['projection_mat']
self.canvas['modelview_mat'] = Window.render_context['modelview_mat']
class MultitextureLayout(FloatLayout):
def __init__(self, **kwargs):
self.size = kwargs['size']
super(MultitextureLayout, self).__init__(**kwargs)
class MultitextureApp(App):
def build(self):
return MultitextureLayout(size=(600, 600))
if __name__ == '__main__':
MultitextureApp().run()

View file

@ -0,0 +1,65 @@
'''
Repeat Texture on Resize
========================
This examples repeats the letter 'K' (mtexture1.png) 64 times in a window.
You should see 8 rows and 8 columns of white K letters, along a label
showing the current size. As you resize the window, it stays an 8x8.
This example includes a label with a colored background.
Note the image mtexture1.png is a white 'K' on a transparent background, which
makes it hard to see.
'''
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.properties import ObjectProperty, ListProperty
from kivy.lang import Builder
kv = '''
<LabelOnBackground>:
canvas.before:
Color:
rgb: self.background
Rectangle:
pos: self.pos
size: self.size
FloatLayout:
canvas.before:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
texture: app.texture
LabelOnBackground:
text: '{} (try to resize the window)'.format(root.size)
color: (0.4, 1, 1, 1)
background: (.3, .3, .3)
pos_hint: {'center_x': .5, 'center_y': .5 }
size_hint: None, None
height: 30
width: 250
'''
class LabelOnBackground(Label):
background = ListProperty((0.2, 0.2, 0.2))
class RepeatTexture(App):
texture = ObjectProperty()
def build(self):
self.texture = Image(source='mtexture1.png').texture
self.texture.wrap = 'repeat'
self.texture.uvsize = (8, 8)
return Builder.load_string(kv)
RepeatTexture().run()

View file

@ -0,0 +1,35 @@
'''
Rotation Example
================
This example rotates a button using PushMatrix and PopMatrix. You should see
a static button with the words 'hello world' rotated at a 45 degree angle.
'''
from kivy.app import App
from kivy.lang import Builder
kv = '''
FloatLayout:
Button:
text: 'hello world'
size_hint: None, None
pos_hint: {'center_x': .5, 'center_y': .5}
canvas.before:
PushMatrix
Rotate:
angle: 45
origin: self.center
canvas.after:
PopMatrix
'''
class RotationApp(App):
def build(self):
return Builder.load_string(kv)
RotationApp().run()

View file

@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Ellipse, Rectangle, RoundedRectangle
from kivy.lang import Builder
TEXTURE = 'kiwi.jpg'
YELLOW = (1, .7, 0)
ORANGE = (1, .45, 0)
RED = (1, 0, 0)
WHITE = (1, 1, 1)
class RoundedRectangleWidget(Widget):
def prepare(self):
with self.canvas:
Color(*WHITE)
# Rectangle of default size 100x100
Rectangle(pos=(50, 400))
# RoundedRectangles of default size 100x100:
# Textured:
RoundedRectangle(
pos=(175, 400), radius=[0, 50, 0, 50], source=TEXTURE)
# Colored:
Color(*YELLOW)
RoundedRectangle(pos=(300, 400), radius=[0, 50, 0, 50])
# Textured + Colored
# Color(.3,.3,.3, 1)
RoundedRectangle(
pos=(425, 400), radius=[0, 50, 0, 50], source=TEXTURE)
# Possible radius arguments:
# 1) Same value for each corner
Color(*ORANGE)
# With same radius 20x20
RoundedRectangle(pos=(50, 275), radius=[20])
# With same radius dimensions 20x40
RoundedRectangle(pos=(175, 275), radius=[(20, 40)])
# 2) Different values for each corner
Color(*RED)
# With different radiuses NxN:
RoundedRectangle(pos=(300, 275), radius=[10, 20, 30, 40])
# With different radiuses:
RoundedRectangle(
pos=(425, 275),
radius=[(10, 20), (20, 30), (30, 40), (40, 50)])
# Default ellipses
Color(*WHITE)
Ellipse(pos=(50, 150))
Ellipse(pos=(175, 150))
Ellipse(pos=(300, 150))
Ellipse(pos=(425, 150))
# Radius dimensions can't be bigger than half of the figure side
RoundedRectangle(pos=(175, 150), radius=[9000], source=TEXTURE)
# Segments parameter defines how many segments each corner has.
# More segments - more roundness
Color(*RED)
RoundedRectangle(pos=(300, 150), radius=[9000])
RoundedRectangle(pos=(425, 150), radius=[9000], segments=15)
Color(*ORANGE)
RoundedRectangle(pos=(425, 150), radius=[9000], segments=2)
Color(*YELLOW)
RoundedRectangle(pos=(425, 150), radius=[9000], segments=1)
# Various sizes
# You can cut corners by setting segments to 1.
# You can set different segment count to corners,
# by using a list useful for lowering vertex count
# by using small amount on small corners, while using
# bigger amount on bigger corners.
RoundedRectangle(
pos=(50, 25),
radius=[40],
segments=[1, 1, 10, 10],
size=(125, 100))
# If radius dimension is 0, then the corner will be sharp
# (90 degrees). It is also possible to mix tuple values
# with numeric
Color(*ORANGE)
RoundedRectangle(
pos=(200, 25),
radius=[(40, 20),
45.5, 45.5, 0],
segments=[2, 3, 3, 1], size=(125, 100))
Color(*RED)
RoundedRectangle(
pos=(350, 25),
radius=[(40, 40), (40, 40), (20, 20), (20, 20)],
segments=[2, 3, 3, 2],
size=(150, 100))
class DrawRoundedRectanglesApp(App):
def build(self):
kv = '''
Widget:
canvas:
Color:
rgba: 1, 1,1, 1
RoundedRectangle:
pos: 575, 400
size: 100, 100
radius: [0, 50, 0, 50]
source: 'kiwi.jpg'
Color:
rgba: 0, 0.8, 0.8, 1
RoundedRectangle:
pos: 575, 275
size: 100, 100
radius: [(10, 20), (20, 30), (30, 40), (40, 50)]
RoundedRectangle:
pos: 575, 150
size: 100, 100
radius: [9000]
segments: 15
RoundedRectangle:
pos: 550, 25
size: 150, 100
segments: [1, 2, 1, 3]
radius: [30, 40, 30, 40]
'''
widget = RoundedRectangleWidget()
widget.prepare()
kvrect = Builder.load_string(kv)
widget.add_widget(kvrect)
return widget
if __name__ == '__main__':
DrawRoundedRectanglesApp().run()

View file

@ -0,0 +1,37 @@
'''
Scaling Example
================
This example scales a button using PushMatrix and PopMatrix. It shows
a static button with the words 'hello world', stretched about its centre by
a factor of 1.5 horizontally and 5 vertically.
'''
from kivy.app import App
from kivy.lang import Builder
kv = '''
FloatLayout:
Button:
text: 'hello world'
size_hint: None, None
pos_hint: {'center_x': .5, 'center_y': .5}
canvas.before:
PushMatrix
Scale:
x: 1.5
y: 5
origin: self.center
canvas.after:
PopMatrix
'''
class ScalingApp(App):
def build(self):
return Builder.load_string(kv)
ScalingApp().run()

View file

@ -0,0 +1,85 @@
'''
Stencil demo
============
This is a test of the stencil graphics instruction inside the stencil view
widget. When you use a stencil, nothing will be drawn outside the bounding
box. All the graphics will draw only in the stencil view.
You can "draw" a stencil view by touch & draw. The touch down will set the
position, and the drag will set the size.
'''
from kivy.app import App
from kivy.core.window import Window
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.stencilview import StencilView
from random import random as r
from functools import partial
class StencilTestWidget(StencilView):
'''Drag to define stencil area
'''
def on_touch_down(self, touch):
self.pos = touch.pos
self.size = (1, 1)
def on_touch_move(self, touch):
self.size = (touch.x - touch.ox, touch.y - touch.oy)
class StencilCanvasApp(App):
def add_rects(self, label, wid, count, *largs):
label.text = str(int(label.text) + count)
with wid.canvas:
for x in range(count):
Color(r(), 1, 1, mode='hsv')
Rectangle(pos=(r() * wid.width + wid.x,
r() * wid.height + wid.y), size=(10, 10))
def reset_stencil(self, wid, *largs):
wid.pos = (0, 0)
wid.size = Window.size
def reset_rects(self, label, wid, *largs):
label.text = '0'
wid.canvas.clear()
def build(self):
wid = StencilTestWidget(size_hint=(None, None), size=Window.size)
label = Label(text='0')
btn_add500 = Button(text='+ 200 rects')
btn_add500.bind(on_press=partial(self.add_rects, label, wid, 200))
btn_reset = Button(text='Reset Rectangles')
btn_reset.bind(on_press=partial(self.reset_rects, label, wid))
btn_stencil = Button(text='Reset Stencil')
btn_stencil.bind(on_press=partial(self.reset_stencil, wid))
layout = BoxLayout(size_hint=(1, None), height=50)
layout.add_widget(btn_add500)
layout.add_widget(btn_reset)
layout.add_widget(btn_stencil)
layout.add_widget(label)
root = BoxLayout(orientation='vertical')
rfl = FloatLayout()
rfl.add_widget(wid)
root.add_widget(rfl)
root.add_widget(layout)
return root
if __name__ == '__main__':
StencilCanvasApp().run()

View file

@ -0,0 +1,146 @@
'''
Tesselate Demonstration
=======================
This demonstrates the experimental library for tesselating polygons. You
should see a hollow square with some buttons below it. You can click and
drag to create additional shapes, watching the number of vertices and elements
at the top of the screen. The 'debug' button toggles showing the mesh in
different colors.
'''
from kivy.app import App
from kivy.graphics import Mesh, Color
from kivy.graphics.tesselator import Tesselator, WINDING_ODD, TYPE_POLYGONS
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.logger import Logger
Builder.load_string("""
<ShapeBuilder>:
BoxLayout:
size_hint_y: None
height: "48dp"
spacing: "2dp"
padding: "2dp"
ToggleButton:
text: "Debug"
id: debug
on_release: root.build()
Button:
text: "New shape"
on_release: root.push_shape()
Button:
text: "Build"
on_release: root.build()
Button:
text: "Reset"
on_release: root.reset()
BoxLayout:
size_hint_y: None
height: "48dp"
top: root.top
spacing: "2dp"
padding: "2dp"
Label:
id: status
text: "Status"
""")
class ShapeBuilder(FloatLayout):
def __init__(self, **kwargs):
super(ShapeBuilder, self).__init__(**kwargs)
self.shapes = [
[100, 100, 300, 100, 300, 300, 100, 300],
[150, 150, 250, 150, 250, 250, 150, 250]
] # the 'hollow square' shape
self.shape = []
self.build()
def on_touch_down(self, touch):
if super(ShapeBuilder, self).on_touch_down(touch):
return True
Logger.info('tesselate: on_touch_down (%5.2f, %5.2f)' % touch.pos)
self.shape.extend(touch.pos)
self.build()
return True
def on_touch_move(self, touch):
if super(ShapeBuilder, self).on_touch_move(touch):
return True
Logger.info('tesselate: on_touch_move (%5.2f, %5.2f)' % touch.pos)
self.shape.extend(touch.pos)
self.build()
return True
def on_touch_up(self, touch):
if super(ShapeBuilder, self).on_touch_up(touch):
return True
Logger.info('tesselate: on_touch_up (%5.2f, %5.2f)' % touch.pos)
self.push_shape()
self.build()
def push_shape(self):
self.shapes.append(self.shape)
self.shape = []
def build(self):
tess = Tesselator()
count = 0
for shape in self.shapes:
if len(shape) >= 3:
tess.add_contour(shape)
count += 1
if self.shape and len(self.shape) >= 3:
tess.add_contour(self.shape)
count += 1
if not count:
return
ret = tess.tesselate(WINDING_ODD, TYPE_POLYGONS)
Logger.info('tesselate: build: tess.tesselate returns {}'.format(ret))
self.canvas.after.clear()
debug = self.ids.debug.state == "down"
if debug:
with self.canvas.after:
c = 0
for vertices, indices in tess.meshes:
Color(c, 1, 1, mode="hsv")
c += 0.3
indices = [0]
for i in range(1, len(vertices) // 4):
if i > 0:
indices.append(i)
indices.append(i)
indices.append(0)
indices.append(i)
indices.pop(-1)
Mesh(vertices=vertices, indices=indices, mode="lines")
else:
with self.canvas.after:
Color(1, 1, 1, 1)
for vertices, indices in tess.meshes:
Mesh(vertices=vertices, indices=indices,
mode="triangle_fan")
self.ids.status.text = "Shapes: {} - Vertex: {} - Elements: {}".format(
count, tess.vertex_count, tess.element_count)
def reset(self):
self.shapes = []
self.shape = []
self.ids.status.text = "Shapes: {} - Vertex: {} - Elements: {}".format(
0, 0, 0)
self.canvas.after.clear()
class TessApp(App):
def build(self):
return ShapeBuilder()
TessApp().run()

View file

@ -0,0 +1,143 @@
'''
Texture Wrapping and Coordinates Example
========================================
This example changes texture properties and the properties
of its containing rectangle. You should see some a multicolored
texture with sliders to the left and below and buttons at the
bottom of the screen. The image texture_example_image.png is
rendered into the rectangle. Sliders change the number of copies of the
texture (the tex_coords), the size of enclosing rectangle (the taw_height
and taw_width) while the buttons change how the texture is rendered when more
than one copy is in the rectangle (the
texture_wrap).
'''
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, ListProperty, StringProperty
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.base import runTouchApp
class TextureAccessibleWidget(Widget):
texture = ObjectProperty(None)
tex_coords = ListProperty([0, 0, 1, 0, 1, 1, 0, 1])
texture_wrap = StringProperty('clamp_to_edge')
def __init__(self, **kwargs):
super(TextureAccessibleWidget, self).__init__(**kwargs)
Clock.schedule_once(self.texture_init, 0)
def texture_init(self, *args):
self.texture = self.canvas.children[-1].texture
def on_texture_wrap(self, instance, value):
self.texture.wrap = value
root = Builder.load_string('''
<TextureAccessibleWidget>:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'texture_example_image.png'
tex_coords: root.tex_coords
<SliderWithValue@BoxLayout>:
min: 0.0
max: 1.0
value: slider.value
Slider:
id: slider
orientation: root.orientation
min: root.min
max: root.max
value: 1.0
Label:
size_hint: None, None
size: min(root.size), min(root.size)
text: str(slider.value)[:4]
BoxLayout:
orientation: 'vertical'
BoxLayout:
SliderWithValue:
orientation: 'vertical'
size_hint_x: None
width: dp(40)
min: 0
max: 5
value: 1
on_value: taw.tex_coords[5] = self.value
on_value: taw.tex_coords[7] = self.value
SliderWithValue:
orientation: 'vertical'
size_hint_x: None
width: dp(40)
min: 0
max: taw_container.height
value: 0.5*taw_container.height
on_value: taw.height = self.value
AnchorLayout:
id: taw_container
anchor_x: 'left'
anchor_y: 'bottom'
TextureAccessibleWidget:
id: taw
size_hint: None, None
BoxLayout:
size_hint_y: None
height: dp(80)
BoxLayout:
orientation: 'vertical'
size_hint_x: None
width: dp(80)
Label:
text: 'size'
text_size: self.size
halign: 'right'
valign: 'middle'
Label:
text: 'tex_coords'
text_size: self.size
halign: 'left'
valign: 'middle'
BoxLayout:
orientation: 'vertical'
SliderWithValue:
min: 0
max: taw_container.width
value: 0.5*taw_container.width
on_value: taw.width = self.value
SliderWithValue:
min: 0.
max: 5.
value: 1.
on_value: taw.tex_coords[2] = self.value
on_value: taw.tex_coords[4] = self.value
BoxLayout:
size_hint_y: None
height: dp(50)
Label:
text: 'texture wrap:'
text_size: self.size
valign: 'middle'
halign: 'center'
Button:
text: 'clamp_to_edge'
on_press: taw.texture_wrap = 'clamp_to_edge'
Button:
text: 'repeat'
on_press: taw.texture_wrap = 'repeat'
Button:
text: 'mirrored_repeat'
on_press: taw.texture_wrap = 'mirrored_repeat'
''')
runTouchApp(root)

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB