first commit
This commit is contained in:
commit
417e54da96
5696 changed files with 900003 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
63
kivy_venv/share/kivy-examples/miscellaneous/clipboard.py
Normal file
63
kivy_venv/share/kivy-examples/miscellaneous/clipboard.py
Normal 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())
|
|
@ -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()
|
14
kivy_venv/share/kivy-examples/miscellaneous/imagesave.py
Normal file
14
kivy_venv/share/kivy-examples/miscellaneous/imagesave.py
Normal 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()))
|
83
kivy_venv/share/kivy-examples/miscellaneous/joystick.py
Normal file
83
kivy_venv/share/kivy-examples/miscellaneous/joystick.py
Normal 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()
|
|
@ -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()
|
104
kivy_venv/share/kivy-examples/miscellaneous/on_textedit_event.py
Normal file
104
kivy_venv/share/kivy-examples/miscellaneous/on_textedit_event.py
Normal 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()
|
28
kivy_venv/share/kivy-examples/miscellaneous/opacitywindow.py
Normal file
28
kivy_venv/share/kivy-examples/miscellaneous/opacitywindow.py
Normal 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()
|
397
kivy_venv/share/kivy-examples/miscellaneous/shapecollisions.py
Normal file
397
kivy_venv/share/kivy-examples/miscellaneous/shapecollisions.py
Normal 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()
|
98
kivy_venv/share/kivy-examples/miscellaneous/shapedwindow.py
Normal file
98
kivy_venv/share/kivy-examples/miscellaneous/shapedwindow.py
Normal 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()
|
47
kivy_venv/share/kivy-examples/miscellaneous/two_panes.py
Normal file
47
kivy_venv/share/kivy-examples/miscellaneous/two_panes.py
Normal 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()
|
128
kivy_venv/share/kivy-examples/miscellaneous/urlrequest.py
Normal file
128
kivy_venv/share/kivy-examples/miscellaneous/urlrequest.py
Normal 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()
|
Loading…
Add table
Add a link
Reference in a new issue