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,63 @@
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.base import runTouchApp
from kivy.uix.boxlayout import BoxLayout
import os
Builder.load_string('''
#:import Clipboard kivy.core.clipboard.Clipboard
<Clip>:
orientation: 'vertical'
GridLayout:
cols: 3
size_hint_y: None
height: self.minimum_height
Button:
text: 'Paste raw'
size_hint_y: None
height: 60
on_release: root.make_labels(Clipboard.paste())
Button:
text: 'Paste & format'
size_hint_y: None
height: 60
on_release: root.make_pretty_labels(Clipboard.paste())
Button:
text: 'Remove widgets'
size_hint_y: None
height: 60
on_release: container.clear_widgets()
ScrollView:
GridLayout:
cols: 1
id: container
size_hint_y: None
height: self.minimum_height
''')
class Clip(BoxLayout):
def make_labels(self, values):
"""Creates widgets from raw clipboard i.e. for each character in the
list that is provided by Clipboard.paste()
"""
print(repr(values))
for value in values:
label = Label(text=value, size_hint_y=None, height=30)
self.ids.container.add_widget(label)
def make_pretty_labels(self, values):
"""Creates widgets from a list of values made by splitting clipboard
by the default OS line separator. Useful when copying columns of data.
"""
print(repr(values))
for value in values.split(os.linesep):
label = Label(text=value, size_hint_y=None, height=30)
self.ids.container.add_widget(label)
runTouchApp(Clip())

View file

@ -0,0 +1,56 @@
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.core.window import Window
from kivy.logger import Logger
kv = """
#:import rgba kivy.utils.rgba
<TitleBar>:
id:title_bar
size_hint: 1,0.1
pos_hint : {'top':0.5}
BoxLayout:
orientation:"vertical"
BoxLayout:
Button:
text: "Click-able"
draggable:False
Button:
text: "non Click-able"
Button:
text: "non Click-able"
BoxLayout:
draggable:False
Button:
text: "Click-able"
Button:
text: "click-able"
Button:
text: "Click-able"
FloatLayout:
"""
class TitleBar(BoxLayout):
pass
class CustomTitleBar(App):
def build(self):
root = Builder.load_string(kv)
Window.custom_titlebar = True
title_bar = TitleBar()
root.add_widget(title_bar)
if Window.set_custom_titlebar(title_bar):
Logger.info("Window: setting custom titlebar successful")
else:
Logger.info("Window: setting custom titlebar "
"Not allowed on this system ")
self.title = "MyApp"
return root
if __name__ == "__main__":
CustomTitleBar().run()

View file

@ -0,0 +1,14 @@
# save an image into bytesio
from kivy.core.image import Image
from io import BytesIO
img = Image.load("data/logo/kivy-icon-512.png")
bio = BytesIO()
ret = img.save(bio, fmt="png")
print("len=", len(bio.read()))
bio = BytesIO()
ret = img.save(bio, fmt="jpg")
print("len=", len(bio.read()))

View file

@ -0,0 +1,83 @@
# Joystick / Gamepad example
# STOP_FIRE from https://wiki.libsdl.org/SDL_JoyAxisEvent
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.properties import ObjectProperty, ListProperty
class Listener(Widget):
# fire / trigger axis
FIRE = (2, 5)
STOP_FIRE = -32767
# min value for user to actually trigger axis
OFFSET = 15000
# current values + event instance
VALUES = ListProperty([])
HOLD = ObjectProperty(None)
def __init__(self, **kwargs):
super(Listener, self).__init__(**kwargs)
# get joystick events first
Window.bind(on_joy_hat=self.on_joy_hat)
Window.bind(on_joy_ball=self.on_joy_ball)
Window.bind(on_joy_axis=self.on_joy_axis)
Window.bind(on_joy_button_up=self.on_joy_button_up)
Window.bind(on_joy_button_down=self.on_joy_button_down)
# show values in console
def print_values(self, *args):
print(self.VALUES)
def joy_motion(self, event, id, axis, value):
# HAT first, returns max values
if isinstance(value, tuple):
if not value[0] and not value[1]:
Clock.unschedule(self.HOLD)
else:
self.VALUES = [event, id, axis, value]
self.HOLD = Clock.schedule_interval(self.print_values, 0)
return
# unschedule if at zero or at minimum (FIRE)
if axis in self.FIRE and value < self.STOP_FIRE:
Clock.unschedule(self.HOLD)
return
elif abs(value) < self.OFFSET or self.HOLD:
Clock.unschedule(self.HOLD)
# schedule if over OFFSET (to prevent accidental event with low value)
if (axis in self.FIRE and value > self.STOP_FIRE or
axis not in self.FIRE and abs(value) >= self.OFFSET):
self.VALUES = [event, id, axis, value]
self.HOLD = Clock.schedule_interval(self.print_values, 0)
# replace window instance with identifier
def on_joy_axis(self, win, stickid, axisid, value):
self.joy_motion('axis', stickid, axisid, value)
def on_joy_ball(self, win, stickid, ballid, xvalue, yvalue):
self.joy_motion('ball', stickid, ballid, (xvalue, yvalue))
def on_joy_hat(self, win, stickid, hatid, value):
self.joy_motion('hat', stickid, hatid, value)
def on_joy_button_down(self, win, stickid, buttonid):
print('button_down', stickid, buttonid)
def on_joy_button_up(self, win, stickid, buttonid):
print('button_up', stickid, buttonid)
class JoystickApp(App):
def build(self):
return Listener()
if __name__ == '__main__':
JoystickApp().run()

View file

@ -0,0 +1,51 @@
from kivy.app import App
from kivy.uix.button import Button
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
class DropFile(Button):
def __init__(self, **kwargs):
super(DropFile, self).__init__(**kwargs)
# get app instance to add function from widget
app = App.get_running_app()
# add function to the list
app.drops.append(self.on_drop_file)
def on_drop_file(self, widget, filename):
# a function catching a dropped file
# if it's dropped in the widget's area
if self.collide_point(*Window.mouse_pos):
# on_drop_file's filename is bytes (py3)
self.text = filename.decode('utf-8')
class DropApp(App):
def build(self):
# set an empty list that will be later populated
# with functions from widgets themselves
self.drops = []
# bind handling function to 'on_drop_file'
Window.bind(on_drop_file=self.handledrops)
box = BoxLayout()
dropleft = DropFile(text='left')
box.add_widget(dropleft)
dropright = DropFile(text='right')
box.add_widget(dropright)
return box
def handledrops(self, *args):
# this will execute each function from list with arguments from
# Window.on_drop_file
#
# make sure `Window.on_drop_file` works on your system first,
# otherwise the example won't work at all
for func in self.drops:
func(*args)
DropApp().run()

View file

@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
'''
on_textedit event sample.
'''
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.uix.textinput import TextInput
from kivy.base import EventLoop
class TextInputIME(TextInput):
testtext = StringProperty()
def __init__(self, **kwargs):
super(TextInputIME, self).__init__(**kwargs)
EventLoop.window.bind(on_textedit=self._on_textedit)
def _on_textedit(self, window, text):
self.testtext = text
class MainWidget(Widget):
text = StringProperty()
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
self.text = ''
def confim(self):
self.text = self.ids["text_box"].text
def changeFont(self):
try:
LabelBase.register(DEFAULT_FONT, self.ids["text_font"].text)
except Exception:
self.ids["text_font"].text = "can't load font."
class TextEditTestApp(App):
def __init__(self, **kwargs):
super(TextEditTestApp, self).__init__(**kwargs)
def build(self):
return MainWidget()
if __name__ == '__main__':
Builder.load_string('''
<MainWidget>:
BoxLayout:
orientation: 'vertical'
size: root.size
BoxLayout:
Label:
size_hint_x: 3
text: "Multi language font file path"
TextInput:
id: text_font
size_hint_x: 5
Button:
size_hint_x: 2
text: "Change Font"
on_press: root.changeFont()
BoxLayout:
Label:
size_hint_x: 3
text: "Text editing by IME"
Label:
size_hint_x: 7
text:text_box.testtext
canvas.before:
Color:
rgb: 0.5765 ,0.5765 ,0.5843
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
Label:
size_hint_x: 3
text: "Enter text ->"
TextInputIME:
id: text_box
size_hint_x: 7
focus: True
BoxLayout:
Button:
size_hint_x: 3
text: "Confirm text property"
on_press: root.confim()
Label:
size_hint_x: 7
text: root.text
canvas.before:
Color:
rgb: 0.5765 ,0.5765 ,0.5843
Rectangle:
pos: self.pos
size: self.size
''')
TextEditTestApp().run()

View file

@ -0,0 +1,28 @@
from kivy.app import App
from kivy.lang import Builder
kv = '''
#:import window kivy.core.window.Window
BoxLayout:
orientation: 'vertical'
Label:
text: f'Window opacity: {window.opacity}'
font_size: '25sp'
Slider:
size_hint_y: 4
min: 0.0
max: 1.0
value: window.opacity
on_value: window.opacity = args[1]
'''
class WindowOpacityApp(App):
def build(self):
return Builder.load_string(kv)
if __name__ == '__main__':
WindowOpacityApp().run()

View file

@ -0,0 +1,397 @@
# This is a simple demo for advanced collisions and mesh creation from a set
# of points. Its purpose is only to give an idea on how to make complex stuff.
# Check garden.collider for better performance.
from math import cos, sin, pi, sqrt
from random import random, randint
from itertools import combinations
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.graphics import Color, Mesh, Point
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import (
ListProperty,
StringProperty,
ObjectProperty,
NumericProperty
)
# Cloud polygon, 67 vertices + custom origin [150, 50]
cloud_poly = [
150, 50,
109.7597, 112.9600, 115.4326, 113.0853, 120.1966, 111.9883,
126.0889, 111.9570, 135.0841, 111.9570, 138.5944, 112.5525,
145.7403, 115.5301, 150.5357, 120.3256, 155.5313, 125.5938,
160.8438, 130.5000, 165.7813, 132.5000, 171.8125, 132.3438,
177.5000, 128.4688, 182.1531, 121.4990, 185.1438, 114.0406,
185.9181, 108.5649, 186.2226, 102.5978, 187.8059, 100.2231,
193.2257, 100.1622, 197.6712, 101.8671, 202.6647, 104.1809,
207.1102, 105.8858, 214.2351, 105.0333, 219.3747, 102.8301,
224.0413, 98.7589, 225.7798, 93.7272, 226.0000, 86.8750,
222.9375, 81.0625, 218.3508, 76.0867, 209.8301, 70.8090,
198.7806, 66.1360, 189.7651, 62.2327, 183.6082, 56.6252,
183.2784, 50.5778, 190.9155, 42.7294, 196.8470, 36.1343,
197.7339, 29.9272, 195.5720, 23.4430, 191.2500, 15.9803,
184.0574, 9.5882, 175.8811, 3.9951, 165.7992, 3.4419,
159.0369, 7.4370, 152.5205, 14.8125, 147.4795, 24.2162,
142.4385, 29.0103, 137.0287, 30.9771, 127.1560, 27.4818,
119.1371, 20.0388, 112.1820, 11.3690, 104.6541, 7.1976,
97.2080, 6.2979, 88.9437, 9.8149, 80.3433, 17.3218,
76.5924, 26.5452, 78.1678, 37.0432, 83.5068, 47.1104,
92.8529, 58.3561, 106.3021, 69.2978, 108.9615, 73.9329,
109.0375, 80.6955, 104.4713, 88.6708, 100.6283, 95.7483,
100.1226, 101.5114, 102.8532, 107.2745, 105.6850, 110.9144,
109.7597, 112.9600
]
class BaseShape(Widget):
'''(internal) Base class for moving with touches or calls.'''
# keep references for offset
_old_pos = ListProperty([0, 0])
_old_touch = ListProperty([0, 0])
_new_touch = ListProperty([0, 0])
# shape properties
name = StringProperty('')
poly = ListProperty([])
shape = ObjectProperty()
poly_len = NumericProperty(0)
shape_len = NumericProperty(0)
debug_collider = ObjectProperty()
debug_collider_len = NumericProperty(0)
def __init__(self, **kwargs):
'''Create a shape with size [100, 100]
and give it a label if it's named.
'''
super(BaseShape, self).__init__(**kwargs)
self.size_hint = (None, None)
self.add_widget(Label(text=self.name))
def move_label(self, x, y, *args):
'''Move label with shape name as the only child.'''
self.children[0].pos = [x, y]
def move_collider(self, offset_x, offset_y, *args):
'''Move debug collider when the shape moves.'''
points = self.debug_collider.points[:]
for i in range(0, self.debug_collider_len, 2):
points[i] += offset_x
points[i + 1] += offset_y
self.debug_collider.points = points
def on_debug_collider(self, instance, value):
'''Recalculate length of collider points' array.'''
self.debug_collider_len = len(value.points)
def on_poly(self, instance, value):
'''Recalculate length of polygon points' array.'''
self.poly_len = len(value)
def on_shape(self, instance, value):
'''Recalculate length of Mesh vertices' array.'''
self.shape_len = len(value.vertices)
def on_pos(self, instance, pos):
'''Move polygon and its Mesh on each position change.
This event is above all and changes positions of the other
children-like components, so that a simple::
shape.pos = (100, 200)
would move everything, not just the widget itself.
'''
# position changed by touch
offset_x = self._new_touch[0] - self._old_touch[0]
offset_y = self._new_touch[1] - self._old_touch[1]
# position changed by call (shape.pos = X)
if not offset_x and not offset_y:
offset_x = pos[0] - self._old_pos[0]
offset_y = pos[1] - self._old_pos[1]
self._old_pos = pos
# move polygon points by offset
for i in range(0, self.poly_len, 2):
self.poly[i] += offset_x
self.poly[i + 1] += offset_y
# stick label to bounding box (widget)
if self.name:
self.move_label(*pos)
# move debug collider if available
if self.debug_collider is not None:
self.move_collider(offset_x, offset_y)
# return if no Mesh available
if self.shape is None:
return
# move Mesh vertices by offset
points = self.shape.vertices[:]
for i in range(0, self.shape_len, 2):
points[i] += offset_x
points[i + 1] += offset_y
self.shape.vertices = points
def on_touch_move(self, touch, *args):
'''Move shape with dragging.'''
# grab single touch for shape
if touch.grab_current is not self:
return
# get touches
x, y = touch.pos
new_pos = [x, y]
self._new_touch = new_pos
self._old_touch = [touch.px, touch.py]
# get offsets, move & trigger on_pos event
offset_x = self._new_touch[0] - self._old_touch[0]
offset_y = self._new_touch[1] - self._old_touch[1]
self.pos = [self.x + offset_x, self.y + offset_y]
def shape_collide(self, x, y, *args):
'''Point to polygon collision through a list of points.'''
# ignore if no polygon area is set
poly = self.poly
if not poly:
return False
n = self.poly_len
inside = False
p1x = poly[0]
p1y = poly[1]
# compare point pairs via PIP algo, too long, read
# https://en.wikipedia.org/wiki/Point_in_polygon
for i in range(0, n + 2, 2):
p2x = poly[i % n]
p2y = poly[(i + 1) % n]
if y > min(p1y, p2y) and y <= max(p1y, p2y) and x <= max(p1x, p2x):
if p1y != p2y:
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
if p1x == p2x or x <= xinters:
inside = not inside
p1x, p1y = p2x, p2y
return inside
class RegularShape(BaseShape):
'''Starting from center and creating edges around for i.e.:
regular triangles, squares, regular pentagons, up to "circle".
'''
def __init__(self, edges=3, color=None, **kwargs):
super(RegularShape, self).__init__(**kwargs)
if edges < 3:
raise Exception('Not enough edges! (3+ only)')
color = color or [random() for i in range(3)]
rad_edge = (pi * 2) / float(edges)
r_x = self.width / 2.0
r_y = self.height / 2.0
poly = []
vertices = []
for i in range(edges):
# get points within a circle with radius of [r_x, r_y]
x = cos(rad_edge * i) * r_x + self.center_x
y = sin(rad_edge * i) * r_y + self.center_y
poly.extend([x, y])
# add UV layout zeros for Mesh, see Mesh docs
vertices.extend([x, y, 0, 0])
# draw Mesh shape from generated poly points
with self.canvas:
Color(rgba=(color[0], color[1], color[2], 0.6))
self.shape = Mesh(
pos=self.pos,
vertices=vertices,
indices=list(range(edges)),
mode='triangle_fan'
)
self.poly = poly
def on_touch_down(self, touch, *args):
if self.shape_collide(*touch.pos):
touch.grab(self)
class MeshShape(BaseShape):
'''Starting from a custom origin and custom points, draw
a convex Mesh shape with both touch and shape collisions.
.. note::
To get the points, use e.g. Pen tool from your favorite
graphics editor and export it to a human readable format.
'''
def __init__(self, color=None, **kwargs):
super(MeshShape, self).__init__(**kwargs)
color = color or [random() for i in range(3)]
min_x = 10000
min_y = 10000
max_x = 0
max_y = 0
# first point has to be the center of the convex shape's mass,
# that's where the triangle fan starts from
poly = [
50, 50, 0, 0, 100, 0, 100, 100, 0, 100
] if not self.poly else self.poly
# make the polygon smaller to fit 100x100 bounding box
poly = [round(p / 1.5, 4) for p in poly]
poly_len = len(poly)
# create list of vertices & get edges of the polygon
vertices = []
vertices_len = 0
for i in range(0, poly_len, 2):
min_x = poly[i] if poly[i] < min_x else min_x
min_y = poly[i + 1] if poly[i + 1] < min_y else min_y
max_x = poly[i] if poly[i] > max_x else max_x
max_y = poly[i + 1] if poly[i + 1] > max_y else max_y
# add UV layout zeros for Mesh
vertices_len += 4
vertices.extend([poly[i], poly[i + 1], 0, 0])
# get center of poly from edges
poly_center_x, poly_center_y = [
(max_x - min_x) / 2.0,
(max_y - min_y) / 2.0
]
# get distance from the widget's center and push the points to
# the widget's origin, so that min_x and min_y for the poly would
# result in 0 i.e.: points moved as close as possible to [0, 0]
# -> No editor gives poly points moved to the origin directly
dec_x = (self.center_x - poly_center_x) - min_x
dec_y = (self.center_y - poly_center_y) - min_y
# move polygon points to the bounding box (touch)
for i in range(0, poly_len, 2):
poly[i] += dec_x
poly[i + 1] += dec_y
# move mesh points to the bounding box (image)
# has to contain the same points as polygon
for i in range(0, vertices_len, 4):
vertices[i] += dec_x
vertices[i + 1] += dec_y
# draw Mesh shape from generated poly points
with self.canvas:
Color(rgba=(color[0], color[1], color[2], 0.6))
self.shape = Mesh(
pos=self.pos,
vertices=vertices,
indices=list(range(int(poly_len / 2.0))),
mode='triangle_fan'
)
# debug polygon points with Line to see the origin point
# and intersections with the other points
# Line(points=poly)
self.poly = poly
def on_touch_down(self, touch, *args):
if self.shape_collide(*touch.pos):
touch.grab(self)
class Collisions(App):
def __init__(self, **kwargs):
super(Collisions, self).__init__(**kwargs)
# register an event for collision
self.register_event_type('on_collision')
def collision_circles(self, shapes=None, distance=100, debug=False, *args):
'''Simple circle <-> circle collision between the shapes i.e. there's
a simple line between the centers of the two shapes and the collision
is only about measuring distance -> 1+ radii intersections.
'''
# get all combinations from all available shapes
if not hasattr(self, 'combins'):
self.combins = list(combinations(shapes, 2))
for com in self.combins:
x = (com[0].center_x - com[1].center_x) ** 2
y = (com[0].center_y - com[1].center_y) ** 2
if sqrt(x + y) <= distance:
# dispatch a custom event if the objects collide
self.dispatch('on_collision', (com[0], com[1]))
# draw collider only if debugging
if not debug:
return
# add circle collider only if the shape doesn't have one
for shape in shapes:
if shape.debug_collider is not None:
continue
d = distance / 2.0
cx, cy = shape.center
points = [(cx + d * cos(i), cy + d * sin(i)) for i in range(44)]
points = [p for ps in points for p in ps]
with shape.canvas:
Color(rgba=(0, 1, 0, 1))
shape.debug_collider = Point(points=points)
def on_collision(self, pair, *args):
'''Dispatched when objects collide, gives back colliding objects
as a "pair" argument holding their instances.
'''
print('Collision {} x {}'.format(pair[0].name, pair[1].name))
def build(self):
# the environment for all 2D shapes
scene = FloatLayout()
# list of 2D shapes, starting with regular ones
shapes = [
RegularShape(
name='Shape {}'.format(x), edges=x
) for x in range(3, 13)
]
shapes.append(MeshShape(name='DefaultMesh'))
shapes.append(MeshShape(name='Cloud', poly=cloud_poly))
shapes.append(MeshShape(
name='3QuarterCloud',
poly=cloud_poly[:110]
))
# move shapes to some random position
for shape in shapes:
shape.pos = [randint(50, i - 50) for i in Window.size]
scene.add_widget(shape)
# check for simple collisions between the shapes
Clock.schedule_interval(
lambda *t: self.collision_circles(shapes, debug=True), 0.1)
return scene
if __name__ == '__main__':
Collisions().run()

View file

@ -0,0 +1,98 @@
from kivy.config import Config
Config.set('graphics', 'shaped', 1)
from kivy.resources import resource_find
default_shape = Config.get('kivy', 'window_shape')
alpha_shape = resource_find('data/logo/kivy-icon-512.png')
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import (
BooleanProperty,
StringProperty,
ListProperty,
)
Builder.load_string('''
#:import win kivy.core.window.Window
<Root>:
orientation: 'vertical'
BoxLayout:
Button:
text: 'default_shape'
on_release: app.shape_image = app.default_shape
Button:
text: 'alpha_shape'
on_release: app.shape_image = app.alpha_shape
BoxLayout:
ToggleButton:
group: 'mode'
text: 'default'
state: 'down'
on_release: win.shape_mode = 'default'
ToggleButton:
group: 'mode'
text: 'binalpha'
on_release: win.shape_mode = 'binalpha'
ToggleButton:
group: 'mode'
text: 'reversebinalpha'
on_release: win.shape_mode = 'reversebinalpha'
ToggleButton:
group: 'mode'
text: 'colorkey'
on_release: win.shape_mode = 'colorkey'
BoxLayout:
ToggleButton:
group: 'cutoff'
text: 'cutoff True'
state: 'down'
on_release: win.shape_cutoff = True
ToggleButton:
group: 'cutoff'
text: 'cutoff False'
on_release: win.shape_cutoff = False
BoxLayout:
ToggleButton:
group: 'colorkey'
text: '1, 1, 1, 1'
state: 'down'
on_release: win.shape_color_key = [1, 1, 1, 1]
ToggleButton:
group: 'colorkey'
text: '0, 0, 0, 1'
on_release: win.shape_color_key = [0, 0, 0, 1]
''')
class Root(BoxLayout):
pass
class ShapedWindow(App):
shape_image = StringProperty('', force_dispatch=True)
def on_shape_image(self, instance, value):
if 'kivy-icon' in value:
Window.size = (512, 512)
Window.shape_image = self.alpha_shape
else:
Window.size = (800, 600)
Window.shape_image = self.default_shape
def build(self):
self.default_shape = default_shape
self.alpha_shape = alpha_shape
return Root()
if __name__ == '__main__':
ShapedWindow().run()

View file

@ -0,0 +1,47 @@
'''
Demonstrates using kv language to create some simple buttons and a
label, with each button modifying the label text.
'''
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string('''
<MainWidget>:
BoxLayout:
orientation: 'vertical'
Button:
text: 'some string '
on_press: the_right_pane.text += self.text
Button:
text: 'one two three four '
on_press: the_right_pane.text += self.text
Button:
text: 'follow the yellow brick road '
on_press: the_right_pane.text += self.text
Button:
text: 'five six seven eight '
on_press: the_right_pane.text += self.text
Button:
text: 'CLEAR LABEL'
on_press: the_right_pane.text = ''
Label:
id: the_right_pane
text: ''
text_size: self.size
halign: 'center'
valign: 'middle'
''')
class MainWidget(BoxLayout):
pass
class ExampleApp(App):
def build(self):
return MainWidget()
ExampleApp().run()

View file

@ -0,0 +1,128 @@
from kivy.lang import Builder
from kivy.app import App
from kivy.network.urlrequest import UrlRequest
from kivy.properties import NumericProperty, StringProperty, DictProperty
import json
KV = '''
#:import json json
#:import C kivy.utils.get_color_from_hex
BoxLayout:
orientation: 'vertical'
Label:
text: 'see https://httpbin.org for more information'
TextInput:
id: ti
hint_text: 'type url or select from dropdown'
size_hint_y: None
height: 48
multiline: False
foreground_color:
(
C('000000')
if (self.text).startswith('http') else
C('FF2222')
)
BoxLayout:
size_hint_y: None
height: 48
Spinner:
id: spinner
text: 'select'
values:
[
'http://httpbin.org/ip',
'http://httpbin.org/user-agent',
'http://httpbin.org/headers',
'http://httpbin.org/delay/3',
'http://httpbin.org/image/jpeg',
'http://httpbin.org/image/png',
'https://httpbin.org/delay/3',
'https://httpbin.org/image/jpeg',
'https://httpbin.org/image/png',
]
on_text: ti.text = self.text
Button:
text: 'GET'
on_press: app.fetch_content(ti.text)
disabled: not (ti.text).startswith('http')
size_hint_x: None
width: 50
Label:
text: str(app.status)
TextInput:
readonly: True
text: app.result_text
Image:
source: app.result_image
nocache: True
TextInput
readonly: True
text: json.dumps(app.headers, indent=2)
'''
class UrlExample(App):
status = NumericProperty()
result_text = StringProperty()
result_image = StringProperty()
headers = DictProperty()
def build(self):
return Builder.load_string(KV)
def fetch_content(self, url):
self.cleanup()
UrlRequest(
url,
on_success=self.on_success,
on_failure=self.on_failure,
on_error=self.on_error
)
def cleanup(self):
self.result_text = ''
self.result_image = ''
self.status = 0
self.headers = {}
def on_success(self, req, result):
self.cleanup()
headers = req.resp_headers
content_type = headers.get('content-type', headers.get('Content-Type'))
if content_type.startswith('image/'):
fn = 'tmpfile.{}'.format(content_type.split('/')[1])
with open(fn, 'wb') as f:
f.write(result)
self.result_image = fn
else:
if isinstance(result, dict):
self.result_text = json.dumps(result, indent=2)
else:
self.result_text = result
self.status = req.resp_status
self.headers = headers
def on_failure(self, req, result):
self.cleanup()
self.result_text = result
self.status = req.resp_status
self.headers = req.resp_headers
def on_error(self, req, result):
self.cleanup()
self.result_text = str(result)
if __name__ == '__main__':
UrlExample().run()