Requirements.txt:
Код: Выделить всё
appdirs==1.4.4
build==1.2.2.post1
buildozer==1.5.0
certifi==2024.8.30
charset-normalizer==3.4.0
colorama==0.4.6
Cython==3.0.11
distlib==0.3.9
docutils==0.21.2
filelock==3.16.1
idna==3.10
Jinja2==3.1.4
Kivy==2.3.0
Kivy-Garden==0.1.5
kivymd==1.1.1
MarkupSafe==3.0.2
numpy==2.1.3
opencv-python==4.10.0.84
packaging==24.2
pexpect==4.9.0
pillow==10.4.0
platformdirs==4.3.6
plyer==2.1.0
ptyprocess==0.7.0
Pygments==2.18.0
pyjnius==1.5.0
pyproject_hooks==1.2.0
python-for-android==2024.1.21
requests==2.32.3
sh==1.14.3
six==1.16.0
toml==0.10.2
urllib3==2.2.3
virtualenv==20.27.1
Код: Выделить всё
[app]
# (str) Title of your application
title = My Application
# (str) Package name
package.name = myapp
# (str) Package domain (needed for android/ios packaging)
package.domain = org.test
# (str) Source code where the main.py live
source.dir = .
# (list) Source files to include (let empty to include all the files)
source.include_exts = py,png,jpg,kv,atlas
# (list) List of inclusions using pattern matching
#source.include_patterns = assets/*,images/*.png
# (list) Source files to exclude (let empty to not exclude anything)
#source.exclude_exts = spec
# (list) List of directory to exclude (let empty to not exclude anything)
#source.exclude_dirs = tests, bin, venv
# (list) List of exclusions using pattern matching
# Do not prefix with './'
#source.exclude_patterns = license,images/*/*.jpg
# (str) Application versioning (method 1)
version = 0.1
# (str) Application versioning (method 2)
# version.regex = __version__ = ['"](.*)['"]
# version.filename = %(source.dir)s/main.py
# (list) Application requirements
# comma separated e.g. requirements = sqlite3,kivy
requirements = python3,kivy,pillow,plyer,opencv,pyjnius,numpy
# (str) Custom source folders for requirements
# Sets custom source for any requirements with recipes
# requirements.source.kivy = ../../kivy
# (str) Presplash of the application
#presplash.filename = %(source.dir)s/data/presplash.png
# (str) Icon of the application
#icon.filename = %(source.dir)s/data/icon.png
# (list) Supported orientations
# Valid options are: landscape, portrait, portrait-reverse or landscape-reverse
orientation = portrait
# (list) List of service to declare
#services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY
#
# OSX Specific
#
#
# author = © Copyright Info
# change the major version of python used by the app
osx.python_version = 3
# Kivy version to use
osx.kivy_version = 1.9.1
#
# Android specific
#
# (bool) Indicate if the application should be fullscreen or not
fullscreen = 0
# (string) Presplash background color (for android toolchain)
# Supported formats are: #RRGGBB #AARRGGBB or one of the following names:
# red, blue, green, black, white, gray, cyan, magenta, yellow, lightgray,
# darkgray, grey, lightgrey, darkgrey, aqua, fuchsia, lime, maroon, navy,
# olive, purple, silver, teal.
#android.presplash_color = #FFFFFF
# (string) Presplash animation using Lottie format.
# see https://lottiefiles.com/ for examples and https://airbnb.design/lottie/
# for general documentation.
# Lottie files can be created using various tools, like Adobe After Effect or Synfig.
#android.presplash_lottie = "path/to/lottie/file.json"
# (str) Adaptive icon of the application (used if Android API level is 26+ at runtime)
#icon.adaptive_foreground.filename = %(source.dir)s/data/icon_fg.png
#icon.adaptive_background.filename = %(source.dir)s/data/icon_bg.png
# (list) Permissions
# (See https://python-for-android.readthedocs.io/en/latest/buildoptions/#build-options-1 for all the supported syntaxes and properties)
android.permissions = INTERNET, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE, CAMERA
Код: Выделить всё
2024-12-06 01:45:53.530 5553-5580 python org.test.myapp I Error: Could not open OpenCV camera
2024-12-06 01:45:53.530 5553-5580 python org.test.myapp I Error code:
2024-12-06 01:45:53.530 5553-5580 python org.test.myapp I Available cameras:
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I General configuration for OpenCV 4.5.1 =====================================
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I Version control: unknown
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I Platform:
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I Timestamp: 2024-12-06T04:42:42Z
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I Host: Linux 6.5.0-44-generic x86_64
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I Target: Android 1 aarch64
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I CMake: 3.27.4
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I CMake generator: Unix Makefiles
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I CMake build tool: /usr/bin/gmake
2024-12-06 01:45:53.531 5553-5580 python org.test.myapp I Configuration: Release
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I CPU/HW features:
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I Baseline: NEON FP16
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I C/C++:
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I Built as dynamic libs?: YES
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I C++ standard: 11
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I C++ Compiler: /home/nate/.buildozer/android/platform/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ (ver 14.0.6)
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I C++ flags (Release): -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winconsistent-missing-override -Wno-delete-non-virtual-dtor -Wno-unnamed-type-template-args -Wno-comment -Wno-deprecated-enum-enum-conversion -Wno-deprecated-anon-enum-enum-conversion -fdiagnostics-show-option -Qunused-arguments -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -DNDEBUG
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I C++ flags (Debug): -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winconsistent-missing-override -Wno-delete-non-virtual-dtor -Wno-unnamed-type-template-args -Wno-comment -Wno-deprecated-enum-enum-conversion -Wno-deprecated-anon-enum-enum-conversion -fdiagnostics-show-option -Qunused-arguments -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden -fno-limit-debug-info -O0 -DDEBUG -D_DEBUG
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I C Compiler: /home/nate/.buildozer/android/platform/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/bin/clang
2024-12-06 01:45:53.532 5553-5580 python org.test.myapp I C flags (Release): -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winconsistent-missing-override -Wno-delete-non-virtual-dtor -Wno-unnamed-type-template-args -Wno-comment -Wno-deprecated-enum-enum-conversion -Wno-deprecated-anon-enum-enum-conversion -fdiagnostics-show-option -Qunused-arguments -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -DNDEBUG
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I C flags (Debug): -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winconsistent-missing-override -Wno-delete-non-virtual-dtor -Wno-unnamed-type-template-args -Wno-comment -Wno-deprecated-enum-enum-conversion -Wno-deprecated-anon-enum-enum-conversion -fdiagnostics-show-option -Qunused-arguments -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden -fno-limit-debug-info -O0 -DDEBUG -D_DEBUG
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I Linker flags (Release): -static-libstdc++ -Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,--fatal-warnings -Wl,--gc-sections -Wl,--no-undefined -Qunused-arguments -L/home/nate/myapp/.buildozer/android/platform/build-arm64-v8a_armeabi-v7a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/android-build -lpython3.11 -Wl,--gc-sections -Wl,--as-needed
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I Linker flags (Debug): -static-libstdc++ -Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,--fatal-warnings -Wl,--gc-sections -Wl,--no-undefined -Qunused-arguments -L/home/nate/myapp/.buildozer/android/platform/build-arm64-v8a_armeabi-v7a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/android-build -lpython3.11 -Wl,--gc-sections -Wl,--as-needed
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I ccache: YES
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I Precompiled headers: NO
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I Extra dependencies: dl m log
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I 3rdparty dependencies:
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I OpenCV modules:
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I To be built: calib3d core dnn features2d flann gapi highgui imgcodecs imgproc ml objdetect photo python3 stitching video videoio
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I Disabled: java java_bindings_generator world
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I Disabled by dependency: -
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I Unavailable: python2 ts
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I Applications: -
2024-12-06 01:45:53.533 5553-5580 python org.test.myapp I Documentation: NO
2024-12-06 01:45:53.534 5553-5580 python org.test.myapp I Non-free algorithms: NO
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I Android NDK: /home/nate/.buildozer/android/platform/android-ndk-r25b (ver 25.1.8937393)
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I Android ABI: arm64-v8a
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I NDK toolchain: standalone: /home/nate/.buildozer/android/platform/android-ndk-r25b
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I STL type: c++_static
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I Native API level: 21
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I Android SDK: /home/nate/.buildozer/android/platform/android-sdk (tools: 6514223 build tools: 36.0.0-rc1)
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I android tool: /home/nate/.buildozer/android/platform/android-sdk/tools/android
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I GUI:
2024-12-06 01:45:53.535 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Media I/O:
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I ZLib: /home/nate/.buildozer/android/platform/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/21/libz.so (ver 1.2.12)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I JPEG: build-libjpeg-turbo (ver 2.0.6-62)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I WEBP: build (ver encoder: 0x020f)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I PNG: build (ver 1.6.37)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I TIFF: build (ver 42 - 4.0.10)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I JPEG 2000: build (ver 2.3.1)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I OpenEXR: build (ver 2.3.0)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I HDR: YES
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I SUNRASTER: YES
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I PXM: YES
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I PFM: YES
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Video I/O:
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Parallel framework: pthreads
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Trace: YES (with Intel ITT)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Other third-party libraries:
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Custom HAL: YES (carotene (ver 0.0.1))
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Protobuf: build (3.5.1)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Python 3:
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Interpreter: /home/nate/myapp/.buildozer/android/platform/build-arm64-v8a_armeabi-v7a/build/other_builds/hostpython3/desktop/hostpython3/native-build/python3 (ver 3.11.5)
2024-12-06 01:45:53.536 5553-5580 python org.test.myapp I Libraries: /home/nate/myapp/.buildozer/android/platform/build-arm64-v8a_armeabi-v7a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/android-build/libpython3.11.so
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I numpy: /home/nate/myapp/.buildozer/android/platform/build-arm64-v8a_armeabi-v7a/build/python-installs/myapp/arm64-v8a/numpy/core/include (ver undefined - cannot be probed because of the cross-compilation)
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I install path: /home/nate/myapp/.buildozer/android/platform/build-arm64-v8a_armeabi-v7a/build/python-installs/myapp/arm64-v8a
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I Python (for build): /home/nate/myapp/.buildozer/android/platform/build-arm64-v8a_armeabi-v7a/build/other_builds/hostpython3/desktop/hostpython3/native-build/python3
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I Java:
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I ant: NO
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I Java wrappers: NO
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I Java tests: NO
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I Install to: /home/nate/myapp/.buildozer/android/platform/build-arm64-v8a_armeabi-v7a/build/other_builds/opencv/arm64-v8a__ndk_target_21/opencv/build/install
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I -----------------------------------------------------------------
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I
2024-12-06 01:45:53.537 5553-5580 python org.test.myapp I
Код: Выделить всё
import cv2
from kivy.app import App
from kivy.lang import Builder #building files correctly
from kivy.uix.button import Button #for the buttons
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout # for the layout (there are different types of layouts in kivy this is just the most basic one)
from kivy.uix.floatlayout import FloatLayout # for non dynamic layout so the buttons dont move for camera page
from kivy.uix.screenmanager import ScreenManager, Screen #for the screen manager to track which screen is being shown
from kivy.uix.label import Label # for the label like headers
from kivy.uix.widget import Widget # adds widget for each of the classes
from kivy.uix.dropdown import DropDown # adds dropdown widgets
from kivy.graphics import Color, Ellipse, Rectangle # adds color to the circle
from kivy.graphics.texture import Texture #used for OpenCV image data
from kivy.core.window import Window # Sets the background color for app
from kivy.uix.camera import Camera #Kivy's built-in Camera widget
from kivy.uix.popup import Popup #For dialog window for popups when needed
from kivy.uix.image import Image #Widgets for displaying selected images
from kivy.properties import NumericProperty, StringProperty # control text values
from kivy.utils import get_color_from_hex # color translating from hex to rgba
from plyer import filechooser #From plyer library for image selection
from PIL import Image as PILImage #Python Image Library
from PIL import ImageDraw, ImageFont #Python Image Library
from kivy.clock import Clock #Used to capture frames form OpenCV Camera
import os #Python module for operating system interactions
import numpy as np #Used for handling image data from OpenCV's numpy arrays
from kivy.utils import platform
if platform == "android":
from android.permissions import request_permissions, Permission #NOT AN ACTUAL ERROR ON ANDROID
request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE, Permission.CAMERA, Permission.INTERNET])
class CameraPage(Screen):
def __init__(self, **kwargs):
super(CameraPage, self).__init__(**kwargs)
self.cap = None # OpenCV VideoCapture
self.image_cache = None
self.frame_event = None
self.current_frame = None # Captures the current frame
# Start OpenCV camera when user is in the camera page
def on_enter(self, *args):
self.start_opencv_camera()
# Stop OpenCV camera when user exits the camera page
def on_leave(self, *args):
if self.cap:
self.cap.release() # Release the camera
print("Camera has been released.")
if self.frame_event:
self.frame_event.cancel() # Cancel frame update
# Start OpenCV camera
def start_opencv_camera(self):
self.cap = cv2.VideoCapture(0) # Useing camera index 0 for default camera
if not self.cap.isOpened():
print("Error: Could not open OpenCV camera")
print("Error code:", cv2.error) # This might provide more insights into the error
print("Available cameras:", cv2.getBuildInformation())
else:
print("OpenCV camera started.")
# Schedule the update of camera frames by 30 FPS
self.frame_event = Clock.schedule_interval(self.update_camera_feed, 1.0 / 15)
# Update the Image widget with the camera feed
def update_camera_feed(self, dt):
if self.cap:
ret, frame = self.cap.read()
if not ret:
print("Error: Failed to capture frame.")
else:
self.current_frame = frame # Store the current frame
# Convert BGR frame to RGB
buf = cv2.flip(frame, 0).tostring()
texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
self.ids.camera_feed.texture = texture
# Capture image using the current frame from OpenCV
def capture_opencv_image(self):
if self.current_frame is not None:
# Convert OpenCV frame (BGR) to PIL Image (RGB)
pil_image = PILImage.fromarray(cv2.cvtColor(self.current_frame, cv2.COLOR_BGR2RGB))
# Save to cache directory
cache_dir = './image_cache'
if not os.path.exists(cache_dir):
os.mkdir(cache_dir)
cached_image_path = os.path.join(cache_dir, 'captured_image.png')
pil_image.save(cached_image_path)
self.image_cache = cached_image_path
print(f"Image captured and cached at: {cached_image_path}")
else:
print("Error: No frame available to capture")
# Function that opens gallery works for windows and should work for android
def open_gallery(self, *args):
# Opens the file chooser to select images
filechooser.open_file(on_selection=self.display_image)
# Display the selected image in a popup
def display_image(self, selection):
if selection:
# Cache for the selected image
self.image_cache = selection[0]
# Create/open popup
image_popup = ImagePopup()
image_popup.ids.img.source = self.image_cache
image_popup.open()
# Cache the selected image
def cache_image(self, instance):
# Checks if cache location exists
if self.image_cache:
cache_dir = './image_cache' # Cache directory
if not os.path.exists(cache_dir):
os.mkdir(cache_dir) # Create cache location if does not existing
# Select a location/path for the cached image
cached_image_path = os.path.join(cache_dir, 'cached_image.png')
try:
with open(self.image_cache, 'rb') as source_file:
with open(cached_image_path, 'wb') as dest_file:
dest_file.write(source_file.read())
print(f"Image cached at: {cached_image_path}")
except Exception as e:
print(f"Error caching the image: {e}")
else:
print("Error: No image selected for caching")
Подробнее здесь: https://stackoverflow.com/questions/792 ... ython-kivy