diff --git a/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonUtil.java b/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonUtil.java index b6a9200f4f..c1180addfa 100644 --- a/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonUtil.java +++ b/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonUtil.java @@ -34,6 +34,7 @@ protected static ArrayList getLibraries(File libsDir) { addLibraryIfExists(libsList, "crystax", libsDir); addLibraryIfExists(libsList, "sqlite3", libsDir); addLibraryIfExists(libsList, "ffi", libsDir); + addLibraryIfExists(libsList, "png16", libsDir); libsList.add("SDL2"); libsList.add("SDL2_image"); libsList.add("SDL2_mixer"); diff --git a/pythonforandroid/recipes/Pillow/__init__.py b/pythonforandroid/recipes/Pillow/__init__.py index 14c9d2b26c..f4cbad9f5b 100644 --- a/pythonforandroid/recipes/Pillow/__init__.py +++ b/pythonforandroid/recipes/Pillow/__init__.py @@ -21,8 +21,8 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): ndk_include_dir = join(self.ctx.ndk_dir, 'sysroot', 'usr', 'include') png = self.get_recipe('png', self.ctx) - png_lib_dir = png.get_lib_dir(arch) - png_jni_dir = png.get_jni_dir(arch) + png_lib_dir = join(png.get_build_dir(arch.arch), '.libs') + png_inc_dir = png.get_build_dir(arch) jpeg = self.get_recipe('jpeg', self.ctx) jpeg_inc_dir = jpeg_lib_dir = jpeg.get_build_dir(arch.arch) @@ -41,7 +41,7 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): env['FREETYPE_ROOT'] = '{}|{}'.format(free_lib_dir, free_inc_dir) env['ZLIB_ROOT'] = '{}|{}'.format(ndk_lib_dir, ndk_include_dir) - cflags = ' -I{}'.format(png_jni_dir) + cflags = ' -I{}'.format(png_inc_dir) cflags += ' -I{} -I{}'.format(harf_inc_dir, join(harf_inc_dir, 'src')) cflags += ' -I{}'.format(free_inc_dir) cflags += ' -I{}'.format(jpeg_inc_dir) diff --git a/pythonforandroid/recipes/matplotlib/__init__.py b/pythonforandroid/recipes/matplotlib/__init__.py new file mode 100644 index 0000000000..a25f66bd64 --- /dev/null +++ b/pythonforandroid/recipes/matplotlib/__init__.py @@ -0,0 +1,33 @@ + +from pythonforandroid.recipe import CppCompiledComponentsPythonRecipe + +from os.path import join + + +class MatplotlibRecipe(CppCompiledComponentsPythonRecipe): + + version = '3.0.3' + url = 'https://github.com/matplotlib/matplotlib/archive/v{version}.zip' + + depends = ['numpy', 'png', 'setuptools', 'freetype', 'kiwisolver'] + + python_depends = ['pyparsing', 'cycler', 'python-dateutil'] + + # We need to patch to: + # - make mpl build against the same numpy version as the numpy recipe + # (this could be done better by setting the target version dynamically) + # - prevent mpl trying to build TkAgg, which wouldn't work on Android anyway but has build issues + patches = ['mpl_android_fixes.patch'] + + call_hostpython_via_targetpython = False + + def prebuild_arch(self, arch): + with open(join(self.get_recipe_dir(), 'setup.cfg.template')) as fileh: + setup_cfg = fileh.read() + + with open(join(self.get_build_dir(arch), 'setup.cfg'), 'w') as fileh: + fileh.write(setup_cfg.format( + ndk_sysroot_usr=join(self.ctx.ndk_dir, 'sysroot', 'usr'))) + + +recipe = MatplotlibRecipe() diff --git a/pythonforandroid/recipes/matplotlib/mpl_android_fixes.patch b/pythonforandroid/recipes/matplotlib/mpl_android_fixes.patch new file mode 100644 index 0000000000..1ae826f22c --- /dev/null +++ b/pythonforandroid/recipes/matplotlib/mpl_android_fixes.patch @@ -0,0 +1,29 @@ +diff --git a/setupext.py b/setupext.py +index fc82d5d..2067db0 100644 +--- a/setupext.py ++++ b/setupext.py +@@ -1004,10 +1004,10 @@ class Numpy(SetupPackage): + ext.define_macros.append(('__STDC_FORMAT_MACROS', 1)) + + def get_setup_requires(self): +- return ['numpy>=1.10.0'] ++ return ['numpy==1.15.1'] # to match p4a's target version + + def get_install_requires(self): +- return ['numpy>=1.10.0'] ++ return ['numpy==1.15.1'] # to match p4a's target version + + + class LibAgg(SetupPackage): +@@ -1443,9 +1443,10 @@ class BackendAgg(OptionalBackendPackage): + + class BackendTkAgg(OptionalBackendPackage): + name = "tkagg" +- force = True ++ force = False + + def check(self): ++ raise CheckFailed("Disabled by patching during Android build") # tk doesn't work on Android but causes build problems + return "installing; run-time loading from Python Tcl / Tk" + + def get_extension(self): diff --git a/pythonforandroid/recipes/matplotlib/setup.cfg.template b/pythonforandroid/recipes/matplotlib/setup.cfg.template new file mode 100644 index 0000000000..68ff2f7782 --- /dev/null +++ b/pythonforandroid/recipes/matplotlib/setup.cfg.template @@ -0,0 +1,93 @@ +# Rename this file to setup.cfg to modify Matplotlib's +# build options. + +[egg_info] + +[directories] +# Uncomment to override the default basedir in setupext.py. +# This can be a single directory or a comma-delimited list of directories. +basedirlist = {ndk_sysroot_usr} + +[test] +# If you plan to develop Matplotlib and run or add to the test suite, +# set this to True. It will download and build a specific version of +# FreeType, and then use that to build the ft2font extension. This +# ensures that test images are exactly reproducible. +# local_freetype = True + +[status] +# To suppress display of the dependencies and their versions +# at the top of the build log, uncomment the following line: +#suppress = True + +[packages] +# There are a number of subpackages of Matplotlib that are considered +# optional. All except tests are installed by default, but that can +# be changed here. +# +tests = False +sample_data = False +#toolkits = True +# Tests for the toolkits are only automatically installed +# if the tests and toolkits packages are also getting installed. +toolkits_tests = False + +[gui_support] +# Matplotlib supports multiple GUI toolkits, including +# GTK3, MacOSX, Qt4, Qt5, Tk, and WX. Support for many of +# these toolkits requires AGG, the Anti-Grain Geometry library, +# which is provided by Matplotlib and built by default. +# +# Some backends are written in pure Python, and others require +# extension code to be compiled. By default, Matplotlib checks for +# these GUI toolkits during installation and, if present, compiles the +# required extensions to support the toolkit. +# +# - Tk support requires Tk development headers and Tkinter. +# - Mac OSX backend requires the Cocoa headers included with XCode. +# - Windowing is MS-Windows specific, and requires the "windows.h" +# header. +# +# The other GUI toolkits do not require any extension code, and can be +# used as long as the libraries are installed on your system -- +# therefore they are installed unconditionally. +# +# You can uncomment any the following lines to change this +# behavior. Acceptable values are: +# +# True: build the extension. Exits with a warning if the +# required dependencies are not available +# False: do not build the extension +# auto: build if the required dependencies are available, +# otherwise skip silently. This is the default +# behavior +# +agg = True +cairo = False +gtk3agg = False +gtk3cairo = False +macosx = False +pyside = False +qt4agg = False +tkagg = False +windowing = False +wxagg = False + +[rc_options] +# User-configurable options +# +# Default backend, one of: Agg, Cairo, GTK3Agg, GTK3Cairo, MacOSX, Pdf, Ps, +# Qt4Agg, Qt5Agg, SVG, TkAgg, WX, WXAgg. +# +# The Agg, Ps, Pdf and SVG backends do not require external dependencies. Do +# not choose MacOSX, or TkAgg if you have disabled the relevant extension +# modules. Agg will be used by default. +# +backend = Agg +# + +[package_data] +# Package additional files found in the lib/matplotlib directories. +# +# On Windows, package DLL files. +#dlls = True diff --git a/pythonforandroid/recipes/pil/__init__.py b/pythonforandroid/recipes/pil/__init__.py index f3ad2f42ef..92f633714f 100644 --- a/pythonforandroid/recipes/pil/__init__.py +++ b/pythonforandroid/recipes/pil/__init__.py @@ -1,79 +1,23 @@ -from os.path import join, exists -from pythonforandroid.recipe import CompiledComponentsPythonRecipe -from pythonforandroid.toolchain import shprint -import sh +from pythonforandroid.recipes.Pillow import PillowRecipe +from pythonforandroid.logger import warning -class PILRecipe(CompiledComponentsPythonRecipe): - name = 'pil' - version = '1.1.7' - url = 'http://effbot.org/downloads/Imaging-{version}.tar.gz' - depends = ['png', 'jpeg', 'setuptools'] - opt_depends = ['freetype'] - site_packages_name = 'PIL' +class PilRecipe(PillowRecipe): + """A transparent wrapper around the Pillow recipe, it should build + Pillow automatically as if "pillow" were specified in the + requirements. + """ - patches = ['disable-tk.patch', - 'fix-directories.patch'] + name = 'Pillow' # ensures the Pillow recipe directory is used where necessary - def get_recipe_env(self, arch=None, with_flags_in_cc=True): - env = super(PILRecipe, self).get_recipe_env(arch, with_flags_in_cc) + conflicts = ['pillow'] - env['PYTHON_INCLUDE_ROOT'] = self.ctx.python_recipe.include_root(arch.arch) - env['PYTHON_LINK_ROOT'] = self.ctx.python_recipe.link_root(arch.arch) + def build_arch(self, arch): + warning('PIL is no longer supported, building Pillow instead. ' + 'This should be a drop-in replacement.') + warning('It is recommended to change "pil" to "pillow" in your requirements, ' + 'to ensure future compatibility') + super(PilRecipe, self).build_arch(arch) - ndk_lib_dir = join(self.ctx.ndk_platform, 'usr', 'lib') - ndk_include_dir = join(self.ctx.ndk_dir, 'sysroot', 'usr', 'include') - png = self.get_recipe('png', self.ctx) - png_lib_dir = png.get_lib_dir(arch) - png_jni_dir = png.get_jni_dir(arch) - - jpeg = self.get_recipe('jpeg', self.ctx) - jpeg_inc_dir = jpeg_lib_dir = jpeg.get_build_dir(arch.arch) - - if 'freetype' in self.ctx.recipe_build_order: - freetype = self.get_recipe('freetype', self.ctx) - free_lib_dir = join(freetype.get_build_dir(arch.arch), 'objs', '.libs') - free_inc_dir = join(freetype.get_build_dir(arch.arch), 'include') - # hack freetype to be found by pil - freetype_link = join(free_inc_dir, 'freetype') - if not exists(freetype_link): - shprint(sh.ln, '-s', join(free_inc_dir), freetype_link) - - harfbuzz = self.get_recipe('harfbuzz', self.ctx) - harf_lib_dir = join(harfbuzz.get_build_dir(arch.arch), 'src', '.libs') - harf_inc_dir = harfbuzz.get_build_dir(arch.arch) - - env['FREETYPE_ROOT'] = '{}|{}'.format(free_lib_dir, free_inc_dir) - - env['JPEG_ROOT'] = '{}|{}'.format(jpeg_lib_dir, jpeg_inc_dir) - env['ZLIB_ROOT'] = '{}|{}'.format(ndk_lib_dir, ndk_include_dir) - - cflags = ' -std=c99' - cflags += ' -I{}'.format(png_jni_dir) - if 'freetype' in self.ctx.recipe_build_order: - cflags += ' -I{} -I{}'.format(harf_inc_dir, join(harf_inc_dir, 'src')) - cflags += ' -I{}'.format(free_inc_dir) - cflags += ' -I{}'.format(jpeg_inc_dir) - cflags += ' -I{}'.format(ndk_include_dir) - - py_v = self.ctx.python_recipe.major_minor_version_string - if py_v[0] == '3': - py_v += 'm' - - env['LIBS'] = ' -lpython{version} -lpng'.format(version=py_v) - if 'freetype' in self.ctx.recipe_build_order: - env['LIBS'] += ' -lfreetype -lharfbuzz' - env['LIBS'] += ' -ljpeg -lturbojpeg' - - env['LDFLAGS'] += ' -L{} -L{}'.format(env['PYTHON_LINK_ROOT'], png_lib_dir) - if 'freetype' in self.ctx.recipe_build_order: - env['LDFLAGS'] += ' -L{} -L{}'.format(harf_lib_dir, free_lib_dir) - env['LDFLAGS'] += ' -L{} -L{}'.format(jpeg_lib_dir, ndk_lib_dir) - - if cflags not in env['CFLAGS']: - env['CFLAGS'] += cflags - return env - - -recipe = PILRecipe() +recipe = PilRecipe() diff --git a/pythonforandroid/recipes/pil/disable-tk.patch b/pythonforandroid/recipes/pil/disable-tk.patch deleted file mode 100644 index c6934c9fdd..0000000000 --- a/pythonforandroid/recipes/pil/disable-tk.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- Imaging-1.1.7/setup.py.orig 2012-08-31 12:52:25.000000000 +0200 -+++ Imaging-1.1.7/setup.py 2012-08-31 12:53:04.000000000 +0200 -@@ -322,7 +322,7 @@ - "_imagingcms", ["_imagingcms.c"], libraries=["lcms"] + extra - )) - -- if sys.platform == "darwin": -+ if False: #sys.platform == "darwin": - # locate Tcl/Tk frameworks - frameworks = [] - framework_roots = [ - diff --git a/pythonforandroid/recipes/pil/fix-directories.patch b/pythonforandroid/recipes/pil/fix-directories.patch deleted file mode 100644 index b9daee3a1a..0000000000 --- a/pythonforandroid/recipes/pil/fix-directories.patch +++ /dev/null @@ -1,85 +0,0 @@ ---- pil/setup.py.orig 2009-11-15 17:06:10.000000000 +0100 -+++ pil/setup.py 2019-01-04 11:08:47.302974315 +0100 -@@ -34,10 +34,10 @@ def libinclude(root): - # TIFF_ROOT = libinclude("/opt/tiff") - - TCL_ROOT = None --JPEG_ROOT = None --ZLIB_ROOT = None -+JPEG_ROOT = tuple(os.environ['JPEG_ROOT'].split('|')) if 'JPEG_ROOT' in os.environ else None -+ZLIB_ROOT = tuple(os.environ['ZLIB_ROOT'].split('|')) if 'ZLIB_ROOT' in os.environ else None - TIFF_ROOT = None --FREETYPE_ROOT = None -+FREETYPE_ROOT = tuple(os.environ['FREETYPE_ROOT'].split('|')) if 'FREETYPE_ROOT' in os.environ else None - LCMS_ROOT = None - - # FIXME: add mechanism to explicitly *disable* the use of a library -@@ -127,33 +127,10 @@ class pil_build_ext(build_ext): - - add_directory(include_dirs, "libImaging") - -- # -- # add platform directories -- -- if sys.platform == "cygwin": -- # pythonX.Y.dll.a is in the /usr/lib/pythonX.Y/config directory -- add_directory(library_dirs, os.path.join( -- "/usr/lib", "python%s" % sys.version[:3], "config" -- )) -- -- elif sys.platform == "darwin": -- # attempt to make sure we pick freetype2 over other versions -- add_directory(include_dirs, "/sw/include/freetype2") -- add_directory(include_dirs, "/sw/lib/freetype2/include") -- # fink installation directories -- add_directory(library_dirs, "/sw/lib") -- add_directory(include_dirs, "/sw/include") -- # darwin ports installation directories -- add_directory(library_dirs, "/opt/local/lib") -- add_directory(include_dirs, "/opt/local/include") -- -- add_directory(library_dirs, "/usr/local/lib") -- # FIXME: check /opt/stuff directories here? -- -- prefix = sysconfig.get_config_var("prefix") -+ prefix = os.environ.get('PYTHON_LINK_ROOT') - if prefix: -- add_directory(library_dirs, os.path.join(prefix, "lib")) -- add_directory(include_dirs, os.path.join(prefix, "include")) -+ add_directory(library_dirs, os.environ.get('PYTHON_LINK_ROOT')) -+ add_directory(include_dirs, os.environ.get('PYTHON_INCLUDE_ROOT')) - - # - # locate tkinter libraries -@@ -199,22 +176,6 @@ class pil_build_ext(build_ext): - add_directory(include_dirs, include_root) - - # -- # add standard directories -- -- # look for tcl specific subdirectory (e.g debian) -- if _tkinter: -- tcl_dir = "/usr/include/tcl" + TCL_VERSION -- if os.path.isfile(os.path.join(tcl_dir, "tk.h")): -- add_directory(include_dirs, tcl_dir) -- -- # standard locations -- add_directory(library_dirs, "/usr/local/lib") -- add_directory(include_dirs, "/usr/local/include") -- -- add_directory(library_dirs, "/usr/lib") -- add_directory(include_dirs, "/usr/include") -- -- # - # insert new dirs *before* default libs, to avoid conflicts - # between Python PYD stub libs and real libraries - -@@ -299,8 +260,6 @@ class pil_build_ext(build_ext): - defs.append(("HAVE_LIBZ", None)) - if sys.platform == "win32": - libs.extend(["kernel32", "user32", "gdi32"]) -- if struct.unpack("h", "\0\1")[0] == 1: -- defs.append(("WORDS_BIGENDIAN", None)) - - exts = [(Extension( - "_imaging", files, libraries=libs, define_macros=defs diff --git a/pythonforandroid/recipes/png/__init__.py b/pythonforandroid/recipes/png/__init__.py index 5b69688825..1ad49cc1ca 100644 --- a/pythonforandroid/recipes/png/__init__.py +++ b/pythonforandroid/recipes/png/__init__.py @@ -1,19 +1,52 @@ -from pythonforandroid.recipe import NDKRecipe +from pythonforandroid.recipe import Recipe +from pythonforandroid.logger import shprint +from pythonforandroid.util import current_directory +from multiprocessing import cpu_count +from os.path import join, exists +import sh -class PngRecipe(NDKRecipe): +class PngRecipe(Recipe): name = 'png' - # This version is the last `sha commit` published in the repo (it's more - # than one year old...) and it's for libpng version `1.6.29`. We set a - # commit for a version because the author of the github's repo never - # released/tagged it, despite He performed the necessary changes in - # master branch. - version = 'b43b4c6' + version = 'v1.6.37' + url = 'https://github.com/glennrp/libpng/archive/{version}.zip' - # TODO: Try to move the repo to mainline - url = 'https://github.com/julienr/libpng-android/archive/{version}.zip' + def should_build(self, arch): + return not exists( + join(self.get_build_dir(arch.arch), '.libs', 'libpng16.so') + ) - generated_libraries = ['libpng.a'] + def get_recipe_env(self, arch=None): + env = super(PngRecipe, self).get_recipe_env(arch) + ndk_lib_dir = join(self.ctx.ndk_platform, 'usr', 'lib') + ndk_include_dir = join(self.ctx.ndk_dir, 'sysroot', 'usr', 'include') + env['CFLAGS'] += ' -I{}'.format(ndk_include_dir) + env['LDFLAGS'] += ' -L{}'.format(ndk_lib_dir) + env['LDFLAGS'] += ' --sysroot={}'.format(self.ctx.ndk_platform) + return env + + def build_arch(self, arch): + super(PngRecipe, self).build_arch(arch) + build_dir = self.get_build_dir(arch.arch) + with current_directory(build_dir): + env = self.get_recipe_env(arch) + build_arch = ( + shprint(sh.gcc, '-dumpmachine') + .stdout.decode('utf-8') + .split('\n')[0] + ) + shprint( + sh.Command('./configure'), + '--build=' + build_arch, + '--host=' + arch.command_prefix, + '--target=' + arch.command_prefix, + '--disable-static', + '--enable-shared', + '--prefix={}/install'.format(self.get_build_dir(arch.arch)), + _env=env, + ) + shprint(sh.make, '-j', str(cpu_count()), _env=env) + self.install_libs(arch, join(build_dir, '.libs', 'libpng16.so')) recipe = PngRecipe() diff --git a/pythonforandroid/recipes/png/build_shared_library.patch b/pythonforandroid/recipes/png/build_shared_library.patch new file mode 100644 index 0000000000..01e3080f6c --- /dev/null +++ b/pythonforandroid/recipes/png/build_shared_library.patch @@ -0,0 +1,17 @@ +diff --git a/jni/Android.mk b/jni/Android.mk +index df2ff1a..2f70985 100644 +--- a/jni/Android.mk ++++ b/jni/Android.mk +@@ -26,8 +26,9 @@ LOCAL_SRC_FILES :=\ + arm/filter_neon_intrinsics.c + + #LOCAL_SHARED_LIBRARIES := -lz +-LOCAL_EXPORT_LDLIBS := -lz ++# LOCAL_EXPORT_LDLIBS := -lz ++LOCAL_LDLIBS := -lz + LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/. + +-#include $(BUILD_SHARED_LIBRARY) +-include $(BUILD_STATIC_LIBRARY) ++include $(BUILD_SHARED_LIBRARY) ++# include $(BUILD_STATIC_LIBRARY) diff --git a/pythonforandroid/recipes/six/__init__.py b/pythonforandroid/recipes/six/__init__.py index 581ab9091c..7025285efc 100644 --- a/pythonforandroid/recipes/six/__init__.py +++ b/pythonforandroid/recipes/six/__init__.py @@ -3,7 +3,7 @@ class SixRecipe(PythonRecipe): - version = '1.9.0' + version = '1.10.0' url = 'https://pypi.python.org/packages/source/s/six/six-{version}.tar.gz' depends = [('python2', 'python2legacy', 'python3', 'python3crystax')] diff --git a/testapps/setup_testapp_python3_matplotlib.py b/testapps/setup_testapp_python3_matplotlib.py new file mode 100644 index 0000000000..3320579466 --- /dev/null +++ b/testapps/setup_testapp_python3_matplotlib.py @@ -0,0 +1,30 @@ + +from distutils.core import setup +from setuptools import find_packages + +options = {'apk': {'requirements': 'sdl2,python3,matplotlib,pyparsing,cycler,python-dateutil,numpy,kiwisolver,kivy', + 'blacklist-requirements': 'openssl,sqlite3', + 'android-api': 27, + 'ndk-api': 21, + 'dist-name': 'matplotlib_testapp', + 'ndk-version': '10.3.2', + 'permission': 'VIBRATE', + }} + +package_data = {'': ['*.py', + '*.png'] + } + +packages = find_packages() +print('packages are', packages) + +setup( + name='testapp_matplotlib', + version='0.1', + description='p4a setup.py test', + author='Alexander Taylor', + author_email='alexanderjohntaylor@gmail.com', + packages=find_packages(), + options=options, + package_data={'testapp_matplotlib': ['*.py', '*.png']} +) diff --git a/testapps/testapp_matplotlib/main.py b/testapps/testapp_matplotlib/main.py new file mode 100644 index 0000000000..fa798204b9 --- /dev/null +++ b/testapps/testapp_matplotlib/main.py @@ -0,0 +1,74 @@ +print('importing numpy') +import numpy as np +print('imported numpy') + +print('importing matplotlib') + +import matplotlib +print('imported matplotlib') + +print('importing pyplot') + +from matplotlib import pyplot as plt + +print('imported pyplot') + +fig, ax = plt.subplots() + +print('created fig and ax') + +ax.plot(np.random.random(50)) + +print('plotted something') + +ax.set_xlabel('test label') + +print('set a label') + +fig.set_size_inches((5, 4)) +fig.savefig('test.png') + +print('saved fig') + +from kivy.app import App +from kivy.uix.image import Image +from kivy.lang import Builder + +class MatplotlibApp(App): + def build(self): + root = Builder.load_string(""" +BoxLayout: + orientation: 'vertical' + Image: + id: the_image + source: 'test.png' + allow_stretch: True + Button: + size_hint_y: None + height: dp(40) + text: 'new plot' + on_release: app.generate_new_plot() + """) + return root + + def generate_new_plot(self): + fig, ax = plt.subplots() + ax.set_xlabel('test xlabel') + ax.set_ylabel('test ylabel') + ax.plot(np.random.random(50)) + ax.plot(np.sin(np.linspace(0, 3*np.pi, 30))) + + ax.legend(['random numbers', 'sin']) + + fig.set_size_inches((5, 4)) + fig.tight_layout() + + fig.savefig('test.png', dpi=150) + + self.root.ids.the_image.reload() + + + + +MatplotlibApp().run() +runTouchApp(Image(source='test.png', allow_stretch=True))