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,77 @@
'''
3D Rotating Monkey Head
========================
This example demonstrates using OpenGL to display a rotating monkey head. This
includes loading a Blender OBJ file, shaders written in OpenGL's Shading
Language (GLSL), and using scheduled callbacks.
The monkey.obj file is an OBJ file output from the Blender free 3D creation
software. The file is text, listing vertices and faces and is loaded
using a class in the file objloader.py. The file simple.glsl is
a simple vertex and fragment shader written in GLSL.
'''
from kivy.app import App
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.resources import resource_find
from kivy.graphics.transformation import Matrix
from kivy.graphics.opengl import glEnable, glDisable, GL_DEPTH_TEST
from kivy.graphics import RenderContext, Callback, PushMatrix, PopMatrix, \
Color, Translate, Rotate, Mesh, UpdateNormalMatrix
from objloader import ObjFile
class Renderer(Widget):
def __init__(self, **kwargs):
self.canvas = RenderContext(compute_normal_mat=True)
self.canvas.shader.source = resource_find('simple.glsl')
self.scene = ObjFile(resource_find("monkey.obj"))
super(Renderer, self).__init__(**kwargs)
with self.canvas:
self.cb = Callback(self.setup_gl_context)
PushMatrix()
self.setup_scene()
PopMatrix()
self.cb = Callback(self.reset_gl_context)
Clock.schedule_interval(self.update_glsl, 1 / 60.)
def setup_gl_context(self, *args):
glEnable(GL_DEPTH_TEST)
def reset_gl_context(self, *args):
glDisable(GL_DEPTH_TEST)
def update_glsl(self, delta):
asp = self.width / float(self.height)
proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 100, 1)
self.canvas['projection_mat'] = proj
self.canvas['diffuse_light'] = (1.0, 1.0, 0.8)
self.canvas['ambient_light'] = (0.1, 0.1, 0.1)
self.rot.angle += delta * 100
def setup_scene(self):
Color(1, 1, 1, 1)
PushMatrix()
Translate(0, 0, -3)
self.rot = Rotate(1, 0, 1, 0)
m = list(self.scene.objects.values())[0]
UpdateNormalMatrix()
self.mesh = Mesh(
vertices=m.vertices,
indices=m.indices,
fmt=m.vertex_format,
mode='triangles',
)
PopMatrix()
class RendererApp(App):
def build(self):
return Renderer()
if __name__ == "__main__":
RendererApp().run()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,147 @@
class MeshData(object):
def __init__(self, **kwargs):
self.name = kwargs.get("name")
self.vertex_format = [
(b'v_pos', 3, 'float'),
(b'v_normal', 3, 'float'),
(b'v_tc0', 2, 'float')]
self.vertices = []
self.indices = []
def calculate_normals(self):
for i in range(len(self.indices) / (3)):
fi = i * 3
v1i = self.indices[fi]
v2i = self.indices[fi + 1]
v3i = self.indices[fi + 2]
vs = self.vertices
p1 = [vs[v1i + c] for c in range(3)]
p2 = [vs[v2i + c] for c in range(3)]
p3 = [vs[v3i + c] for c in range(3)]
u, v = [0, 0, 0], [0, 0, 0]
for j in range(3):
v[j] = p2[j] - p1[j]
u[j] = p3[j] - p1[j]
n = [0, 0, 0]
n[0] = u[1] * v[2] - u[2] * v[1]
n[1] = u[2] * v[0] - u[0] * v[2]
n[2] = u[0] * v[1] - u[1] * v[0]
for k in range(3):
self.vertices[v1i + 3 + k] = n[k]
self.vertices[v2i + 3 + k] = n[k]
self.vertices[v3i + 3 + k] = n[k]
class ObjFile:
def finish_object(self):
if self._current_object is None:
return
mesh = MeshData()
idx = 0
for f in self.faces:
verts = f[0]
norms = f[1]
tcs = f[2]
for i in range(3):
# get normal components
n = (0.0, 0.0, 0.0)
if norms[i] != -1:
n = self.normals[norms[i] - 1]
# get texture coordinate components
t = (0.0, 0.0)
if tcs[i] != -1:
t = self.texcoords[tcs[i] - 1]
# get vertex components
v = self.vertices[verts[i] - 1]
data = [v[0], v[1], v[2], n[0], n[1], n[2], t[0], t[1]]
mesh.vertices.extend(data)
tri = [idx, idx + 1, idx + 2]
mesh.indices.extend(tri)
idx += 3
self.objects[self._current_object] = mesh
# mesh.calculate_normals()
self.faces = []
def __init__(self, filename, swapyz=False):
"""Loads a Wavefront OBJ file. """
self.objects = {}
self.vertices = []
self.normals = []
self.texcoords = []
self.faces = []
self._current_object = None
material = None
for line in open(filename, "r"):
if line.startswith('#'):
continue
if line.startswith('s'):
continue
values = line.split()
if not values:
continue
if values[0] == 'o':
self.finish_object()
self._current_object = values[1]
# elif values[0] == 'mtllib':
# self.mtl = MTL(values[1])
# elif values[0] in ('usemtl', 'usemat'):
# material = values[1]
if values[0] == 'v':
v = list(map(float, values[1:4]))
if swapyz:
v = v[0], v[2], v[1]
self.vertices.append(v)
elif values[0] == 'vn':
v = list(map(float, values[1:4]))
if swapyz:
v = v[0], v[2], v[1]
self.normals.append(v)
elif values[0] == 'vt':
self.texcoords.append(list(map(float, values[1:3])))
elif values[0] == 'f':
face = []
texcoords = []
norms = []
for v in values[1:]:
w = v.split('/')
face.append(int(w[0]))
if len(w) >= 2 and len(w[1]) > 0:
texcoords.append(int(w[1]))
else:
texcoords.append(-1)
if len(w) >= 3 and len(w[2]) > 0:
norms.append(int(w[2]))
else:
norms.append(-1)
self.faces.append((face, norms, texcoords, material))
self.finish_object()
def MTL(filename):
contents = {}
mtl = None
return
for line in open(filename, "r"):
if line.startswith('#'):
continue
values = line.split()
if not values:
continue
if values[0] == 'newmtl':
mtl = contents[values[1]] = {}
elif mtl is None:
raise ValueError("mtl file doesn't start with newmtl stmt")
mtl[values[0]] = values[1:]
return contents

View file

@ -0,0 +1,47 @@
/* simple.glsl
simple diffuse lighting based on laberts cosine law; see e.g.:
http://en.wikipedia.org/wiki/Lambertian_reflectance
http://en.wikipedia.org/wiki/Lambert%27s_cosine_law
*/
---VERTEX SHADER-------------------------------------------------------
#ifdef GL_ES
precision highp float;
#endif
attribute vec3 v_pos;
attribute vec3 v_normal;
uniform mat4 modelview_mat;
uniform mat4 projection_mat;
varying vec4 normal_vec;
varying vec4 vertex_pos;
void main (void) {
//compute vertex position in eye_space and normalize normal vector
vec4 pos = modelview_mat * vec4(v_pos,1.0);
vertex_pos = pos;
normal_vec = vec4(v_normal,0.0);
gl_Position = projection_mat * pos;
}
---FRAGMENT SHADER-----------------------------------------------------
#ifdef GL_ES
precision highp float;
#endif
varying vec4 normal_vec;
varying vec4 vertex_pos;
uniform mat4 normal_mat;
void main (void){
//correct normal, and compute light vector (assume light at the eye)
vec4 v_normal = normalize( normal_mat * normal_vec ) ;
vec4 v_light = normalize( vec4(0,0,0,1) - vertex_pos );
//reflectance based on lamberts law of cosine
float theta = clamp(dot(v_normal, v_light), 0.0, 1.0);
gl_FragColor = vec4(theta, theta, theta, 1.0);
}

View file

@ -0,0 +1,72 @@
#:kivy 1.1.0
Root:
text_input: text_input
BoxLayout:
orientation: 'vertical'
BoxLayout:
size_hint_y: None
height: 30
Button:
text: 'Load'
on_release: root.show_load()
Button:
text: 'Save'
on_release: root.show_save()
BoxLayout:
TextInput:
id: text_input
text: ''
RstDocument:
text: text_input.text
show_errors: True
<LoadDialog>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
<SaveDialog>:
text_input: text_input
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
on_selection: text_input.text = self.selection and self.selection[0] or ''
TextInput:
id: text_input
size_hint_y: None
height: 30
multiline: False
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Save"
on_release: root.save(filechooser.path, text_input.text)

View file

@ -0,0 +1,64 @@
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
import os
class LoadDialog(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
class SaveDialog(FloatLayout):
save = ObjectProperty(None)
text_input = ObjectProperty(None)
cancel = ObjectProperty(None)
class Root(FloatLayout):
loadfile = ObjectProperty(None)
savefile = ObjectProperty(None)
text_input = ObjectProperty(None)
def dismiss_popup(self):
self._popup.dismiss()
def show_load(self):
content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def show_save(self):
content = SaveDialog(save=self.save, cancel=self.dismiss_popup)
self._popup = Popup(title="Save file", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def load(self, path, filename):
with open(os.path.join(path, filename[0])) as stream:
self.text_input.text = stream.read()
self.dismiss_popup()
def save(self, path, filename):
with open(os.path.join(path, filename), 'w') as stream:
stream.write(self.text_input.text)
self.dismiss_popup()
class Editor(App):
pass
Factory.register('Root', cls=Root)
Factory.register('LoadDialog', cls=LoadDialog)
Factory.register('SaveDialog', cls=SaveDialog)
if __name__ == '__main__':
Editor().run()

View file

@ -0,0 +1,3 @@
title=Compass
author=Nik Klever
orientation=portrait

View file

@ -0,0 +1,25 @@
#:kivy 1.7.0
FloatLayout:
canvas:
Color:
rgb: .98, .98, .98
Rectangle:
size: self.size
Image:
source: 'rose.png'
Image:
source: 'needle.png'
canvas.before:
PushMatrix
Rotate:
angle: app.needle_angle
axis: 0, 0, 1
origin: self.center
canvas.after:
PopMatrix

View file

@ -0,0 +1,77 @@
'''
Compass example
===============
This example is a demonstration of Hardware class usage.
But it has severals drawbacks, like using only the magnetic sensor, and
extrapolating values to get the orientation. The compass is absolutely not
accurate.
The right way would be to get the accelerometer + magnetic, and computer
everything according to the phone orientation. This is not the purpose of this
example right now.
You can compile it with::
./build.py --package org.test.compass --name compass \
--private ~/code/kivy/examples/android/compass \
--window --version 1.0 debug installd
'''
import kivy
kivy.require('1.7.0')
from jnius import autoclass
from math import floor
from kivy.app import App
from kivy.properties import NumericProperty
from kivy.clock import Clock
from kivy.vector import Vector
from kivy.animation import Animation
Hardware = autoclass('org.renpy.android.Hardware')
class CompassApp(App):
needle_angle = NumericProperty(0)
def build(self):
self._anim = None
Hardware.magneticFieldSensorEnable(True)
Clock.schedule_interval(self.update_compass, 1 / 10.)
def update_compass(self, *args):
# read the magnetic sensor from the Hardware class
(x, y, z) = Hardware.magneticFieldSensorReading()
# calculate the angle
needle_angle = Vector(x, y).angle((0, 1)) + 90.
# fix animation transition around the unit circle
if (self.needle_angle % 360) - needle_angle > 180:
needle_angle += 360
elif (self.needle_angle % 360) - needle_angle < -180:
needle_angle -= 360
# add the number of revolutions to the result
needle_angle += 360 * floor(self.needle_angle / 360.)
# animate the needle
if self._anim:
self._anim.stop(self)
self._anim = Animation(needle_angle=needle_angle, d=.2, t='out_quad')
self._anim.start(self)
def on_pause(self):
# when you are going on pause, don't forget to stop the sensor
Hardware.magneticFieldSensorEnable(False)
return True
def on_resume(self):
# reactivate the sensor when you are back to the app
Hardware.magneticFieldSensorEnable(True)
if __name__ == '__main__':
CompassApp().run()

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View file

@ -0,0 +1,3 @@
title=TakePicture
author=Mathieu Virbel
orientation=portrait

View file

@ -0,0 +1,80 @@
'''
Take picture
============
.. author:: Mathieu Virbel <mat@kivy.org>
Little example to demonstrate how to start an Intent, and get the result.
When you use the Android.startActivityForResult(), the result will be
dispatched into onActivityResult. You can catch the event with the
android.activity API from python-for-android project.
If you want to compile it, don't forget to add the CAMERA permission::
./build.py --name 'TakePicture' --package org.test.takepicture \
--permission CAMERA --version 1 \
--private ~/code/kivy/examples/android/takepicture \
debug installd
'''
__version__ = '0.1'
from kivy.app import App
from os.path import exists
from jnius import autoclass, cast
from android import activity, mActivity
from functools import partial
from kivy.clock import Clock
from kivy.uix.scatter import Scatter
from kivy.properties import StringProperty
from PIL import Image
Intent = autoclass('android.content.Intent')
MediaStore = autoclass('android.provider.MediaStore')
Uri = autoclass('android.net.Uri')
Environment = autoclass('android.os.Environment')
class Picture(Scatter):
source = StringProperty(None)
class TakePictureApp(App):
def build(self):
self.index = 0
activity.bind(on_activity_result=self.on_activity_result)
def get_filename(self):
while True:
self.index += 1
fn = (Environment.getExternalStorageDirectory().getPath() +
'/takepicture{}.jpg'.format(self.index))
if not exists(fn):
return fn
def take_picture(self):
intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
self.last_fn = self.get_filename()
self.uri = Uri.parse('file://' + self.last_fn)
self.uri = cast('android.os.Parcelable', self.uri)
intent.putExtra(MediaStore.EXTRA_OUTPUT, self.uri)
mActivity.startActivityForResult(intent, 0x123)
def on_activity_result(self, requestCode, resultCode, intent):
if requestCode == 0x123:
Clock.schedule_once(partial(self.add_picture, self.last_fn), 0)
def add_picture(self, fn, *args):
im = Image.open(fn)
width, height = im.size
im.thumbnail((width / 4, height / 4), Image.ANTIALIAS)
im.save(fn, quality=95)
self.root.add_widget(Picture(source=fn, center=self.root.center))
def on_pause(self):
return True
TakePictureApp().run()

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,40 @@
#:kivy 1.0
#:import win kivy.core.window
Widget:
canvas:
Color:
rgb: .85, .87, .88
Rectangle:
size: self.size
Button:
text: 'Take a picture'
width: self.texture_size[0] + dp(40)
height: '48dp'
on_release: app.take_picture()
<Picture>:
on_size: self.center = win.Window.center
size: image.size
size_hint: None, None
Image:
id: image
source: root.source
# create initial image to be 400 pixels width
size: 400, 400
# add shadow background
canvas.before:
Color:
rgba: 1, 1, 1, 1
BorderImage:
source: 'shadow32.png'
border: (36, 36, 36, 36)
size:(self.width + 72, self.height + 72)
pos: (-36, -36)

View file

@ -0,0 +1,42 @@
'''
Widget animation
================
This example demonstrates creating and applying a multi-part animation to
a button widget. You should see a button labelled 'plop' that will move with
an animation when clicked.
'''
import kivy
kivy.require('1.0.7')
from kivy.animation import Animation
from kivy.app import App
from kivy.uix.button import Button
class TestApp(App):
def animate(self, instance):
# create an animation object. This object could be stored
# and reused each call or reused across different widgets.
# += is a sequential step, while &= is in parallel
animation = Animation(pos=(100, 100), t='out_bounce')
animation += Animation(pos=(200, 100), t='out_bounce')
animation &= Animation(size=(500, 500))
animation += Animation(size=(100, 50))
# apply the animation on the button, passed in the "instance" argument
# Notice that default 'click' animation (changing the button
# color while the mouse is down) is unchanged.
animation.start(instance)
def build(self):
# create a button, and attach animate() method as a on_press handler
button = Button(size_hint=(None, None), text='plop',
on_press=self.animate)
return button
if __name__ == '__main__':
TestApp().run()

View file

@ -0,0 +1,152 @@
'''
Suite of Application Builders
=============================
This explores different methods of starting an application. If you run
this without a command line parameter, you should see a menu in your terminal.
You can also run this with a 'r' parameter to pick a random method.
There are lots of logging options to make this easier to debug: the execution
order may not be obvious. Each time you run the command, only one kivy
application is created.
This uses the file testkvfile.kv and the file app_suite_data/testkvdir.kv.
'''
from __future__ import print_function
import sys
import re
from random import choice
import kivy
kivy.require('1.8.0') # 1.8 is when kv_directory became part of app.
from kivy.app import App
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
# Note that importing FloatLayout causes Kivy to execute, including
# starting up the Logger and some other messages.
print("** In main program, done with imports")
class TestBuildApp(App):
""" Use build() function to return a widget. """
def build(self):
""" Build called by kivy when an App is started.
Called after trying to load a .kv file.
Returns a new Button as a root widget.
"""
print("** inside build()")
return Button(text='hello from TestBuildApp')
class TestKVFileApp(App):
"""
Empty class, but name used to find .kv file. The filename is the lowercase
version of the class, i.e. 'testkvfileapp.kv'. If not found, it strips
off the final 'app', i.e. 'testkvfile.kv'. If not file is found, and no
other method sets the self.root, the program will run with an empty screen.
"""
pass
class TestKVDirApp(App):
"""
Empty class except for setting class variable kv_directory.
This directory sets the directory in which to search for the .kv file.
The name of the kv file is still governed by the class name and the .kv
file should still have one root widget.
"""
kv_directory = 'app_suite_data'
class TestKVStringApp(App):
"""
Use a build() function and use the kivy.lang.Builder function to parse up a
Kivy language string.
"""
def build(self):
""" Called by kivy run(). """
print("** inside build()")
widget = Builder.load_string(
"Button:\n text: 'hello from TestKVStringApp'")
print("** widget built")
return widget
class TestPrebuiltApp(App):
""" Use the Builder to create a top level widget at the beginning
of the Python program, then use a dummy class for that widget.
This costs a bit more in start-up time. """
kv = "<Prebuilt>\n Button:\n text:'hello from TestPrebuiltApp'"
Builder.load_string(kv)
print("** in TestPrebuiltApp, class initialization built <Prebuilt>")
class Prebuilt(FloatLayout):
""" Empty class to cause setting root to <Prebuilt> tag and
set inheritance """
pass
def build(self):
""" called, returns instance matching tag . """
return self.Prebuilt()
def print_class(class_name):
""" Read this file and print the section with the class name specified.)"""
filename = sys.argv[0]
with open(filename) as f:
data = f.read()
regex = "^(class " + class_name + "\\b.*?)^\\S"
match = re.search(regex, data, flags=re.MULTILINE | re.DOTALL)
if match:
print(match.group(1))
# the __name__ idiom executes when run from command line but not from import.
if __name__ == '__main__':
dash = "-" * 40
arg = sys.argv[1][0].lower() if len(sys.argv) > 1 else "h"
print(dash)
if arg == 'r':
arg = choice('bfds')
if arg == 'b':
print_class("TestBuildApp")
TestBuildApp().run()
elif arg == 'f':
print_class("TestKVFileApp")
TestKVFileApp().run()
elif arg == 'd':
print_class("TestKVDirApp")
TestKVDirApp().run()
elif arg == 's':
print_class("TestKVStringApp")
TestKVStringApp().run()
elif arg == 'p':
print_class("TestPrebuiltApp")
TestPrebuiltApp().run()
else: # help
print("""
This demo runs different application windows based on a command line argument.
Try using one of these:
b - Use build() method to return a widget
d - Use a kv file from a different directory
f - Use a kv file with the widget object
p - Use prebuilt widget inside a layout
s - Use a kivy language string to create the widget
r - pick one of the options at random.
h - show this help message.
After closing the application window, this program will exit.
While the run() method does return, kivy cannot run another
application window after one has been closed.
""")
print(dash)
print("This program is gratified to be of use.")

View file

@ -0,0 +1,4 @@
#:kivy 1.0
Button:
text: 'Hello from app_suite_data/testkvdir.kv'

View file

@ -0,0 +1,24 @@
'''
Application example using build() + return
==========================================
An application can be built if you return a widget on build(), or if you set
self.root.
'''
import kivy
kivy.require('1.0.7')
from kivy.app import App
from kivy.uix.button import Button
class TestApp(App):
def build(self):
# return a Button() as a root widget
return Button(text='hello world')
if __name__ == '__main__':
TestApp().run()

View file

@ -0,0 +1,49 @@
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ConfigParserProperty
KV = '''
FloatLayout:
BoxLayout:
size_hint: .5, .5
pos_hint: {'center': (.5, .5)}
orientation: 'vertical'
TextInput:
text: app.text
on_text: app.text = self.text
Slider:
min: 0
max: 100
value: app.number
on_value: app.number = self.value
'''
class ConfigApp(App):
number = ConfigParserProperty(
0, 'general', 'number',
'app', val_type=float
)
text = ConfigParserProperty(
'', 'general', 'text',
'app', val_type=str
)
def build_config(self, config):
config.setdefaults(
'general',
{
'number': 0,
'text': 'test'
}
)
def build(self):
return Builder.load_string(KV)
if __name__ == '__main__':
ConfigApp().run()

View file

@ -0,0 +1,25 @@
'''
Application built from a .kv file
==================================
This shows how to implicitly use a .kv file for your application. You
should see a full screen button labelled "Hello from test.kv".
After Kivy instantiates a subclass of App, it implicitly searches for a .kv
file. The file test.kv is selected because the name of the subclass of App is
TestApp, which implies that kivy should try to load "test.kv". That file
contains a root Widget.
'''
import kivy
kivy.require('1.0.7')
from kivy.app import App
class TestApp(App):
pass
if __name__ == '__main__':
TestApp().run()

View file

@ -0,0 +1,27 @@
'''
Application from a .kv in a Template Directory
==============================================
This example shows how you can change the directory for the .kv file. You
should see "Hello from template1/test.ky" as a button.
As kivy instantiates the TestApp subclass of App, the variable kv_directory
is set. Kivy then implicitly searches for a .kv file matching the name
of the subclass in that directory, finding the file template1/test.kv. That
file contains the root widget.
'''
import kivy
kivy.require('1.0.7')
from kivy.app import App
class TestApp(App):
kv_directory = 'template1'
if __name__ == '__main__':
TestApp().run()

View file

@ -0,0 +1,4 @@
#:kivy 1.0
Button:
text: 'Hello from template1/test.kv'

View file

@ -0,0 +1,4 @@
#:kivy 1.0
Button:
text: 'Hello from test.kv'

View file

@ -0,0 +1,4 @@
#:kivy 1.0
Button:
text: 'Hello from testkvfile.kv'

View file

@ -0,0 +1,90 @@
'''Example shows the recommended way of how to run Kivy with the Python built
in asyncio event loop as just another async coroutine.
'''
import asyncio
from kivy.app import App
from kivy.lang.builder import Builder
kv = '''
BoxLayout:
orientation: 'vertical'
BoxLayout:
ToggleButton:
id: btn1
group: 'a'
text: 'Sleeping'
allow_no_selection: False
on_state: if self.state == 'down': label.status = self.text
ToggleButton:
id: btn2
group: 'a'
text: 'Swimming'
allow_no_selection: False
on_state: if self.state == 'down': label.status = self.text
ToggleButton:
id: btn3
group: 'a'
text: 'Reading'
allow_no_selection: False
state: 'down'
on_state: if self.state == 'down': label.status = self.text
Label:
id: label
status: 'Reading'
text: 'Beach status is "{}"'.format(self.status)
'''
class AsyncApp(App):
other_task = None
def build(self):
return Builder.load_string(kv)
def app_func(self):
'''This will run both methods asynchronously and then block until they
are finished
'''
self.other_task = asyncio.ensure_future(self.waste_time_freely())
async def run_wrapper():
# we don't actually need to set asyncio as the lib because it is
# the default, but it doesn't hurt to be explicit
await self.async_run(async_lib='asyncio')
print('App done')
self.other_task.cancel()
return asyncio.gather(run_wrapper(), self.other_task)
async def waste_time_freely(self):
'''This method is also run by the asyncio loop and periodically prints
something.
'''
try:
i = 0
while True:
if self.root is not None:
status = self.root.ids.label.status
print('{} on the beach'.format(status))
# get some sleep
if self.root.ids.btn1.state != 'down' and i >= 2:
i = 0
print('Yawn, getting tired. Going to sleep')
self.root.ids.btn1.trigger_action()
i += 1
await asyncio.sleep(2)
except asyncio.CancelledError as e:
print('Wasting time was canceled', e)
finally:
# when canceled, print that it finished
print('Done wasting time')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(AsyncApp().app_func())
loop.close()

View file

@ -0,0 +1,59 @@
'''Example shows the recommended way of how to run Kivy with the Python built
in asyncio event loop as just another async coroutine.
'''
import asyncio
from kivy.app import async_runTouchApp
from kivy.lang.builder import Builder
kv = '''
BoxLayout:
orientation: 'vertical'
Button:
id: btn
text: 'Press me'
BoxLayout:
Label:
id: label
text: 'Button is "{}"'.format(btn.state)
'''
async def run_app_happily(root, other_task):
'''This method, which runs Kivy, is run by the asyncio loop as one of the
coroutines.
'''
# we don't actually need to set asyncio as the lib because it is the
# default, but it doesn't hurt to be explicit
await async_runTouchApp(root, async_lib='asyncio') # run Kivy
print('App done')
# now cancel all the other tasks that may be running
other_task.cancel()
async def waste_time_freely():
'''This method is also run by the asyncio loop and periodically prints
something.
'''
try:
while True:
print('Sitting on the beach')
await asyncio.sleep(2)
except asyncio.CancelledError as e:
print('Wasting time was canceled', e)
finally:
# when canceled, print that it finished
print('Done wasting time')
if __name__ == '__main__':
def root_func():
'''This will run both methods asynchronously and then block until they
are finished
'''
root = Builder.load_string(kv) # root widget
other_task = asyncio.ensure_future(waste_time_freely())
return asyncio.gather(run_app_happily(root, other_task), other_task)
loop = asyncio.get_event_loop()
loop.run_until_complete(root_func())
loop.close()

View file

@ -0,0 +1,93 @@
'''Example shows the recommended way of how to run Kivy with a trio
event loop as just another async coroutine.
'''
import trio
from kivy.app import App
from kivy.lang.builder import Builder
kv = '''
BoxLayout:
orientation: 'vertical'
BoxLayout:
ToggleButton:
id: btn1
group: 'a'
text: 'Sleeping'
allow_no_selection: False
on_state: if self.state == 'down': label.status = self.text
ToggleButton:
id: btn2
group: 'a'
text: 'Swimming'
allow_no_selection: False
on_state: if self.state == 'down': label.status = self.text
ToggleButton:
id: btn3
group: 'a'
text: 'Reading'
allow_no_selection: False
state: 'down'
on_state: if self.state == 'down': label.status = self.text
Label:
id: label
status: 'Reading'
text: 'Beach status is "{}"'.format(self.status)
'''
class AsyncApp(App):
nursery = None
def build(self):
return Builder.load_string(kv)
async def app_func(self):
'''trio needs to run a function, so this is it. '''
async with trio.open_nursery() as nursery:
'''In trio you create a nursery, in which you schedule async
functions to be run by the nursery simultaneously as tasks.
This will run all two methods starting in random order
asynchronously and then block until they are finished or canceled
at the `with` level. '''
self.nursery = nursery
async def run_wrapper():
# trio needs to be set so that it'll be used for the event loop
await self.async_run(async_lib='trio')
print('App done')
nursery.cancel_scope.cancel()
nursery.start_soon(run_wrapper)
nursery.start_soon(self.waste_time_freely)
async def waste_time_freely(self):
'''This method is also run by trio and periodically prints something.
'''
try:
i = 0
while True:
if self.root is not None:
status = self.root.ids.label.status
print('{} on the beach'.format(status))
# get some sleep
if self.root.ids.btn1.state != 'down' and i >= 2:
i = 0
print('Yawn, getting tired. Going to sleep')
self.root.ids.btn1.trigger_action()
i += 1
await trio.sleep(2)
except trio.Cancelled as e:
print('Wasting time was canceled', e)
finally:
# when canceled, print that it finished
print('Done wasting time')
if __name__ == '__main__':
trio.run(AsyncApp().app_func)

View file

@ -0,0 +1,58 @@
'''Example shows the recommended way of how to run Kivy with a trio
event loop as just another async coroutine.
'''
import trio
from kivy.app import async_runTouchApp
from kivy.lang.builder import Builder
kv = '''
BoxLayout:
orientation: 'vertical'
Button:
id: btn
text: 'Press me'
BoxLayout:
Label:
id: label
text: 'Button is "{}"'.format(btn.state)
'''
async def run_app_happily(root, nursery):
'''This method, which runs Kivy, is run by trio as one of the coroutines.
'''
# trio needs to be set so that it'll be used for the event loop
await async_runTouchApp(root, async_lib='trio') # run Kivy
print('App done')
# now cancel all the other tasks that may be running
nursery.cancel_scope.cancel()
async def waste_time_freely():
'''This method is also run by trio and periodically prints something.'''
try:
while True:
print('Sitting on the beach')
await trio.sleep(2)
except trio.Cancelled as e:
print('Wasting time was canceled', e)
finally:
# when canceled, print that it finished
print('Done wasting time')
if __name__ == '__main__':
async def root_func():
'''trio needs to run a function, so this is it. '''
root = Builder.load_string(kv) # root widget
async with trio.open_nursery() as nursery:
'''In trio you create a nursery, in which you schedule async
functions to be run by the nursery simultaneously as tasks.
This will run all two methods starting in random order
asynchronously and then block until they are finished or canceled
at the `with` level. '''
nursery.start_soon(run_app_happily, root, nursery)
nursery.start_soon(waste_time_freely)
trio.run(root_func)

View file

@ -0,0 +1,59 @@
#:kivy 1.0
#:import kivy kivy
<AudioBackground>:
orientation: 'vertical'
canvas:
Color:
rgb: 1, 1, 1
Rectangle:
source: 'data/images/background.jpg'
size: self.size
BoxLayout:
padding: 10
spacing: 10
size_hint: 1, None
pos_hint: {'top': 1}
height: 44
Image:
size_hint: None, None
size: 24, 24
source: 'data/logo/kivy-icon-24.png'
Label:
height: 24
text_size: self.size
color: (1, 1, 1, .8)
text: 'Kivy %s - Audio sample' % kivy.__version__
valign: 'middle'
Label:
text: 'Audio example'
font_size: 32
size_hint_y: None
BoxLayout:
Slider:
min: 0.0
max: 1.0
value: 1.0
on_value: app.set_volume(self.value)
orientation: "vertical"
size_hint_x: None
width: "48dp"
StackLayout:
id: sl
Button:
text: 'Stop and release all audio'
size_hint_y: None
height: '50sp'
on_press: app.release_audio()
<AudioButton>:
size_hint: None,0.333
width: self.height
text_size: self.size
font_size: '12sp'
valign: 'middle'

View file

@ -0,0 +1,81 @@
'''
Audio example
=============
This example plays sounds of different formats. You should see a grid of
buttons labelled with filenames. Clicking on the buttons will play, or
restart, each sound. Not all sound formats will play on all platforms.
All the sounds are from the http://woolyss.com/chipmusic-samples.php
"THE FREESOUND PROJECT", Under Creative Commons Sampling Plus 1.0 License.
'''
import kivy
kivy.require('1.0.8')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.core.audio import SoundLoader
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from glob import glob
from os.path import dirname, join, basename
class AudioButton(Button):
filename = StringProperty(None)
sound = ObjectProperty(None, allownone=True)
volume = NumericProperty(1.0)
def on_press(self):
if self.sound is None:
self.sound = SoundLoader.load(self.filename)
# stop the sound if it's currently playing
if self.sound.state != 'stop':
self.sound.stop()
self.sound.volume = self.volume
self.sound.play()
def release_audio(self):
if self.sound:
self.sound.stop()
self.sound.unload()
self.sound = None
def set_volume(self, volume):
self.volume = volume
if self.sound:
self.sound.volume = volume
class AudioBackground(BoxLayout):
pass
class AudioApp(App):
def build(self):
root = AudioBackground(spacing=5)
for fn in glob(join(dirname(__file__), '*.wav')):
btn = AudioButton(
text=basename(fn[:-4]).replace('_', ' '), filename=fn,
size_hint=(None, None), halign='center',
size=(128, 128), text_size=(118, None))
root.ids.sl.add_widget(btn)
return root
def release_audio(self):
for audiobutton in self.root.ids.sl.children:
audiobutton.release_audio()
def set_volume(self, value):
for audiobutton in self.root.ids.sl.children:
audiobutton.set_volume(value)
if __name__ == '__main__':
AudioApp().run()

View file

@ -0,0 +1,42 @@
# encoding: utf8
from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from sys import version_info
NOTES = (
('Do', 1),
('', 9 / 8.),
('Mi', 5 / 4.),
('Fa', 4 / 3.),
('Sol', 3 / 2.),
('La', 5 / 3.),
('Si', 15 / 8.),
)
class Test(App):
def build(self):
self.sound = SoundLoader.load(
'/usr/lib64/python{}.{}/test/audiodata/pluck-pcm32.wav'
.format(*version_info[0:2])
)
root = BoxLayout()
for octave in range(-2, 3):
for note, pitch in NOTES:
button = Button(text=note)
button.pitch = pitch * 2 ** octave
button.bind(on_release=self.play_note)
root.add_widget(button)
return root
def play_note(self, button):
self.sound.pitch = button.pitch
self.sound.play()
Test().run()

View file

@ -0,0 +1,59 @@
'''
Camera Example
==============
This example demonstrates a simple use of the camera. It shows a window with
a buttoned labelled 'play' to turn the camera on and off. Note that
not finding a camera, perhaps because gstreamer is not installed, will
throw an exception during the kv language processing.
'''
# Uncomment these lines to see all the messages
# from kivy.logger import Logger
# import logging
# Logger.setLevel(logging.TRACE)
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
import time
Builder.load_string('''
<CameraClick>:
orientation: 'vertical'
Camera:
id: camera
resolution: (640, 480)
play: False
ToggleButton:
text: 'Play'
on_press: camera.play = not camera.play
size_hint_y: None
height: '48dp'
Button:
text: 'Capture'
size_hint_y: None
height: '48dp'
on_press: root.capture()
''')
class CameraClick(BoxLayout):
def capture(self):
'''
Function to capture the images and give them the names
according to their captured time and date.
'''
camera = self.ids['camera']
timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
class TestCamera(App):
def build(self):
return CameraClick()
TestCamera().run()

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()

Some files were not shown because too many files have changed in this diff Show more