inclem.nethttps://inclem.net/2023-12-30T16:00:00+01:00State of Vivarium2023-12-30T16:00:00+01:002023-12-30T16:00:00+01:00Alexander Taylortag:inclem.net,2023-12-30:/2023/12/30/wayland/state_of_vivarium/<p>Somehow it’s been over two and a half years since my last post. Time flies when not slowed down by a global pandemic.</p>
<p>My last few posts were about the state of <a class="reference external" href="https://github.com/inclement/vivarium">Vivarium</a>, my dynamic tiling wayland compositor. I had a number of feature targets and completion goals I …</p><p>Somehow it’s been over two and a half years since my last post. Time flies when not slowed down by a global pandemic.</p>
<p>My last few posts were about the state of <a class="reference external" href="https://github.com/inclement/vivarium">Vivarium</a>, my dynamic tiling wayland compositor. I had a number of feature targets and completion goals I was working on. But surprisingly something unexpected happened: Vivarium turned out to be good enough to use! I still use it every day on all my machines but since the last release I’ve only performed minor tweaks and fixes.</p>
<p>Vivarium is by no means finished, it has many missing and unsupported features including things like support for a recent wlroots release, and big wayland features like protocol support to allow screen sharing. However, it isn’t missing anything that bugs me right now. I continue to fix things as they come up but it’s probably unrealistic to think I’ll put a lot of effort into new development any time soon.</p>
<p>As far as I can tell there might be one or two people using Vivarium, which I know only from occasional pull requests. Many thanks if that’s you, I appreciate (and have benefited from) many of those.</p>
<p>In the last couple of years there’s been a lot more wayland development and more compositor alternatives have sprung up. Perhaps one of them would suit me, but for now I’ll still be using Vivarium for the foreseeable future.</p>
Vivarium 0.0.3 release: damage tracking and wlroots 0.13 support2021-05-30T21:00:00+02:002021-05-30T21:00:00+02:00Alexander Taylortag:inclem.net,2021-05-30:/2021/05/30/wayland/vivarium_wayland_compositor_0_0_3_release/<p>I’ve just tagged for release <a class="reference external" href="https://github.com/inclement/vivarium">Vivarium 0.0.3</a>. Vivarium is a dynamic tiling wayland compositor, with desktop semantics inspired mostly by xmonad.</p>
<div class="section" id="release-summary">
<h2>Release summary</h2>
<p>This release has two major new features: damage tracking, and support for wlroots 0.13. There are also various minor fixes and additions, such …</p></div><p>I’ve just tagged for release <a class="reference external" href="https://github.com/inclement/vivarium">Vivarium 0.0.3</a>. Vivarium is a dynamic tiling wayland compositor, with desktop semantics inspired mostly by xmonad.</p>
<div class="section" id="release-summary">
<h2>Release summary</h2>
<p>This release has two major new features: damage tracking, and support for wlroots 0.13. There are also various minor fixes and additions, such as new mappable functions and many semi-hidden rendering issues found during the damage tracking work.</p>
<div class="section" id="damage-tracking">
<h3>Damage tracking</h3>
<p>Damage tracking means that instead of re-compositing everything every frame, copying all the view output buffers around into the output framebuffer, updates are only made when something has actually changed.</p>
<p>Vivarium 0.0.3 automatically enables frame-wise damage tracking, where nothing is drawn unless something changes in one of the surfaces being displayed. This is an enormous improvement on the previous status quo of redrawing everything at the monitor refresh rate, and in cases where nothing is changing much (e.g. just text editors and terminals, no video) brings most of the damage tracking benefits.</p>
<p>Vivarium also supports full damage tracking, in which it tracks which parts of the frame have changed and only draws these. That means for example even if a video is playing at 60fps only its subregion of the screen is repeatedly redrawn. However, this full damage tracking support is a little more experimental and is not enabled by default: it should work for single outputs, and for multiple outputs if they have the same scale, but is likely to have bugs in more complex cases such as output scaling as these cases haven’t been rigorously tested.</p>
<p>If you’d like to enable full damage tracking, set <tt class="docutils literal"><span class="pre">damage-tracking-mode</span> = "full"</tt> in the <tt class="docutils literal">[debug]</tt> section of your config.toml.</p>
<p>I found quite a few internal Vivarium issues while making damage tracking work as it exercises the full stack of assumptions about how/where surfaces are displayed. There may still be more, if you see anything appearing to render incorrectly (or mostly likely not rendering at all) bug reports are <a class="reference external" href="https://github.com/inclement/vivarium/issues">gratefully received</a>.</p>
</div>
<div class="section" id="wlroots-0-13">
<h3>Wlroots 0.13</h3>
<p>Wlroots 0.13 was released in early April but it’s taken a little while to get around to supporting it as I was focusing on damage tracking at the time. For Vivarium’s purposes there aren’t any major new features this allows, but there are plenty of background improvements.</p>
</div>
</div>
<div class="section" id="next-goals">
<h2>Next goals</h2>
<p>The next focus is output handling. Vivarium currently works with one or more outputs, but doesn’t let you configure any aspect of them including e.g. their relative position and scaling applied. It also has some issue around output destruction and creation, which affects even normal use if e.g. you switch monitors or simply suspend/resume your system in which case the output may disappear briefly.</p>
<p>I’m not sure exactly what options Vivarium will expose, but I definitely expect to support output configuration tools like <a class="reference external" href="https://github.com/emersion/kanshi">kanshi</a>.</p>
</div>
Thoughts on writing a wayland window manager with wlroots2021-04-17T21:00:00+02:002021-04-17T21:00:00+02:00Alexander Taylortag:inclem.net,2021-04-17:/2021/04/17/wayland/writing_a_wayland_compositor_with_wlroots/<p>I recently released <a class="reference external" href="https://inclem.net/2021/03/15/wayland/vivarium_wayland_compositor/">Vivarium</a>, a dynamic tiling
wayland compositor built using the <a class="reference external" href="https://github.com/swaywm/wlroots">wlroots library</a>. Going from zero to compositing was interesting,
there are good libraries available but everything has moved very fast in the last couple
of years so it can be hard to know where to start. I especially …</p><p>I recently released <a class="reference external" href="https://inclem.net/2021/03/15/wayland/vivarium_wayland_compositor/">Vivarium</a>, a dynamic tiling
wayland compositor built using the <a class="reference external" href="https://github.com/swaywm/wlroots">wlroots library</a>. Going from zero to compositing was interesting,
there are good libraries available but everything has moved very fast in the last couple
of years so it can be hard to know where to start. I especially wanted to write the
equivalent of an X window manager that only really cares about window layout and wants
everything else to Just Work.</p>
<p>Wlroots is a library created to support this use case. To quote its own <span class="caps">README</span>, it
provides “pluggable, composable, unopinionated modules for building a Wayland compositor;
or about 50,000 line of code you were going to write anyway”. For me it’s fulfilled that
goal very well, and Vivarium probably wouldn’t exist without it. Still, while there are
many good development resources around it wasn’t always straightforward to get up to speed
so I thought I should write the blog post I would have loved to read at the start, setting
out the broad ideas and good resources for each development step. This is that post.</p>
<div class="section" id="background">
<h2>Background</h2>
<p>(If you’ve got this far you probably have some idea what Wayland is about already, but
here’s an unnecessarily long summary for general background purposes.)</p>
<p>Any modern desktop utilises some kind of <em>display server</em>, a program that brings together
the disparate components of desktop input and output to allow a consistent desktop
experience. In broad terms the display server keeps track of system outputs (an
abstraction that for the most part means physical monitors), and the applications that
want to draw to them, and via some set of rules draws the window images to them. This
means the display server also has to care about all sorts of other details: it must track
system inputs (keyboard, mouse) and pass their events through to applications; it must
deal with hardware details like monitor framerates, dpi scaling and rotation including
potentially passing these details to applications; and it may need to do drawing of its
own such as window decorations if the clients have not drawn their own. These are just
examples, the definition of the display manager is not rigid and these tasks can be
divided up in more than one different way, but you can’t think about implementing display
server functionality without worrying about them.</p>
<p>For a very long time the display server on almost all linux systems has been <a class="reference external" href="https://www.x.org/wiki/">X.Org</a>, implementing the X11 protocol. X has the problem that its
design is old and crufty, there are many details of its architecture that do not map well
to how modern software wants to work. For this reason there has been growing development
effort behind an alternative, the <a class="reference external" href="https://wayland.freedesktop.org/">Wayland protocol</a>. It’s been a long time coming, Wayland first became
a named thing somewhere around 2008, but nowadays the big desktop environments (Gnome,
<span class="caps">KDE</span>) are providing Wayland servers, many applications toolkits support it, and some big
distros are supporting Wayland by default.</p>
<p>Much has been written about this, some people think it’s incredibly terrible for various
reasons and others don’t. For what it’s worth I’m in the latter camp, I think Wayland will
improve the Linux desktop experience. Still, changing such a big desktop component is a
complicated process and personally I have one particular problem: what about my window manager?</p>
<p>The problem here is that under X it’s been normal that the X server exists and handles all
the core display server functionality, but other applications can control a lot of its
behaviour. A special example is the <em>window manager</em>, the program that looks at the open
windows and actually decides where to draw them. In a big desktop environment like
Gnome/<span class="caps">KDE</span> the window manager is a small part of a larger desktop architecture, but it’s
also totally practical and quite common to run a standalone window manager and there are
many popular ones for X11. As a quick reference, the Arch Linux wiki lists about <a class="reference external" href="https://wiki.archlinux.org/index.php/window_manager">60
different options</a>, and these are
just the ones that people have bothered to list there. Personally I’ve used <a class="reference external" href="https://xmonad.org/">xmonad</a> for many years, not because I think its particular brand of
tiling window management is the one true way, but I like it and it’s been super stable.</p>
<p>With Wayland the situation is different. It isn’t an irreconcilable change, you could
certainly write a Wayland server that lets you hotplug all the functionality a window
manager needs (and probably e.g. Gnome/<span class="caps">KDE</span> plugins already can do a lot of that, I’m not
sure), but in practice different projects have implemented their own Wayland display
servers. A window manager in this environment really has its work cut out, instead of
focusing on implementing window positioning logic it would need to implement the entire
wayland protocol, all those hardware interfaces, and all the additional protocols you
want to use such as the xdg-shell protocol applications use to actually tell the
Wayland server they want to display stuff.</p>
<p>Thankfully there has been much developer effort to rally around libraries and tooling to
make Wayland window manager equivalents easy. The most popular example is almost certainly
<a class="reference external" href="https://github.com/swaywm/wlroots">wlroots</a>, a library for creating Wayland
compositors. Effectively wlroots can be used to write a window manager under Wayland
without worrying directly about the core Wayland functionality, although the final result is a
standalone binary that implements a full Wayland compositor as opposed to the X model of
window managers plugging into a separate server. Wlroots is still under development but is
nowadays quite stable and complete for every core task I’ve worked through. There are many
technical details of interest, I want to cover here how I got started learning about the
key ideas and writing a useful compositor that I now use full time.</p>
</div>
<div class="section" id="how-to-get-started-with-wlroots">
<h2>How to get started with wlroots</h2>
<p>The following is entirely my opinion and perspective. There is no single correct path to
take and I’m sure there are other great resources I didn’t even notice or pay attention to.</p>
<p>Before anything else you may want to ask whether you really need to write a Wayland
compositor. Window management functionality can potentially be achieved just as well
(maybe more easily!) as a plugin to another compositor, if it supports such an
interface. Some possibilities include:</p>
<ul class="simple">
<li>Write a <a class="reference external" href="https://github.com/WayfireWM/wayfire/wiki/Plugin-architecture">Wayfire plugin</a>. Wayfire is an
existing Wayland compositor using wlroots and its plugin system seems extensive enough
to cover a lot of window manager functionality. There are also other similar options
like writing a KWin or Gnome Shell script, but I don’t know much about what these
currently support.</li>
<li>Use an existing tiling window manager. <a class="reference external" href="https://swaywm.org/">Sway</a> is mostly a
drop-in replacement for the i3 window manager and is popular and well supported, with
some scripting functionality. <a class="reference external" href="https://github.com/ifreund/river">River</a> supports a
system of user-provided executables that makes its layouts quite flexible. Indeed
<a class="reference external" href="https://github.com/inclement/vivarium">Vivarium</a> itself supports customisable
layouts, but those other projects are much more established and perhaps more flexible.<ul>
<li>Obviously I wrote Vivarium despite these alternative possibilities. I did that because
I wanted to learn from the project and see how practical it was. Without that
motivation, I’d probably be a happy Sway/River/other user.</li>
</ul>
</li>
<li>Hang around and see if wlroots grows a <a class="reference external" href="https://github.com/swaywm/wlroots/issues/1826">higher level <span class="caps">API</span></a>, or if another project/library starts
to provide one. As it stands I think wlroots is a step more technically involved than it
needs to be for most window management purposes - it isn’t hard, but I feel like
different wlroots compositors tend to implement some things in similar ways, which
supports the idea that a better abstraction layer isn’t quite there yet. Examples
include damage tracking and the abstraction of views to provide a consistent
api for views managed via different protocols, especially xdg-shell/xwayland. This isn’t a
criticism, just a feature that I think will eventually exist but doesn’t yet.</li>
</ul>
<p>I think ultimately there will naturally end up being multiple active compositor projects,
some of which support easily creating distinct window managers via extensive plugin
APIs, although it’s of course impossible to know which projects will ultimately be
successful. See <a class="reference external" href="https://tudorr.ro/blog/technical/2021/01/26/the-wayland-experience/">Should you write a Wayland compositor</a> by tudor for
another overview discussing these questions. Of course, there’s never anything wrong with
starting another project.</p>
<p>So, if you do want to write a wlroots compositor…how to get started? Here’s what I found
useful, in rough order. I started Vivarium without any specific knowledge about wlroots or
wayland, so that’s the direction I’m coming from below.</p>
<ul class="simple">
<li>Drew DeVault’s <a class="reference external" href="https://drewdevault.com/2018/02/17/Writing-a-Wayland-compositor-1.html">Writing a Wayland compositor</a> blog posts
are an excellent introduction to how to think about Wayland. Don’t worry too much about
the code itself, I think it’s outdated for current wlroots and the repository is
archived, but the overview of key ideas will take you a long way.</li>
<li>Fork <a class="reference external" href="https://github.com/swaywm/wlroots/blob/master/tinywl/tinywl.c">tinywl</a>. This
tiny example is shipped with wlroots and is an excellent base for a serious
compositor. Although short it implements in a basic way almost every core
functionality you’ll need, and implicitly teaches a lot about Wayland <span class="caps">API</span> interaction
(especially if using the C interface to events, listeners etc.) which scales very well
when branching out into other protocols. Since tinywl is within the wlroots tree it is
also guaranteed to be up to date.</li>
<li>Watch (and join in on) the #sway and #sway-dev irc channels on irc.freenode.net. Seeing
how other people think about things is always invaluable.</li>
<li>Don’t be afraid to actually read the wayland protocol definitions - the other linked
resources also say this but it bears repeating. They are often surprisingly straightforward.</li>
<li>Read <a class="reference external" href="https://wayland-book.com/">the Wayland Book</a>. This is a much more thorough (but
not complete) overview of the Wayland protocols and way of working. I’ve found this more
useful as a reference to revisit than a direct learning tool, mostly because much of the
detail isn’t actually necessary to sit down and write code, but it’s very nice for
formalising knowledge to really do things right.</li>
<li>Read <a class="reference external" href="https://github.com/swaywm/sway">the Sway source code</a>. Sway doubles as a
thorough reference for how to do just about anything with wlroots, since it’s an active
and fairly complete project that has tackled most issues you’re likely to run into.</li>
<li>Make use of the <a class="reference external" href="https://github.com/swaywm/wlroots/tree/master/examples">wlroots examples</a>. When testing individual
protocols/features these save a lot of time writing your own test code!</li>
</ul>
<p>One issue I’ve sometimes hit is that wlroots doesn’t always have much in-code
documentation. However, it mostly makes up for this in general design consistency, and
this is a big part of the value of tinywl: the methodology it demonstrates is widely
applicable throughout wlroots. For instance, supporting a new protocol is likely to come
down to a <tt class="docutils literal">_create</tt> function call returning a manager object with an obvious-looking
<span class="caps">API</span>, whose events you can probably read the protocol documentation to understand, and
tinywl demonstrates this process.</p>
<p>It’s also worth looking through the list of <a class="reference external" href="https://github.com/swaywm/wlroots/wiki/Projects-which-use-wlroots">projects which use wlroots</a>. Between them these
demonstrate many different things, especially where they focus on functionality that is
not so core to Sway.</p>
<p>There are many other useful resources scattered around, such as some posts on the blogs of
<a class="reference external" href="https://drewdevault.com/">Drew DeVault</a> (sway and wlroots creator) and <a class="reference external" href="https://emersion.fr/blog/">Simon Ser</a> (sway and wlroots current maintainer), but I’ve generally
found these by googling keywords when stuck rather than from any specific catalogue.</p>
<p>And with all that…this is pretty much where I am. I’m no expert, but Vivarium works and
it was fun to write. Thanks to the Wayland developer community for creating all these
useful resources.</p>
</div>
Vivarium 0.0.2 release: improved config handling, cli help, better CI and unit tests2021-04-17T18:00:00+02:002021-04-17T18:00:00+02:00Alexander Taylortag:inclem.net,2021-04-17:/2021/04/17/wayland/vivarium_wayland_compositor_0_0_2_release/<p>I’ve just tagged for release <a class="reference external" href="https://github.com/inclement/vivarium">Vivarium 0.0.2</a>. This is an incremental release with many small fixes following the <a class="reference external" href="https://inclem.net/2021/03/15/wayland/vivarium_wayland_compositor/">first release announcement</a>. Many thanks to those who tested it out and reported issues.</p>
<p>Vivarium is a dynamic tiling wayland compositor, with desktop semantics inspired mostly by xmonad.</p>
<div class="section" id="release-summary">
<h2>Release …</h2></div><p>I’ve just tagged for release <a class="reference external" href="https://github.com/inclement/vivarium">Vivarium 0.0.2</a>. This is an incremental release with many small fixes following the <a class="reference external" href="https://inclem.net/2021/03/15/wayland/vivarium_wayland_compositor/">first release announcement</a>. Many thanks to those who tested it out and reported issues.</p>
<p>Vivarium is a dynamic tiling wayland compositor, with desktop semantics inspired mostly by xmonad.</p>
<div class="section" id="release-summary">
<h2>Release summary</h2>
<p>Key improvements in this release include:</p>
<ul class="simple">
<li>Improved <span class="caps">CI</span> to actually run Vivarium with different config types, catching issues with default values on different systems.</li>
<li>Added cli argument parsing and initial cli interface, the Vivarium binary can now output a list of available layouts and action commands for keybinds instead of having to read the code to find them.</li>
<li>Added unit tests for layout function and config loading.</li>
<li>Improved XWayland popup window detection (i.e. windows that should be made floating automatically) by properly inspecting X window properties.</li>
<li>Improved default config values in general and fixed issues with config loading crashing on certain missing data.</li>
<li>Various minor bugfixes.</li>
</ul>
<p>Many thanks also to Arch Linux user lmartinez-mirror for contributing an <a class="reference external" href="https://aur.archlinux.org/packages/vivarium-git/"><span class="caps">AUR</span> package</a> for Vivarium.</p>
</div>
<div class="section" id="next-goals">
<h2>Next goals</h2>
<p>This release has been tagged partly because I’m working to merge damage tracking. This is a key feature that will dramatically improve performance but it has the potential to introduce tricky bugs, so this release serves as a clear stable target. I hope to merge damage tracking for the next release.</p>
<p>Our backend library <a class="reference external" href="https://github.com/swaywm/wlroots">Wlroots</a> recently tagged the new version 0.13 and Vivarium needs a little tweaking to support it. I expect to bundle this into the next release.</p>
</div>
Vivarium: A dynamic tiling wayland compositor2021-03-15T14:00:00+01:002021-03-15T14:00:00+01:00Alexander Taylortag:inclem.net,2021-03-15:/2021/03/15/wayland/vivarium_wayland_compositor/<p>This post is to announce the existence of <a class="reference external" href="https://github.com/inclement/vivarium">Vivarium</a>, a dynamic tiling wayland compositor.</p>
<div class="figure align-center">
<img alt="Screenshot of Vivarium with browser window, terminal and PDF viewer" src="https://inclem.net/media/vivarium_readme_screenshot_20210314.png" style="width: 85%;" />
</div>
<p>Core Vivarium features include:</p>
<ul class="simple">
<li>Automatic/dynamic tiling with your choice of layouts.</li>
<li>Per-output workspaces: each monitor can switch independently through the same set of workspaces.</li>
<li>Floating windows on demand.</li>
<li>(optional) XWayland support.</li>
<li>Layer shell support, compatible …</li></ul><p>This post is to announce the existence of <a class="reference external" href="https://github.com/inclement/vivarium">Vivarium</a>, a dynamic tiling wayland compositor.</p>
<div class="figure align-center">
<img alt="Screenshot of Vivarium with browser window, terminal and PDF viewer" src="https://inclem.net/media/vivarium_readme_screenshot_20210314.png" style="width: 85%;" />
</div>
<p>Core Vivarium features include:</p>
<ul class="simple">
<li>Automatic/dynamic tiling with your choice of layouts.</li>
<li>Per-output workspaces: each monitor can switch independently through the same set of workspaces.</li>
<li>Floating windows on demand.</li>
<li>(optional) XWayland support.</li>
<li>Layer shell support, compatible with tools like <a class="reference external" href="https://github.com/Alexays/Waybar">Waybar</a>, <a class="reference external" href="https://github.com/Cloudef/bemenu">bemenu</a> and <a class="reference external" href="https://github.com/swaywm/swaybg">swaybg</a>.</li>
<li>Simple static config and/or sophisticated C config.</li>
</ul>
<p>As of now Vivarium is…pretty much usable! In particular it has reached the bar of being good enough for me to use full time, for several weeks now. Vivarium has many missing or incomplete features and undoubtedly plenty of bugs, but may be of interest to anyone looking for a tiling wayland compositor / window manager. Bug reports (or code contributions!) are very welcome <a class="reference external" href="https://github.com/inclement/vivarium">via Github</a>. This also marks the first numbered version tag, v0.0.1, and transitioning to slightly more careful development. Merges to the main branch will now not be allowed unless the build is verified passing, and main branch history will not be modified.</p>
<p>Vivarium’s desktop semantics are inspired by <a class="reference external" href="https://xmonad.org/">xmonad</a>: each monitor displays a workspace, and new windows are automatically tiled within the current workspace and active layout. The order of windows within the layouts is adjustable at runtime. Each workspace may independently switch between different layouts. Each output (usually equivalent to each monitor) can independently switch between workspaces. Windows may be made floating and moved/resized smoothly, but this is generally the exception rather than the rule. That said, Vivarium makes no attempt to rigorously mimic xmonad or to replicate its internal design philosophy - not least, Vivarium is written in C and is not for now so directly and transparently extensible.</p>
<p>Vivarium is built using the <a class="reference external" href="https://github.com/swaywm/wlroots">wlroots library</a> and largely inherits support for different protocols and backend features from this toolkit - though not all are enabled or fully implemented yet. Vivarium is released under the GPLv3 license.</p>
<p>For further information including install and config instructions, see <a class="reference external" href="https://github.com/inclement/vivarium">Vivarium’s Github</a>.</p>
<div class="section" id="vivarium-s-tiling-model">
<h2>Vivarium’s tiling model</h2>
<p>Vivarium lets you define any number of workspaces, each with some number of tiling layouts that you can switch between at runtime. New windows are automatically tiled according to the current layout, or can be made floating to be placed anywhere you like.</p>
<p>A standard config will generally set up a small number of layouts whose parameters you adjust at runtime according to your needs. For instance, if you find you need too many terminals to fit in a single stack next to a browser window then you might switch to a layout with more columns. Or if you want to focus on the browser, you might switch to a fullscreen layout.</p>
<p>Example layouts include (left to right): split, fullscreen, central column, and recursive split:</p>
<div class="figure align-center">
<img alt="Vivarium layout illustrations" src="https://inclem.net/media/viv_layout_type_illustrations.png" style="width: 95%;" />
</div>
<p>Most layouts have a main panel displaying the primary window, and a secondary space for the other windows. The window order can be adjusted, including swapping out the primary window at any time.</p>
<p>Layouts have a “fill fraction” parameter, adjustable at runtime via hotkeys, which controls the size of the main panel:</p>
<div class="figure align-center">
<img alt="Vivarium varying fill fraction illustrations" src="https://inclem.net/media/viv_layout_split_dist_illustrations.png" style="width: 95%;" />
</div>
<p>Layouts also have an integer main panel “count”, adjustable at runtime via hotkeys, which controls how many windows are stacked in the main panel. It can be zero so that all windows occupy the secondary space:</p>
<div class="figure align-center">
<img alt="Vivarium varying main panel counter illustrations" src="https://inclem.net/media/viv_layout_counter_illustrations.png" style="width: 95%;" />
</div>
<p>Layouts further let you configure options including the display of window borders, and whether windows adhere to the excluded region of a desktop bar (or other layer surface) or instead get drawn on top of it.</p>
<p>This basic model is core to Vivarium and not expected to change, but it is intended to become more flexible over time. For instance, currently the list of workspaces is fixed in the config, but it would be straightforward to support dynamically adding/removing workspaces. Similarly, all workspaces currently have to use the same choice of layouts, but this too ought to be made configurable.</p>
</div>
<div class="section" id="roadmap-missing-features">
<h2>Roadmap / missing features</h2>
<p>I’m currently working through bugs or minor missing features I find in the course of using Vivarium. When these die down the following are priorities:</p>
<ul class="simple">
<li>Better documentation, especially Vivarium needs to be able to list available layouts and commands so that these can be referenced easily in user configs.</li>
<li>Proper output configuration (<span class="caps">DPI</span>, scaling, positioning). Wlroots provides everything we need for this but testing it properly requires monitor hardware I don’t currently have.</li>
<li>Per-application/window configuration, e.g. configuring applications to automatically be sent to a certain desktop or be made floating.</li>
<li>Damage tracking: Vivarium simply doesn’t do this yet, it naively renders everything every frame. This works fine but is quite inefficient. This can be improved quickly even without a full implementation: the first step is at least to avoid rendering frames where no surface has changed.</li>
<li>Protocol support: Vivarium inherits support for many Wayland protocols from wlroots, but in general they need a little boilerplate to initialise. In some cases like the layer shell protocol, Vivarium supports enough of the protocol to basically work but doesn’t handle entirely correctly according to the spec. I intend to both fix this, and make sure Vivarum supports as many protocol features as possible. Immediate goals include:
* Full layer shell support, especially making sure the overlay layer works correctly.
* Input inhibitor protocol support, to allow screen locking.
* Screen copy and screen share support.</li>
<li>Better configuration: The current static config should be enough for many practical purposes, and more advanced tasks can be achieved via the C config header, but a more accessible configuration-as-code would be nice. This would make it easy to support features like user-provided layouts without needing to recompile the C source. I’d like to explore providing e.g. a Python wrapper library that makes it easy to inject dynamic configurations.</li>
</ul>
<p>This is not a formal roadmap, the actual order of feature addition may vary.</p>
</div>
<div class="section" id="alternatives">
<h2>Alternatives</h2>
<p>If you’re reading this thinking “hey that’s cool, but what if I want a tiling Wayland compositor that already works well?” (or simply that works differently), you may be interested in:</p>
<ul class="simple">
<li><a class="reference external" href="https://github.com/swaywm/sway">Sway</a>: An i3-compatible Wayland compositor. Sway is easily the most popular and well supported Wayland tiling compositor, and also the origin of the wlroots library used by Vivarium.</li>
<li><a class="reference external" href="https://github.com/ifreund/river">River</a>: A dynamic tiling wayland compositor that takes inspiration from dwm and bspwm. River has a nice system of dynamic layouts based on user-provided executables, but is not as mature as Sway.</li>
<li><a class="reference external" href="https://github.com/WayfireWM/wayfire">Wayfire</a>: A 3D Wayland compositor inspired by Compiz. Wayfire doesn’t provide tiling-type window management as a core feature, but there is a plugin for it.</li>
</ul>
</div>
An actual history of Python on Android2020-05-16T16:00:00+02:002020-05-16T16:00:00+02:00Alexander Taylortag:inclem.net,2020-05-16:/2020/05/16/kivy/python_android_history/<p><a class="reference external" href="https://www.zdnet.com/article/programming-languages-python-apps-might-soon-be-running-on-android/">This ZDNet article</a>
was published a few days ago about how “Python apps might soon be
running on Android”. It summarises some recent developments in Android
support for CPython, but disappointingly it’s highly misleading about
some key points. In particular the article states that “apps written
in Python may …</p><p><a class="reference external" href="https://www.zdnet.com/article/programming-languages-python-apps-might-soon-be-running-on-android/">This ZDNet article</a>
was published a few days ago about how “Python apps might soon be
running on Android”. It summarises some recent developments in Android
support for CPython, but disappointingly it’s highly misleading about
some key points. In particular the article states that “apps written
in Python may one day run natively on iOS and Android devices”, but in
fact people have been doing this since at least 2011.</p>
<p>I thought I’d write some short details about the actual history of
Python on Android from my own perspective. It’s something I’ve been
involved in on some level for about 8 years, and for a significant
part of that I’ve been a primary maintainer of <a class="reference external" href="https://github.com/kivy/python-for-android/tree/master">python-for-android</a>. This is a
build tool for creating APKs from Python applications, originally
created for <a class="reference external" href="https://kivy.org/#home">Kivy</a> but now more generic so
that it can also support e.g. flask running on the device with a
webview gui, or more recently Pygame via its upcoming <span class="caps">SDL2</span> support.</p>
<div class="figure align-center">
<img alt="Kivy app screenshots" src="https://inclem.net/media/example_python_android_apps.png" />
<p class="caption">Example Python apps for Android. <a class="reference external" href="https://inclem.net/media/example_python_android_apps_large.png">Large version</a>. From
left to right: <a class="reference external" href="https://play.google.com/store/apps/details?id=com.meltingrocks.flatjewels">Flat Jewels</a>,
<a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.colourblind">ColourBlind</a>,
<a class="reference external" href="https://play.google.com/store/apps/details?id=org.kognitivo.kognitivo">Kognitivo</a>,
<a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.lazybaduk">Lazy Baduk</a>,
<a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">Pyonic interpreter</a>,
<a class="reference external" href="https://www.barlyapp.com/">Barly</a> (original Python version).</p>
</div>
<div class="section" id="notes-on-the-term-native">
<h2>Notes on the term “native”</h2>
<p>A key problem point around discussing Python on Android in that the
term “native” is overloaded to mean two different things.</p>
<p>The first refers to simply compiling CPython to run “natively” with
the Android kernel and libraries, just as it does on desktop
platforms, and this is what the ZDNet article refers to with e.g. the
quote “Android devices are now fast enough, and the Android kernel
permissive enough, to run CPython itself”. This is not a recent
development, it has been technically possible for many years, although
the article is correct that it comes with disadvantages and not
everything works the same as on desktop.</p>
<p>The second is using the “native” <span class="caps">GUI</span> toolkit for the platform. On
Android that means using the same widget toolkit as normal Java-based
development. This is the part that is difficult and not yet widely
supported, see below for more discussion about the recent
developments. I’m actually not sure if the ZDNet article intends to
focus on this, but perhaps fails because the author doesn’t understand
the distinction, certainly the article focuses on CPython technical details.</p>
<p>It’s absolutely true that having Python applications seamlessly
using the native <span class="caps">GUI</span> toolkit is essential to making Python a serious
alternative to Java for generic Android development. However, there
are huge areas of Python development where this simply isn’t a big
concern, e.g. everything from games (Pygame, Kivy, Renpy), to
scientific visualisation (matplotlib, Vispy, other toolkits), to
novel user interfaces (Jupyter notebooks or other apps that
don’t care about the native <span class="caps">GUI</span> toolkit for whatever reason). All of
these things are tremendously popular on the desktop without using the
native <span class="caps">GUI</span> toolkit, because that detail is either irrelevant or
specifically at odds with what they want to do.</p>
</div>
<div class="section" id="history-of-python-for-android-projects">
<h2>History of Python for Android projects</h2>
<p>The following projects are ones I remember as interesting and
historically important in terms of creating Python applications on
Android, in rough date order. This list undoubtedly isn’t complete and
is strongly based on events as I remember them, I might have missed
other important projects and I’m certainly missing details from before
about 2012! I’ve also focused on application build tools, not the many
individual patches and Python contributions that made them possible.</p>
<div class="section" id="pygame-subset-for-android-pgs4a">
<h3>Pygame Subset for Android (<span class="caps">PGS4A</span>)</h3>
<p>Pygame gained rudimentary Android support fairly early in Android’s
history. I’m not sure exactly what happened when, but <a class="reference external" href="https://groups.google.com/forum/#!topic/pygame-mirror-on-google-groups/wTk8vtHfBXE">this old
release announcement</a>
is from February 2011 and isn’t the first version.</p>
<p><span class="caps">PGS4A</span> worked by compiling CPython for Android, with a modified version
of <span class="caps">SDL</span> (the gui library that Pygame uses), and some Java code to get
the app to start and display a surface that pygame could draw to. I
believe there were some limitations to what you could do (there’s a
reason it was a “subset” of Pygame), but I’m not sure what these were.</p>
<p>I remember people were actively using <span class="caps">PGS4A</span> around 2012 when I started
looking at it myself, but its popularity slowly diminished over time,
probably due to lack of developer support combined with its limitations.</p>
</div>
<div class="section" id="renpy">
<h3>Renpy</h3>
<p><a class="reference external" href="https://www.renpy.org/">Ren’Py</a> is a visual novel engine with
great cross-platform support. Actually, I suspect it’s
quietly one of the most successful Python game engine projects,
e.g. you can find quite a few Renpy-made games available via Steam.</p>
<p>Renpy gained Android support a long time ago, maybe in 2011 or
earlier. I believe that as with <span class="caps">PGS4A</span>, it worked by combining pygame
with a modified version of <span class="caps">SDL</span> and some custom Java code to display an
app surface that Renpy could draw to. I’m not sure if the Renpy build
tools actually had history in common with <span class="caps">PGS4A</span>, but I don’t think
they were the same project at any point I remember.</p>
<p>Renpy’s Android support hasn’t been static, it’s evolved
since 2011. At some point around 2014-2015 it switched to use a <a class="reference external" href="https://github.com/renpy/pygame_sdl2">fork
of Pygame using <span class="caps">SDL2</span></a>, with
corresponding updates to the Android build process. This makes sense
because <span class="caps">SDL2</span> itself supports Android properly, removing a huge
maintenance burden. Note that this pygame_sdl2 project is not the same
as the <span class="caps">SDL2</span> support currently under development from the core Pygame
team, I don’t think Renpy’s pygame_sdl2 is in wide use and I don’t
know what its limitations are.</p>
</div>
<div class="section" id="kivy-and-python-for-android">
<h3>Kivy and python-for-android</h3>
<p><a class="reference external" href="https://kivy.org/#home">Kivy</a> is easily the most well known
Android-supporting Python toolkit I’m aware of, and has been since
around 2012 when I came across it myself after failing to get a Java
Android tutorial working. Kivy is a graphical toolkit that was
not specifically designed for mobile support, but instead focused on
being generically cross-platform and supporting novel user
interfaces. This turned out very timely, as these properties made it
very well suited for Android and iOS support, although Kivy also
supports desktop use. Kivy draws its <span class="caps">GUI</span> using OpenGL, which
has the advantage of working essentially the same on all different
platforms, but this can also be a disadvantage in that it means not
using the native <span class="caps">GUI</span> toolkit as discussed above.</p>
<p>My understanding is that Kivy’s Android support was originally based
on Renpy’s Android build tools, which through some amount of
collaboration and changes from different places ended up being the
genesis for Kivy’s python-for-android project, first commit
November 2011. Renpy’s Android build project then shifted to use a
fork of python-for-android at some point, after which the projects
have been developed separately.</p>
<p>Python-for-android was originally quite Kivy-specific, but was totally
rewritten in 2015 to be a more generic and modular build tool,
coinciding with Kivy’s own transition to <span class="caps">SDL2</span> as a default backend (in
fact this is where I really got involved). Since then its
breadth of support has increased dramatically.</p>
</div>
<div class="section" id="beeware">
<h3>Beeware</h3>
<p>BeeWare is a collection of tools and libraries for building Python
applications across different platforms, both desktop and
mobile. These projects have a particular focus on manipulating the
native graphical toolkits of a given platform, e.g. on Android they
want to use the same “native” <span class="caps">GUI</span> widgets as a normal Java-built
application. Its <a class="reference external" href="https://github.com/beeware/toga">toga</a> toolkit
provides a platform-independent <span class="caps">GUI</span> abstraction for this, in
combination with platform-specific toolkits for each individual target.</p>
<p>I believe the BeeWare developers (or at least the core developer
Russell Keith-Magee) did some initial experimentation using CPython on
Android somewhere around 2015. The idea there would be to create and
manipulate the normal Java-native <span class="caps">GUI</span> widgets using Java Native
Interface (<span class="caps">JNI</span>). This is actually possible, it’s something we also
support in Kivy and is occasionally useful to e.g. display a
webview. Unfortunately it has some key disadvantages including that
Python is still quite slow to start, and in particular that Android
used to enforce a fairly low limit on the number of <span class="caps">JNI</span> references
that could be simultaneously maintained, which makes building a full
<span class="caps">GUI</span> impractical. My understanding is that a combination of these
factors made CPython use impractical for Beeware on Android.</p>
<p>BeeWare instead switched to creating <a class="reference external" href="https://github.com/beeware/voc"><span class="caps">VOC</span></a>, a Python code to Java bytecode
transpiler. This converts the Python input into genuine Java bytecode
that can run as a normal app without the above limitations. I haven’t
tried this for some time, but I understand it works fine. However, it
seems the difficulty of supporting the full breadth of Python
libraries has been a barrier (at least, that’s been my impression from
watching discussions about it, I think it’s still under active
development and working well).</p>
<p>Fortunately Android itself has improved, and in particular the
limitation on <span class="caps">JNI</span> references is no longer present in recent
versions. In 2019 BeeWare switched back to targeting CPython on
Android, <a class="reference external" href="https://beeware.org/news/buzz/beeware-project-awarded-a-psf-education-grant/">supported by a <span class="caps">PSF</span> grant</a>. It
is the outcome of this grant that led to the discussions and article I
linked at the top. See the end of this post for a brief summary.</p>
</div>
<div class="section" id="chaquopy">
<h3>Chaquopy</h3>
<p><a class="reference external" href="https://chaquo.com/chaquopy/">Chaquopy</a> provides build tools for
both including Python code in Java applications, and building apps
entirely in Python. I’m not sure about the technical details, but I’ve
been consistently aware of it as an active project since about 2017,
so it may be useful to anyone interested in this sort of thing. I
guess there must be some overlap with what python-for-android does,
but Chaquopy’s integration of Python and Java code seems to be more of
a focus.</p>
</div>
<div class="section" id="pyqtdeploy">
<h3>pyqtdeploy</h3>
<p>The popular Qt graphical framework <a class="reference external" href="https://doc.qt.io/qt-5/android.html">supports Android</a>. Python bindings to this
framework are quite popular, so it’s not a huge surprise that there’s
some level of Python for Android support using Qt for the <span class="caps">GUI</span>. As far
as I’m aware <a class="reference external" href="https://www.riverbankcomputing.com/software/pyqtdeploy/intro">pyqtdeploy</a> is
the primary build tool for this, using the PyQt bindings, but I may
not be up to date about it. I’ve never seen this to be very popular,
but I don’t know if there’s a reason for this beyond its relative obscurity.</p>
</div>
</div>
<div class="section" id="what-are-actually-the-recent-developments-in-cpython-on-android">
<span id="developments"></span><h2>What are actually the recent developments in CPython on Android?</h2>
<p>Various contributors have driven improvements in CPython’s Android
support over the years. I would give more details but honestly I’ve
never found the time to get deeply involved so the historical summary
is limited by my own ignorance! This has brought CPython to the point
of being fairly easy to compile for Android as of about version 3.6.
For instance, python-for-android’s CPython build recipe applies no
essential patches to the Python 3 source, just an appropriate set of
build arguments.</p>
<p>The key recent development is the BeeWare project’s <a class="reference external" href="https://beeware.org/news/buzz/beeware-project-awarded-a-psf-education-grant/">switch to CPython</a>
explained above. They’ve made a specific goal of understanding where
CPython’s Android support can be improved, and getting involved to
resolve these problems. This means attacking both individual technical
issues (e.g. getting Python’s test suit passing correctly), and
longer-term structural problems (e.g. the suggestion described in the
ZDNet article to create a stripped-back Python kernel for mobile use).</p>
<p>These recent developments are great, and hopefully will lead to huge
improvements in the ease of deploying Python applications for Android,
especially addressing the missing functionality of using the native
Java <span class="caps">GUI</span> toolkit. However, let’s not forget the history of CPython on
Android, people have been creating applications for both business and
pleasure for many years.</p>
</div>
Getting started with Kivy2019-12-19T22:00:00+01:002019-12-19T22:00:00+01:00Alexander Taylortag:inclem.net,2019-12-19:/2019/12/19/kivy/getting_started_with_kivy/<p>This post collates various resources for getting started with the
<a class="reference external" href="https://kivy.org/#home">Kivy graphical framework</a> for Python.</p>
<div class="section" id="installation">
<h2>Installation</h2>
<p>Follow the <a class="reference external" href="https://kivy.org/#download">official installation documentation</a>.</p>
</div>
<div class="section" id="introductory-resources">
<h2>Introductory resources</h2>
<ul class="simple">
<li>Official Kivy docs<ul>
<li>The <a class="reference external" href="https://kivy.org/doc/stable/gettingstarted/intro.html">official Kivy “Getting Started” pages</a>. These
cover general introductory concepts, but are a bit eclectic.</li>
<li>Kivy’s official <a class="reference external" href="https://kivy.org/doc/stable/tutorials/pong.html">Pong game tutorial</a></li>
<li>Kivy’s …</li></ul></li></ul></div><p>This post collates various resources for getting started with the
<a class="reference external" href="https://kivy.org/#home">Kivy graphical framework</a> for Python.</p>
<div class="section" id="installation">
<h2>Installation</h2>
<p>Follow the <a class="reference external" href="https://kivy.org/#download">official installation documentation</a>.</p>
</div>
<div class="section" id="introductory-resources">
<h2>Introductory resources</h2>
<ul class="simple">
<li>Official Kivy docs<ul>
<li>The <a class="reference external" href="https://kivy.org/doc/stable/gettingstarted/intro.html">official Kivy “Getting Started” pages</a>. These
cover general introductory concepts, but are a bit eclectic.</li>
<li>Kivy’s official <a class="reference external" href="https://kivy.org/doc/stable/tutorials/pong.html">Pong game tutorial</a></li>
<li>Kivy’s <a class="reference external" href="https://kivy.org/doc/stable/tutorials/firstwidget.html">paint app tutorial</a>.</li>
</ul>
</li>
<li>Text tutorials<ul>
<li>My <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">from-scratch introduction to Kivy</a> (<a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_001_say_hello/">direct link to first
entry</a>).</li>
</ul>
</li>
<li>Videos<ul>
<li>My <a class="reference external" href="https://www.youtube.com/watch?v=F7UKmK9eQLY&list=PLdNh1e1kmiPP4YApJm8ENK2yMlwF1_edq">Kivy crash course</a>.</li>
<li>Sentdex’s <a class="reference external" href="https://www.youtube.com/watch?v=CYNWK2GpwgA&list=PLQVvvaa0QuDe_l6XiJ40yGTEqIKugAdTy">Kivy application development</a> series.</li>
<li><a class="reference external" href="https://www.youtube.com/watch?v=bMHK6NDVlCM">Kivy tutorials</a> by Tech With Tim.</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="general-resources">
<h2>General resources</h2>
<ul class="simple">
<li>The <a class="reference external" href="https://kivy.org/doc/stable/api-kivy.html">official Kivy documentation</a>. Linked is the <span class="caps">API</span>
reference, almost everything in Kivy is documented in there somewhere.</li>
</ul>
</div>
<div class="section" id="kivy-examples">
<h2>Kivy examples</h2>
<p>Kivy comes with <a class="reference external" href="https://github.com/kivy/kivy/tree/master/examples">many examples</a>, with fairly
complete coverage of most widgets and other core features. These are
very valuable for working out how to use things.</p>
<p>On some platforms, the examples may be installable as a separate
package from your operating system’s package manager.</p>
<p>See in particular:</p>
<ul class="simple">
<li><a class="reference external" href="https://github.com/kivy/kivy/tree/master/examples/widgets">Widget examples</a></li>
<li><a class="reference external" href="https://github.com/kivy/kivy/tree/master/examples/canvas">Canvas instruction examples</a></li>
</ul>
</div>
<div class="section" id="support-channels">
<h2>Support channels</h2>
<ul class="simple">
<li>The <a class="reference external" href="https://chat.kivy.org/">official Kivy Discord channels</a> are
the single most active support channel.</li>
<li>The <a class="reference external" href="https://groups.google.com/forum/embed/?place=forum/kivy-users">Kivy-users google group</a>.</li>
<li>The <a class="reference external" href="https://github.com/kivy/kivy/issues">issues list on Github</a>. This should only be used
if reporting an apparent bug in Kivy itself, it isn’t a good place
for generic support requests.</li>
<li>Kivy users and developers may be active on <a class="reference external" href="https://pythondiscord.com/">Python Discord</a>.</li>
</ul>
</div>
Kivy tutorial 009: Finishing the drawing app2019-12-18T22:38:00+01:002019-12-18T22:38:00+01:00Alexander Taylortag:inclem.net,2019-12-18:/2019/12/18/kivy/kivy_tutorial_009_finishing_the_drawing_app/<p>This is number 9 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Passing data between widgets, creating Kivy properties</p>
<p>This tutorial directly follows on from the previous, so start by
retrieving the previous code, as below:</p>
<p>main.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout …</span></pre></div><p>This is number 9 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Passing data between widgets, creating Kivy properties</p>
<p>This tutorial directly follows on from the previous, so start by
retrieving the previous code, as below:</p>
<p>main.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span><span class="p">,</span> <span class="n">Line</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">+</span> <span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="k">class</span> <span class="nc">Interface</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Interface</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>drawing.kv:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">DrawingWidget</span><span class="o">></span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgba</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="o"><</span><span class="n">ColourSlider</span><span class="nd">@Slider</span><span class="o">></span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">red_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">green_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">blue_slider</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
</pre></div>
<p>With this code, you should still be able to draw in the DrawingWidget
region of the app interface, but the lines still have a random colour
each time. Our final task is to make the lines use the colour selected
via the sliders.</p>
<p>Let’s start by reassessing where the app’s state should be held. For a
start, we need to store somewhere the target colour for the
lines. It’s natural to put this inside the DrawingWidget, since this
class is what does the drawing and needs to know what colour to use.</p>
<p>The best way to store this data is to use a Kivy property of our
own. We’ve made use of many Kivy properties of other widgets already,
but this time there isn’t one already created to hold the colour, so
it’s time to create one.</p>
<p>Change the <tt class="docutils literal">DrawingWidget</tt> code as follows:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ListProperty</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="n">target_colour_rgb</span> <span class="o">=</span> <span class="n">ListProperty</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">+</span> <span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
</pre></div>
<p>That’s all it takes to define a new Kivy property, and it
automatically has all the behaviour you’ve seen so far. For instance,
if you change the <tt class="docutils literal">target_colour_rgb</tt> of a DrawingWidget instance,
an event is automatically dispatched. In fact because this is a
ListProperty an event will be dispatched even if we just change the
value of an item of the list! There are other types of Kivy property
for ensuring correct event dispatching with different types of object
(list, dict, int/float, generic objects etc.), which you can find in
<a class="reference external" href="https://kivy.org/doc/stable/api-kivy.properties.html">the documentation</a>.</p>
<p><strong>Note:</strong> It may look a little strange that we’ve defined the property
at the class level (no reference to <tt class="docutils literal">self.target_colour_rgb</tt>), and
it is clearly a ListProperty and not an actual list so how does
accessing its values work? The answer is that Kivy properties are
<a class="reference external" href="https://docs.python.org/3/howto/descriptor.html">descriptors</a>,
which are defined at the class level but here are coded to behave
like normal lists/ints/whatever when accessed from a class
instance. You don’t need to worry about these details, just consider
the properties as normal attributes of your objects when accessing them.</p>
<p>As an example of what that really means, lets hook up the property to
change so that we can respond to these events. Change your kv file
<tt class="docutils literal"><Interface></tt> rule to read as follows:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">target_colour_rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span> <span class="c1"># <- new line</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">red_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">green_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">blue_slider</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
</pre></div>
<p>Note that the only new line here sets the value of <cite>target_colour_rgb</cite>
based on the values of the sliders. We’re once again taking advantage
of automatic kv event binding: whenever any of <tt class="docutils literal">red_slider.value</tt>,
<tt class="docutils literal">green_slider.value</tt> or <tt class="docutils literal">blue_slider.value</tt> changes then this line
will be re-evaluated to update <tt class="docutils literal">target_colour_rgb</tt>. We can add
some code to prove that it’s working, via a new method in the
<tt class="docutils literal">DrawingWidget</tt> class:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">on_target_colour_rgb</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="n">f</span><span class="s2">"target_colour_rgb changed to {self.target_colour_rgb}"</span><span class="p">)</span>
</pre></div>
<p>A method with this name will be called automatically whenever the
<tt class="docutils literal">target_colour_rgb</tt> property changes - this is another handy feature
of Kivy event dispatching, instead of binding explicitly this default
event method is always available. You can consider that code something
like <tt class="docutils literal">self.bind(target_colour_rgb=self.on_target_colour_rgb)</tt> has
been automatically run to create the event binding.</p>
<p>Now, run the application and move the values of the sliders. You
should see code printed in your terminal every time a slider moves,
because every movement updates the value of <tt class="docutils literal">target_colour_rgb</tt>:</p>
<div class="highlight"><pre><span></span><span class="n">target_colour_rgb</span> <span class="n">changed</span> <span class="n">to</span> <span class="p">[</span><span class="mf">0.20853658536585365</span><span class="p">,</span> <span class="mf">0.6012195121951219</span><span class="p">,</span> <span class="mf">0.4573170731707317</span><span class="p">]</span>
<span class="n">target_colour_rgb</span> <span class="n">changed</span> <span class="n">to</span> <span class="p">[</span><span class="mf">0.20853658536585365</span><span class="p">,</span> <span class="mf">0.6012195121951219</span><span class="p">,</span> <span class="mf">0.4585365853658537</span><span class="p">]</span>
<span class="n">target_colour_rgb</span> <span class="n">changed</span> <span class="n">to</span> <span class="p">[</span><span class="mf">0.20853658536585365</span><span class="p">,</span> <span class="mf">0.6012195121951219</span><span class="p">,</span> <span class="mf">0.45975609756097563</span><span class="p">]</span>
</pre></div>
<p><strong>Note:</strong> The colour changes in this example are very small because
you’re getting an update every time the slider moves even a single pixel!</p>
<p>The final step is to make the DrawingWidget use this target colour
for the next line it draws. For this we just have to update the
<tt class="docutils literal">on_touch_down</tt> method:</p>
<blockquote>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">target_colour_rgb</span><span class="p">)</span> <span class="c1"># <- this line changed</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
</pre></div>
</blockquote>
<p>That’s all there is to it! When we make the new Color instruction for
the new line, we pass in the current value of our property instead of
selecting random values.</p>
<p>Run the app now and every line should match your currently selected colour:</p>
<div class="figure align-center">
<img alt="Line drawn using selected colour" src="https://inclem.net/media/kivy_text_tutorials/09_colour_select_works.png" style="width: 400px;" />
</div>
<p>For a further example, let’s add a similar method to set the Line
width. Again, we add a Kivy property to DrawingWidget:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ListProperty</span><span class="p">,</span> <span class="n">NumericProperty</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="n">target_colour_rgb</span> <span class="o">=</span> <span class="n">ListProperty</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>
<span class="n">target_width_px</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
<p>Then in the kv <tt class="docutils literal"><Interface></tt> rule we add a Slider to select the
width, and connect it to the property:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">target_colour_rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">target_width_px</span><span class="p">:</span> <span class="n">width_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">red_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">green_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">blue_slider</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s2">"width: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">width_slider</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">width_slider</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">2</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">10</span>
<span class="n">value</span><span class="p">:</span> <span class="mi">2</span>
</pre></div>
<p>And finally, update the <tt class="docutils literal">DrawingWidget.on_touch_down</tt> to use the
currently-selected width for the new Line instruction:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">target_colour_rgb</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span>
<span class="n">width</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">target_width_px</span><span class="p">)</span>
</pre></div>
<p><strong>Note:</strong> I’ve added not just a single Slider, but a new BoxLayout to
the kv rule, in order to display a Label indicating what the slider
is for. Notice how, consistent with everything so far, the Label
automatically updates to always show the current value of the Slider.</p>
<p>Run the app and try the drawing. You should now be able to control
both the colour and width of every line:</p>
<div class="figure align-center">
<img alt="Lines drawn using selected colour and width" src="https://inclem.net/media/kivy_text_tutorials/09_fully_working_with_width.png" style="width: 400px;" />
</div>
<p>With that, the application is fully connected together. We have a <span class="caps">UI</span>
element for drawing, alongside extra elements for controlling the
details of the lines, with data passed around using Kivy
properties. These basic ideas are at the heart of all Kivy applications.</p>
<p>This would be a good time to experiment. Try adding or removing
widgets, and maybe adding more customisation to the lines.</p>
<div class="section" id="full-code">
<h2>Full code</h2>
<div class="highlight"><pre><span></span><span class="n">main</span><span class="o">.</span><span class="n">py</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span><span class="p">,</span> <span class="n">Line</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ListProperty</span><span class="p">,</span> <span class="n">NumericProperty</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="n">target_colour_rgb</span> <span class="o">=</span> <span class="n">ListProperty</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>
<span class="n">target_width_px</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">target_colour_rgb</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span>
<span class="n">width</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">target_width_px</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">+</span> <span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="k">def</span> <span class="nf">on_target_colour_rgb</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="n">f</span><span class="s2">"target_colour_rgb changed to {self.target_colour_rgb}"</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">Interface</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Interface</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>drawing.kv:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">DrawingWidget</span><span class="o">></span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgba</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="o"><</span><span class="n">ColourSlider</span><span class="nd">@Slider</span><span class="o">></span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">target_colour_rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">target_width_px</span><span class="p">:</span> <span class="n">width_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">red_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">green_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">blue_slider</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s2">"width: {:.1f}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">width_slider</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">width_slider</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">2</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">10</span>
<span class="n">value</span><span class="p">:</span> <span class="mi">2</span>
</pre></div>
</div>
Kivy tutorial 008: More kv language2019-12-18T22:37:00+01:002019-12-18T22:37:00+01:00Alexander Taylortag:inclem.net,2019-12-18:/2019/12/18/kivy/kivy_tutorial_008_more_kv_language/<p>This is number 8 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Event binding and canvas instructions in kv language</p>
<p>This tutorial directly follows on from the previous, so start by
retrieving the previous code, as below:</p>
<p>main.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import …</span></pre></div><p>This is number 8 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Event binding and canvas instructions in kv language</p>
<p>This tutorial directly follows on from the previous, so start by
retrieving the previous code, as below:</p>
<p>main.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span><span class="p">,</span> <span class="n">Line</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="p">,</span>
<span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_rectangle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">+</span> <span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="k">class</span> <span class="nc">Interface</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Interface</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>drawing.kv:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
</pre></div>
<p>The first thing to do is draw the coloured Rectangle that the final
Widget uses to display an output colour, and for this we need to know
how to draw canvas instructions in kv language. The syntax is as below:</p>
<div class="highlight"><pre><span></span><span class="n">Widget</span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgb</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1"># using a fixed colour for now</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
</pre></div>
<p>Run the code, and you’ll see another of kv language’s most important
features; <em>automatic event binding</em>. In the original Python code of
tutorial 7 we needed an extra <tt class="docutils literal"><span class="pre">.bind(...)</span></tt> call to make the
be updated to always be placed within its Widget. In kv language this
is not necessary, the dependency on <tt class="docutils literal">self.size</tt> and
<tt class="docutils literal">self.pos</tt> is automatically detected, and a binding
automatically created!</p>
<p>This is also the generic syntax for canvas instructions; first add
<tt class="docutils literal">canvas:</tt> (or <tt class="docutils literal">canvas.before</tt> or <tt class="docutils literal">canvas.after</tt>),
then, indent by 4 spaces, and add canvas instructions much like you
would Widgets. However, note that canvas instructions are <em>not</em> widgets.</p>
<p>The only thing now missing from the original Python interface
implementation in tutorial 7 is having the Sliders automatically
update the output colour rectangle. Change the <tt class="docutils literal"><Interface>:</tt>
rule to the following:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">red_slider</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">green_slider</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">blue_slider</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
</pre></div>
<p>There are actually only two changes here; we gave each Slider an
<tt class="docutils literal">id</tt> declaration, and in the canvas Color referred to the
sliders with this name. Giving a widget an id is just like naming it
in Python so that you can refer to it elsewhere.</p>
<p>Thanks to kv’s automatic binding, this is all we need to do to have
the Color update automatically whenever a slider value changes. Run
the code, and you should see that things work exactly as they did in
the original Python interface.</p>
<p>We can finish this tutorial with a couple of extra kv
conveniences. First, just as we added an automatically updating
Rectangle in the Widget kv, we can do the same for the background of
the DrawingWidget. Delete the <tt class="docutils literal">__init__</tt> and
<tt class="docutils literal">update_rectangle</tt> methods in the Python DrawingWidget code, and
add a new rule in the kv file:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">DrawingWidget</span><span class="o">></span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgba</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
</pre></div>
<p>Second, you might have noticed that there’s a lot of code duplication
in each of the Slider rules - we set the same <tt class="docutils literal">min</tt>,
<tt class="docutils literal">max</tt>, initial <tt class="docutils literal">value`, ``size_hint_y`</tt> and
<tt class="docutils literal">height</tt> for every one. As is normal in Python, it would be
natural to abstract this in a new class, so as to set each value only
once. You can probably already see how to do this with what we’ve
learned so far (make a new <tt class="docutils literal">class YourSlider(Slider):</tt> in the
Python and add a new <tt class="docutils literal"><YourSlider>:</tt> rule in the kv), but I’ll
note that you can even do this entirely in kv:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">ColourSlider</span><span class="nd">@Slider</span><span class="o">></span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">red_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">green_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">blue_slider</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
</pre></div>
<p>The new <tt class="docutils literal"><ColourSlider@Slider>:</tt> rule defines a <em>dynamic class</em>, a
Python class kv rule without a corresponding Python code
definition. This is convenient if you want to do something repeatedly
only in kv, and never access it from Python. These Sliders are a good
example: they don’t need any extra code, just some default property
values, so using a throwaway dynamic class gets that out of the way.</p>
<p>At this point, we’ve reached feature parity with the original Python
code, and seen all the basics of kv language. In the next tutorial
we’ll finish off the original purpose of all these sliders; letting
the user set the colour of line that is drawn by the DrawingWidget.</p>
<p><strong>Next tutorial:</strong> <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_009_finishing_the_drawing_app/">Finishing the drawing app</a></p>
<div class="section" id="full-code">
<h2>Full code</h2>
<p>main.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span><span class="p">,</span> <span class="n">Line</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">+</span> <span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="k">class</span> <span class="nc">Interface</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Interface</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>drawing.kv:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">DrawingWidget</span><span class="o">></span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgba</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="o"><</span><span class="n">ColourSlider</span><span class="nd">@Slider</span><span class="o">></span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">red_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">green_slider</span>
<span class="n">ColourSlider</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">blue_slider</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgb</span><span class="p">:</span> <span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
</pre></div>
</div>
Kivy tutorial 007: Introducing kv language2019-12-18T22:36:00+01:002019-12-18T22:36:00+01:00Alexander Taylortag:inclem.net,2019-12-18:/2019/12/18/kivy/kivy_tutorial_007_introducing_kv_language/<p>This is number 7 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> kv language, building a gui, integration with Python</p>
<p>The goal of this tutorial will be to build up a simple gui around the
DrawingWidget built in the last two tutorials. A nice simple goal
would be to …</p><p>This is number 7 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> kv language, building a gui, integration with Python</p>
<p>The goal of this tutorial will be to build up a simple gui around the
DrawingWidget built in the last two tutorials. A nice simple goal
would be to let the user select the colour of the lines. Kivy actually
has a ColorPicker Widget for this purpose (see the <a class="reference external" href="https://kivy.org/docs/api-kivy.uix.colorpicker.html">documentation</a>), but we’ll
skip that for now in order to continue demonstrating Kivy widget construction.</p>
<p><strong>Note:</strong> Since all Kivy widgets are built out of other Widgets and
canvas instructions, you might like to think about how you’d build the
ColorPicker from scratch.</p>
<p>Let’s start with the code from last time, minus the now-unnecessary
red Rectangle:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span><span class="p">,</span> <span class="n">Line</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="p">,</span>
<span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_rectangle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">+</span> <span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">DrawingWidget</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>I’ll demonstrate adding the new gui components in two ways; first in
pure Python as has been demonstrated in previous tutorials, and second
using kv language instead. So, here’s a Python implementation of the
new features we want, beginning with importing the Widget classes
we’ll need:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
</pre></div>
<p>Slider is a previously-unseen Widget displaying a draggable marker. We’ll be using a
Slider for each primary colour (red, blue, green), and using this to
set the Color when a Line is drawn.</p>
<p>We can now update the build method of DrawingApp, replacing the root
widget and adding the new gui components:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'vertical'</span><span class="p">)</span>
<span class="n">drawing_widget</span> <span class="o">=</span> <span class="n">DrawingWidget</span><span class="p">()</span>
<span class="n">red_slider</span> <span class="o">=</span> <span class="n">Slider</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
<span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
<span class="n">green_slider</span> <span class="o">=</span> <span class="n">Slider</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
<span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
<span class="n">blue_slider</span> <span class="o">=</span> <span class="n">Slider</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
<span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
<span class="n">colour_row</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'horizontal'</span><span class="p">,</span>
<span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
<span class="n">colour_label</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'output colour:'</span><span class="p">)</span>
<span class="n">colour_widget</span> <span class="o">=</span> <span class="n">Widget</span><span class="p">()</span>
<span class="c1"># We draw a Rectangle on colour_widget exactly the same way as</span>
<span class="c1"># with DrawingWidget, just without making a new class</span>
<span class="k">with</span> <span class="n">colour_widget</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">output_colour</span> <span class="o">=</span> <span class="n">Color</span><span class="p">(</span><span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
<span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
<span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="n">output_rectangle</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">update_colour_widget_rect</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">output_rectangle</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">colour_widget</span><span class="o">.</span><span class="n">pos</span>
<span class="n">output_rectangle</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="n">colour_widget</span><span class="o">.</span><span class="n">size</span>
<span class="n">colour_widget</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="n">update_colour_widget_rect</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="n">update_colour_widget_rect</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_colour_widget_colour</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">output_colour</span><span class="o">.</span><span class="n">rgb</span> <span class="o">=</span> <span class="p">(</span><span class="n">red_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
<span class="n">green_slider</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
<span class="n">blue_slider</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="n">red_slider</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="n">update_colour_widget_colour</span><span class="p">)</span>
<span class="n">green_slider</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="n">update_colour_widget_colour</span><span class="p">)</span>
<span class="n">blue_slider</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="n">update_colour_widget_colour</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">drawing_widget</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">red_slider</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">green_slider</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">blue_slider</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">colour_row</span><span class="p">)</span>
<span class="n">colour_row</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">colour_label</span><span class="p">)</span>
<span class="n">colour_row</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">colour_widget</span><span class="p">)</span>
<span class="k">return</span> <span class="n">root_widget</span>
</pre></div>
<p>This is a lot of code to drop all at once, but read it carefully and
you’ll see that it’s only the same concepts already introduced: we
instantiate Widgets, add them to one another, and create bindings so
that things automatically happen when Kivy properties are changed. In
this case, we make use of the <tt class="docutils literal">value</tt> Kivy property of the
Slider widget, which gives its current value (changing automatically
when the slider is moved).</p>
<p>Run the code and you should see something like the image below. You
can update the colour in the bottom right by moving the sliders.</p>
<div class="figure align-center">
<img alt="Sliders bound to a colour change" src="https://inclem.net/media/kivy_text_tutorials/07_01_python_version.png" style="width: 400px;" />
</div>
<p>A problem now becoming obvious is that all this code is kind of
verbose, and also it can be a little unclear what is happening -
Widget instantiation is in a different place to where the Widgets are
added to one another, which is different again to where their events
are bound. You can mitigate this with a careful app structure and
following whatever coding conventions you like, but some of it is
unavoidable given how Python works.</p>
<p>It’s for this reason that Kivy comes with <em>kv language</em>, a simple but
powerful declaration language specifically designed for creating Kivy
widget trees. If learning a new language sounds worrying…don’t be
concerned! Kv isn’t a general purpose language, it doesn’t have much
special syntax and is targeted specifically at Kivy widgets. It also
uses normal Python code wherever possible (we’ll see that soon).</p>
<p>All of the kv language stuff discussed below is <a class="reference external" href="https://kivy.org/docs/guide/lang.html">documented on the
Kivy website</a>; I’ll cover
the basics, but you can find more information there.</p>
<p>First, get rid of <em>all</em> the Python code from above, and replace the
root widget return with the following:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Interface</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Interface</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
</pre></div>
<p>Kv language works by writing <em>rules</em> for Widget classes, which will be
automatically applied every time you instantiate one. We can use kv
for almost everything added to the app so far, but this time we’ll
construct the gui step by step to see how each part is added with the
new kv syntax. We’ll be writing a kv rule for the new
<tt class="docutils literal">Interface</tt> class.</p>
<p>To start using kv language, write the following code in a file named
<tt class="docutils literal">drawing.kv</tt>. This name comes from the name
of the App class, minus the App at the end if present, and in
lowercase (e.g. if you named your App <tt class="docutils literal">MySuperKivyApp</tt> you’d
need to name the file <tt class="docutils literal">mysuperkivy.kv</tt>). This is only necessary if
you want the file to be automatically loaded, you can also <a class="reference external" href="https://kivy.org/docs/guide/lang.html#how-to-load-kv">load files
or string manually</a>. Our first
kv code is:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'label added with kv'</span>
<span class="n">font_size</span><span class="p">:</span> <span class="mi">50</span>
</pre></div>
<p>Run the code again, and you should see the a Label with the given
text, as the kv file is automatically loaded and its
<tt class="docutils literal"><Interface></tt> rule applied.</p>
<div class="figure align-center">
<img alt="Label added with kv rule." src="https://inclem.net/media/kivy_text_tutorials/08_kv_rule.png" style="width: 400px;" />
</div>
<p>This demonstrates the core rules of kv syntax. A <em>kv rule</em> is created
with the <tt class="docutils literal"><WidgetName>:</tt> syntax. You can make a rule for <em>any</em>
widget, including built in ones (Kivy internally has a <a class="reference external" href="https://github.com/kivy/kivy/blob/master/kivy/data/style.kv">large kv file</a>), and
if you make multiple rules for the same Widget then all of them are
applied one by one.</p>
<p>Below the rule creation, we indent by 4 spaces and define values for
Kivy properties of the widget, and add child widgets. Lines like
<tt class="docutils literal">orientation: 'vertical'</tt> set Kivy properties just like we did
previously in the Python code. Note that everything to the right of
the colon is <em>normal Python code</em> - that doesn’t matter here, but for
instance we could equally well write <tt class="docutils literal">orientation: <span class="pre">''.join(['v',</span>
'e', 'r', 't', 'i', 'c', 'a', <span class="pre">'l'])</span></tt> and it would be exactly the
same. You can set any Kivy property of a widget in this way, finding
the available options in the documentation as previously discussed.</p>
<p>We can also add child widgets by writing the widget name with a colon,
then indenting by a further 4 spaces, as is done here with the
<tt class="docutils literal">Label</tt>. After this you can keep going as deep as you like,
setting properties or adding more child widgets.</p>
<p>We can use these pieces of syntax to construct the previous Python
interface entirely in kv:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
</pre></div>
<p>This hasn’t yet set up the event binding, but the full widget tree has
been constructed entirely using the kv syntax described above. The
immediate advantage of this is that kv language directly expresses the
widget tree - there are no longer separate steps for instantiating
Widgets, setting their properties and adding them to one
another. Instead, you get to see everything at once.</p>
<p>This gui doesn’t yet have the behaviour of the Python one (i.e. having
the sliders control output colour), but in the interest of keeping
these tutorials relatively short, I’ll stop here for now. In the next
tutorial will see how kv language also makes event binding very easy.</p>
<p><strong>Next tutorial:</strong> <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_008_more_kv_language/">More kv language</a></p>
<div class="section" id="full-code">
<h2>Full code</h2>
<p>main.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span><span class="p">,</span> <span class="n">Line</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="p">,</span>
<span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_rectangle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">collide_point</span><span class="p">(</span><span class="o">*</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">):</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">+</span> <span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="k">class</span> <span class="nc">Interface</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Interface</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>drawing.kv:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">Interface</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">DrawingWidget</span><span class="p">:</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Slider</span><span class="p">:</span>
<span class="nb">min</span><span class="p">:</span> <span class="mi">0</span>
<span class="nb">max</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">value</span><span class="p">:</span> <span class="mf">0.5</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="mi">50</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'output colour:'</span>
<span class="n">Widget</span><span class="p">:</span>
</pre></div>
</div>
Kivy tutorial 006: Let’s draw something2019-12-18T22:35:00+01:002019-12-18T22:35:00+01:00Alexander Taylortag:inclem.net,2019-12-18:/2019/12/18/kivy/kivy_tutorial_006_lets_draw_something/<p>This is number 6 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Handling touch or mouse input, more canvas instructions</p>
<p>In this tutorial we’ll directly add touch handling to the basic code
developed in tutorial 5, starting with the code from last time:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App …</span></pre></div><p>This is number 6 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Handling touch or mouse input, more canvas instructions</p>
<p>In this tutorial we’ll directly add touch handling to the basic code
developed in tutorial 5, starting with the code from last time:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="p">,</span>
<span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># note that we must reset the colour</span>
<span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span>
<span class="n">pos</span><span class="o">=</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_rectangle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">DrawingWidget</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>We’ve already seen some input interaction via the Button widget, where
we could bind to its <tt class="docutils literal">on_press</tt> event to have a function called
whenever the Button was clicked. This is great for a Button, but is
not a general way to handle interaction - it gives no indication of
the position of the touch, or any other information like the button
clicked on the mouse.</p>
<p>Kivy achieves general mouse/touch handling via the
<tt class="docutils literal">on_touch_down</tt>, <tt class="docutils literal">on_touch_move</tt> and <tt class="docutils literal">on_touch_up</tt>
methods of all Widget classes. Let’s dive in with an example,
modifying our DrawingWidget:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="p">,</span>
<span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect_colour</span> <span class="o">=</span> <span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># note that we must reset the colour</span>
<span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span>
<span class="n">pos</span><span class="o">=</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_rectangle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect_colour</span><span class="o">.</span><span class="n">rgb</span> <span class="o">=</span> <span class="p">(</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">())</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'touch pos is {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">))</span>
</pre></div>
<p>Note that the only changes are to set <tt class="docutils literal">self.rect_colour</tt>, and to
add the <tt class="docutils literal">on_touch_down</tt> method. Run the code now, and whenever
you click the screen you should see the colour of the rectangle change.</p>
<p>How does this work? The answer is that whenever a mouse click or touch
is registered, the root widget’s <tt class="docutils literal">on_touch_down</tt> method is
called, with a <tt class="docutils literal">touch</tt> object holding information about the
touch: you can see this here, where we access the <tt class="docutils literal">pos</tt> of this
object to get the pixel coordinates of its position. Each widget
passes this touch object to all its children. For this reason, it’s
important to call <tt class="docutils literal"><span class="pre">super(...)</span></tt> if you want the touch to also be
passed to the current Widget’s children, though as it happens that’s
not actually important here.</p>
<p>Note that although these methods are called <tt class="docutils literal"><span class="pre">on_touch_...</span></tt>, and I’ve
called the argument <tt class="docutils literal">touch</tt>, they are used for both mouse and touch
handling; these events are handled in exactly the same way, except
that the touch object may contain different information such as the
button clicked (in the case of the mouse). I’ll mostly refer to this
input as ‘touch’, but this always includes mouse interaction too.</p>
<p>The other methods I mentioned, <tt class="docutils literal">on_touch_move</tt> and
<tt class="docutils literal">on_touch_up</tt>, work the same way; they are called whenever that
thing happens, though only when <tt class="docutils literal">on_touch_down</tt> has already
happened, you don’t get events when moving the mouse without having
clicked. We can use this to achieve drawing.</p>
<p>First, change the kivy.graphics import to include <tt class="docutils literal">Line</tt>:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span><span class="p">,</span> <span class="n">Line</span>
</pre></div>
<p>Then, add modify <tt class="docutils literal">on_touch_down</tt> and <tt class="docutils literal">on_touch_move</tt> to
draw and update a Line each time:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">+</span> <span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
</pre></div>
<p>Run the code again, and try clicking and dragging…you should see a
line! Each time you click and drag the line has a different colour, as
we add a new random Color instruction before its instruction each
time. We’re updating it by adding the x and y value of the touch
position to the Line’s points, every time the touch is moved.</p>
<div class="figure align-center">
<img alt="Lines drawn in example app" src="https://inclem.net/media/kivy_text_tutorials/06_lines.png" style="width: 400px;" />
</div>
<p>You can also note that we only use <tt class="docutils literal">with self.canvas</tt> when the
Line is instantiated - not when it is updated. This is because we only
need to add the Line canvas instruction to the canvas once, after that
the gui will automatically be updated whenever the Line changes,
including if we modified e.g. its width. Try changing
<tt class="docutils literal">self.line.width</tt> in <tt class="docutils literal">on_touch_move</tt> and see what happens.</p>
<p><strong>Note:</strong> This way of storing the line (in <tt class="docutils literal">self.line</tt>) isn’t very
robust if there are multiple simultaneous interactions, e.g. in a
multitouch display. This is easy to resolve by storing the reference
somewhere more appropriate, e.g. we could set <tt class="docutils literal"><span class="pre">touch.ud['line']</span></tt> to
store a reference specific to the touch object (<tt class="docutils literal">ud</tt> stands for
“user dictionary”, see <a class="reference external" href="https://kivy.org/doc/stable/api-kivy.input.motionevent.html#kivy.input.motionevent.MotionEvent.ud">the documentation</a>). You
might like to try implementing this.</p>
<p>You could continue here by experimenting with other actions in
response to touches, such as drawing different things (e.g. a
Rectangle at the touch position rather than a Line) or doing more
complex modifications to existing instructions.</p>
<p>With the basic drawing apparatus set up, the next tutorial will
introduce the <em>kv markup language</em>, showing how a gui can easily be
constructed without some of the Python boilerplate that comes from
using a general purpose language for creating a gui.</p>
<p><strong>Next tutorial:</strong> <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_007_introducing_kv_language/">Introducing kv language</a></p>
<div class="section" id="full-code">
<h2>Full code</h2>
<p>main.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span><span class="p">,</span> <span class="n">Line</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="p">,</span>
<span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect_colour</span> <span class="o">=</span> <span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># note that we must reset the colour</span>
<span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span>
<span class="n">pos</span><span class="o">=</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_rectangle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">on_touch_down</span><span class="p">(</span><span class="n">touch</span><span class="p">)</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">Line</span><span class="p">(</span><span class="n">points</span><span class="o">=</span><span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_touch_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">points</span> <span class="o">+</span> <span class="p">[</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">DrawingWidget</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
</div>
Kivy tutorial 005: A drawing app2019-12-18T22:34:00+01:002019-12-18T22:34:00+01:00Alexander Taylortag:inclem.net,2019-12-18:/2019/12/18/kivy/kivy_tutorial_005_a_drawing_app/<p>This is number 5 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Canvas instructions</p>
<p>The next couple of tutorials will move to a new application in order
to showcase some more of Kivy’s core components. In this tutorial
we’ll cover <em>canvas instructions</em>, Kivy’s low level drawing …</p><p>This is number 5 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Canvas instructions</p>
<p>The next couple of tutorials will move to a new application in order
to showcase some more of Kivy’s core components. In this tutorial
we’ll cover <em>canvas instructions</em>, Kivy’s low level drawing <span class="caps">API</span> which
is always available. In the next two, we’ll add touch/mouse
interaction to let you click to draw stuff, and then introduce <em>kv
language</em>, and show how it interacts with Python code to easily
produce guis without so much Python boilerplate.</p>
<p>To showcase Kivy’s drawing <span class="caps">API</span>, our next app will be a simple drawing
application. We’ll be making a widget gui to select a few different options
(colour, size etc.), and handling the mouse/touch interaction manually
to draw the result of user input.</p>
<p>We’ll need to start with a new basic app template, as introduced in
the first couple of tutorials:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>Before anything else, let’s start by getting some basic drawing
working, with no other gui components. There isn’t a Widget for
drawing already (there’s no nice way to abstract all the options you
might want), so instead Kivy makes it easy to build your own Widget class:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Width</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">DrawingWidget</span><span class="p">()</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>You can run the app now, but the screen will just be black because
Widget (and therefore DrawingWidget) doesn’t draw anything by default.
We’re using Widget as the base class because we want to add it to the
screen, but don’t need any extra behaviour beyond that.</p>
<p>Time to do our own drawing. Change your code to add the following:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># arguments are red, green, blue, alpha</span>
<span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span>
<span class="n">pos</span><span class="o">=</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">200</span><span class="p">))</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">DrawingWidget</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>If you run the app now, you’ll see a red rectangle. Its position in
pixels will be 300 right and 200 up from the bottom left of the
screen; Kivy’s coordinate system follows OpenGL using the bottom left
as the (0, 0) point.</p>
<div class="figure align-center">
<img alt="Rectangle in example app" src="https://inclem.net/media/kivy_text_tutorials/05_rectangle.png" style="width: 400px;" />
</div>
<p>This is the basic way of doing any kind of drawing, and with a
combination of canvas instructions (also called graphics instructions)
you can achieve any kind of gui result. In fact, anything you see
drawn with Kivy is ultimately using canvas instructions, including all
the built in widget classes!</p>
<p>To describe what actually happened here: we first opened a <tt class="docutils literal">with
self.canvas</tt> block. The canvas context manager sets an internal
variable that means all graphics instructions will automatically be
added to the canvas we just opened, in this case the canvas of the
current DrawingWidget. All widgets have a canvas that works the same
way, you can draw on e.g. a Label or BoxLayout if you want. Next we
instantiated our graphics instructions; in this case we use Color
(which sets the colour of any following instructions) and Rectangle
(which draws a rectangle at the given position). Any instructions you
add later will be drawn on top of the previous ones.</p>
<p>Try changing these arguments to modify what you see. The arguments to
Color are red, green, blue and alpha components (currently opaque
red). You can also try drawing other shapes by checking the <a class="reference external" href="https://kivy.org/docs/api-kivy.graphics.vertex_instructions.html">vertex
instruction documentation</a>
(vertex instructions are shapes, other instructions like Color are
claled <a class="reference external" href="https://kivy.org/docs/api-kivy.graphics.context_instructions.html">context instructions</a>
and include e.g. translation and rotation).</p>
<p><strong>Note:</strong> As with several other things mentioned so far, canvas
instructions have their own simple syntax for drawing in kv language,
introduced in <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_007_introducing_kv_language/">007: Introducing Kivy language</a>.</p>
<p><strong>Note:</strong> You can also access <tt class="docutils literal">self.canvas.before</tt> and
<tt class="docutils literal">self.canvas.after</tt>; everything in the former is drawn first, then
everything in <tt class="docutils literal">self.canvas</tt>, then everything in
<tt class="docutils literal">self.canvas.after</tt>. These are designed to cover most basic
requirements for layer-based drawing, but you can also introduce
further layers if you want to.</p>
<p>Let’s now draw a Rectangle that fills the whole DrawingWidget, to
serve as the background for anything else we draw:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="p">,</span>
<span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">DrawingWidget</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>Surprise, it doesn’t work right! Although we set the rectangle size to
<tt class="docutils literal">self.size</tt> (the size of the DrawingWidget), and its pos to
<tt class="docutils literal">self.pos</tt> (the pos of the DrawingWidget), it always appears in the
bottom left of the window and has size 100 pixels square. This is
because although the DrawingWidget automatically fills the window
(because it is the root widget), its pos and size are not set until
<em>after</em> its <tt class="docutils literal">__init__</tt> method has finished.</p>
<p><strong>Note:</strong> <tt class="docutils literal">pos</tt> and <tt class="docutils literal">size</tt> are two more Kivy properties that all
widgets have. They give the position of the bottom left corner (in
pixels) and the size of the Widget (also in pixels).</p>
<p>To solve this problem, we again use <em>event bindings</em>:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="p">,</span>
<span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_rectangle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
</pre></div>
<p>This works just like in the previous tutorials; we’ve bound to the
<tt class="docutils literal">pos</tt> and <tt class="docutils literal">size</tt> of the widget, and made it so that
whenever they update the Rectangle is also updated. Remember, this is
possible because <tt class="docutils literal">pos</tt> and <tt class="docutils literal">size</tt> are Kivy properties,
which you can also bind to (the function is called when their value
changes). When run, your app should now look like the following:</p>
<div class="figure align-center">
<img alt="Rectangle in example app" src="https://inclem.net/media/kivy_text_tutorials/05_output.png" style="width: 400px;" />
</div>
<p>This tutorial has introduced the basic use of <em>canvas instructions</em>,
including the notion of automatically updating them in response to gui
changes, thanks to event binding. This is an important building block
for building complex applications.</p>
<p>In the next tutorial we’ll introduce mouse/touch input handling, so
that we can finally draw something dynamicall in response to user input.</p>
<p><strong>Next tutorial:</strong> <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_006_lets_draw_something/">Let’s draw something</a></p>
<div class="section" id="full-code">
<h2>Full code</h2>
<p>main.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.slider</span> <span class="kn">import</span> <span class="n">Slider</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span>
<span class="k">class</span> <span class="nc">DrawingWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">DrawingWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="p">,</span>
<span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rectangle</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_rectangle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="k">class</span> <span class="nc">DrawingApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">DrawingWidget</span><span class="p">()</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">DrawingApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
</div>
Kivy tutorial 004: Making the GUI do stuff, binding to events2019-12-18T22:33:00+01:002019-12-18T22:33:00+01:00Alexander Taylortag:inclem.net,2019-12-18:/2019/12/18/kivy/kivy_tutorial_004_event_bindings/<p>This is number 4 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Events and Kivy properties</p>
<p>We left the last tutorial with a calculator app <span class="caps">GUI</span> with some nice
automatic behaviour, but which doesn’t actually do anything. Let’s
change that; it’s time to learn about <em>binding …</em></p><p>This is number 4 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Events and Kivy properties</p>
<p>We left the last tutorial with a calculator app <span class="caps">GUI</span> with some nice
automatic behaviour, but which doesn’t actually do anything. Let’s
change that; it’s time to learn about <em>binding events</em>.</p>
<p>To refresh, the basic calculator <span class="caps">GUI</span> code was as follows. If you
modified it to experiment, feel free to continue with your modified
code, and try to change the instructions to fit your modifications:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.gridlayout</span> <span class="kn">import</span> <span class="n">GridLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'vertical'</span><span class="p">)</span>
<span class="n">output_label</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">size_hint_y</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">button_symbols</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'1'</span><span class="p">,</span> <span class="s1">'2'</span><span class="p">,</span> <span class="s1">'3'</span><span class="p">,</span> <span class="s1">'+'</span><span class="p">,</span>
<span class="s1">'4'</span><span class="p">,</span> <span class="s1">'5'</span><span class="p">,</span> <span class="s1">'6'</span><span class="p">,</span> <span class="s1">'-'</span><span class="p">,</span>
<span class="s1">'7'</span><span class="p">,</span> <span class="s1">'8'</span><span class="p">,</span> <span class="s1">'9'</span><span class="p">,</span> <span class="s1">'.'</span><span class="p">,</span>
<span class="s1">'0'</span><span class="p">,</span> <span class="s1">'*'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">,</span> <span class="s1">'='</span><span class="p">)</span>
<span class="n">button_grid</span> <span class="o">=</span> <span class="n">GridLayout</span><span class="p">(</span><span class="n">cols</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">size_hint_y</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">for</span> <span class="n">symbol</span> <span class="ow">in</span> <span class="n">button_symbols</span><span class="p">:</span>
<span class="n">button_grid</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="n">symbol</span><span class="p">))</span>
<span class="n">clear_button</span> <span class="o">=</span> <span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'clear'</span><span class="p">,</span> <span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
<span class="n">height</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">output_label</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">button_grid</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">clear_button</span><span class="p">)</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p><strong>Note:</strong> This tutorial introduces some major new Kivy concepts. I
recommend working through it even if you don’t entirely follow what’s
going on, then going back to modify components and see how things change.</p>
<p>The plan now is that every one of these buttons should add their
symbol to the Label at the top, except <tt class="docutils literal">=</tt> which should evaluate the
code and display the result. This is obviously an extremely basic
calculator, but the point here is to showcase some Kivy basics - if
you’d like to improve the interface, go ahead!</p>
<p>To make the buttons do something, we must <em>bind</em> to their events. This
is a generic Kivy concept; whenever you want one thing to trigger
another, you look for an event to bind to. Some widgets such as Button
have events indicating they have been clicked on, and every Kivy
property (such as all those used to customise Widgets so far) has an
associated event when it changes.</p>
<p>Let’s start with a simple binding example:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">print_button_text</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="n">instance</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
<span class="k">for</span> <span class="n">button</span> <span class="ow">in</span> <span class="n">button_grid</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">1</span><span class="p">:]:</span>
<span class="n">button</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_press</span><span class="o">=</span><span class="n">print_button_text</span><span class="p">)</span>
<span class="c1"># we could also have used `button.bind(on_press=lambda instance: print(instance.text))`</span>
</pre></div>
<p>If you run the code now, and click on any of the buttons, you should
see its text printed in the console (but not in the Kivy <span class="caps">GUI</span>).</p>
<p>The key concept here is the <tt class="docutils literal">bind</tt> method, which you can use
with any Widget, as well as several other Kivy objects (discussed in
later tutorials). This takes any number of keyword arguments, each
specifying an <em>event name</em> and a <em>function to call</em>; in this case the
event name is <tt class="docutils literal">on_press</tt>, and the function to be called is our
new <tt class="docutils literal">print_button_text</tt>. The <tt class="docutils literal">bind</tt> method makes sure that
whenever <tt class="docutils literal">on_press</tt> occurs, the function is called. It
automatically receives a single argument, the <tt class="docutils literal">bind</tt>-ed widget instance.</p>
<p>Also note that we’ve iterated over
<tt class="docutils literal"><span class="pre">button_grid.children[1:]`.</span> The ``children`</tt> property is
available on any Widget, and holds a list of all the widgets added to
it, in reverse order. In this case, we use <tt class="docutils literal">[1:]</tt> to skip the
first element, ‘=’, as we want to use this to evaluate the result.</p>
<p><strong>Note:</strong> Button also has an <tt class="docutils literal">on_release</tt> event that is called when
the user releases a click or touch. You can find more information in
the <a class="reference external" href="https://kivy.org/docs/api-kivy.uix.button.html">Button documentation</a>.</p>
<p>This binding idea is very normal in Kivy, and you’ll quickly get used
to seeing it used in different ways, including some introduced later
in these tutorials. The kv markup language, also introduced later,
has special syntax designed to make it even simpler and clearer.</p>
<p>Anyway, all this does so far is print some text when the event occurs,
but we want to update the <span class="caps">GUI</span>. Let’s change the bound function to
achieve that:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">print_button_text</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">text</span> <span class="o">+=</span> <span class="n">instance</span><span class="o">.</span><span class="n">text</span>
</pre></div>
<p>Run the code again. Now when you press the buttons, you should see the
text appear at the top of the screen, as in the screenshot below:</p>
<div class="figure align-center">
<img alt="Calculator text after number input" src="https://inclem.net/media/kivy_text_tutorials/04_example_text.png" style="width: 400px;" />
</div>
<p>At this point, a new problem presents itself; the font size of the
label is kind of small. We can use another event to have it update
automatically in response to the label’s height:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">resize_label_text</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="n">new_height</span><span class="p">):</span>
<span class="n">label</span><span class="o">.</span><span class="n">font_size</span> <span class="o">=</span> <span class="mf">0.5</span><span class="o">*</span><span class="n">label</span><span class="o">.</span><span class="n">height</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">height</span><span class="o">=</span><span class="n">resize_label_text</span><span class="p">)</span>
</pre></div>
<p>Note that the event here is named <tt class="docutils literal">height</tt>. This works because the
Label has a Kivy property named height (as do all Widgets, see the
<a class="reference external" href="https://kivy.org/docs/api-kivy.uix.widget.html#kivy.uix.widget.Widget.height">documentation</a>),
and all Kivy properties can be bound to as an event of the same name,
called automatically when the property changes. In this case, you can
now resize the window, which causes the layouts in the Widget tree to
automatically resize their children, which in turn causes
<tt class="docutils literal">resize_label_text</tt> to automatically be called.</p>
<p>We’ll use one final binding to make the calculator interface actually
work; when the ‘=’ button is pressed, we can evaluate the entire label
text as python code, and display the result.</p>
<p><strong>Note:</strong> Using <tt class="docutils literal">eval</tt> as a calculator like this is in general a
terrible idea, used here only to avoid dwelling on the non-Kivy details.</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">evaluate_result</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="nb">eval</span><span class="p">(</span><span class="n">output_label</span><span class="o">.</span><span class="n">text</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">SyntaxError</span><span class="p">:</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s1">'Python syntax error!'</span>
<span class="n">button_grid</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_press</span><span class="o">=</span><span class="n">evaluate_result</span><span class="p">)</span>
<span class="c1"># Remember, button_grid.children[0] is the '=' button</span>
</pre></div>
<p>Further, we can make the ‘clear’ button clear the label, so that you
can start a new calculation:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">clear_label</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">clear_button</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_press</span><span class="o">=</span><span class="n">clear_label</span><span class="p">)</span>
</pre></div>
<p>With this all in place, run the app again and…the calculator works!
Every button now does something, either adding its symbol to the
output label, evaluating the label’s text as python code, or clearing
the result. You should be seeing something like the image below:</p>
<div class="figure align-center">
<img alt="Output for calculator app with number input" src="https://inclem.net/media/kivy_text_tutorials/04_output.png" style="width: 400px;" />
</div>
<p>These core event binding concepts are central to working with Kivy
widgets, and come up in many different ways. Don’t worry if you don’t
remember all the details straight away, such as the way all properties
have events you can bind to, or the specific syntax; you can look all
this up in the documentation as linked throughout and indexed on the
<a class="reference external" href="https://kivy.org/docs/api-kivy.html">Kivy website</a>. Later
tutorials also follow on to help cement this knowledge.</p>
<p><strong>Next tutorial:</strong> <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_005_a_drawing_app/">A drawing app</a></p>
<div class="section" id="full-code">
<h2>Full code</h2>
<p>your_filename.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.gridlayout</span> <span class="kn">import</span> <span class="n">GridLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'vertical'</span><span class="p">)</span>
<span class="n">output_label</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">size_hint_y</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">button_symbols</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'1'</span><span class="p">,</span> <span class="s1">'2'</span><span class="p">,</span> <span class="s1">'3'</span><span class="p">,</span> <span class="s1">'+'</span><span class="p">,</span>
<span class="s1">'4'</span><span class="p">,</span> <span class="s1">'5'</span><span class="p">,</span> <span class="s1">'6'</span><span class="p">,</span> <span class="s1">'-'</span><span class="p">,</span>
<span class="s1">'7'</span><span class="p">,</span> <span class="s1">'8'</span><span class="p">,</span> <span class="s1">'9'</span><span class="p">,</span> <span class="s1">'.'</span><span class="p">,</span>
<span class="s1">'0'</span><span class="p">,</span> <span class="s1">'*'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">,</span> <span class="s1">'='</span><span class="p">)</span>
<span class="n">button_grid</span> <span class="o">=</span> <span class="n">GridLayout</span><span class="p">(</span><span class="n">cols</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">size_hint_y</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">for</span> <span class="n">symbol</span> <span class="ow">in</span> <span class="n">button_symbols</span><span class="p">:</span>
<span class="n">button_grid</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="n">symbol</span><span class="p">))</span>
<span class="n">clear_button</span> <span class="o">=</span> <span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'clear'</span><span class="p">,</span> <span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
<span class="n">height</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">print_button_text</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">text</span> <span class="o">+=</span> <span class="n">instance</span><span class="o">.</span><span class="n">text</span>
<span class="k">for</span> <span class="n">button</span> <span class="ow">in</span> <span class="n">button_grid</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">1</span><span class="p">:]:</span> <span class="c1"># note use of the</span>
<span class="c1"># `children` property</span>
<span class="n">button</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_press</span><span class="o">=</span><span class="n">print_button_text</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">resize_label_text</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="n">new_height</span><span class="p">):</span>
<span class="n">label</span><span class="o">.</span><span class="n">font_size</span> <span class="o">=</span> <span class="mf">0.5</span><span class="o">*</span><span class="n">label</span><span class="o">.</span><span class="n">height</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">height</span><span class="o">=</span><span class="n">resize_label_text</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">evaluate_result</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="nb">eval</span><span class="p">(</span><span class="n">output_label</span><span class="o">.</span><span class="n">text</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">SyntaxError</span><span class="p">:</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s1">'Python syntax error!'</span>
<span class="n">button_grid</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_press</span><span class="o">=</span><span class="n">evaluate_result</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">clear_label</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">clear_button</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_press</span><span class="o">=</span><span class="n">clear_label</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">output_label</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">button_grid</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">clear_button</span><span class="p">)</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
</div>
Kivy tutorial 003: Building a full GUI2019-12-18T22:32:00+01:002019-12-18T22:32:00+01:00Alexander Taylortag:inclem.net,2019-12-18:/2019/12/18/kivy/kivy_tutorial_003_building_a_full_gui/<p>This is number 3 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Adding Widgets to one another</p>
<p>The tutorals so far have covered the very basics of a Kivy
application; getting everything running, adding a Widget (the Label),
and doing some customisation.</p>
<p>Let’s now <em>combine</em> some widgets to …</p><p>This is number 3 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Adding Widgets to one another</p>
<p>The tutorals so far have covered the very basics of a Kivy
application; getting everything running, adding a Widget (the Label),
and doing some customisation.</p>
<p>Let’s now <em>combine</em> some widgets to make a larger <span class="caps">GUI</span>. This tutorial
will solely cover joining the widgets together, not making them do
anything; this is covered in later tutorials.</p>
<p><strong>Note:</strong> This tutorial will construct the <span class="caps">GUI</span> using entirely Python
code. You can always do this with Python as described here, but
normally we recommend using the easier, clearer and more concise <a class="reference external" href="https://kivy.org/docs/guide/lang.html">kv
language</a> to construct
widget trees. This will be covered fully in later tutorials (see <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_007_introducing_kv_language/">007:
Introducing Kivy language</a>).</p>
<p>Our new task will be to build a simple calculator app; we’ll need
Buttons for each of the numbers and mathematical operations, and a
Label to display the result.</p>
<p>Let’s start with a new basic app structure:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>Right now, you can run the code but the window will be empty because
we didn’t add any widgets. Let’s do that now, but we no longer want
just a Label; our app will be made of multiple Widgets next to one
another. For this, we use Layout classes; let’s start with the following:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">layout</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'vertical'</span><span class="p">)</span>
<span class="n">b1</span> <span class="o">=</span> <span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'button 1'</span><span class="p">)</span>
<span class="n">b2</span> <span class="o">=</span> <span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'button 2'</span><span class="p">)</span>
<span class="n">layout</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">b1</span><span class="p">)</span>
<span class="n">layout</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">b2</span><span class="p">)</span>
<span class="k">return</span> <span class="n">layout</span>
<span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>We’re now instantiating three Widget classes; the BoxLayout and two
Buttons. Just like with the Label, each one can be customised by
passing properties. The only new one here is the <tt class="docutils literal">orientation</tt>
of the BoxLayout; passing <tt class="docutils literal">'vertical'</tt> means it will place its
children below one another.</p>
<p><strong>Note:</strong> Kivy’s design emphasises the composition of small components
to achieve combined results. For instance, the Button widget is
implemented as a Label with a background image and special
touch-related behaviour (you can see this in the <a class="reference external" href="https://kivy.org/docs/api-kivy.uix.button.html#kivy.uix.button.Button">Button documentation</a>,
check the ‘Bases:’), so we can use the Label’s text property just like
before. This isn’t too important right now, but knowing it can help
with navigating the documentation.</p>
<p>After instantiating the widgets, we can <em>add</em> them to one another. You
can almost always add any widget instance to any other in exactly this
way. When you do so, the newly added widgets will appear on the
screen, and you’ll be able to interact with them. The widget you add
to is called the <em>parent widget</em>, and the added widget (in this case
the Buttons) is the <em>child widget</em>.</p>
<p>This code should give you something like the following image. You can
also now click the buttons to see their colour change; this behaviour
is automatic, pressing them doesn’t do anything else yet.</p>
<div class="figure align-center">
<img alt="App output showing 2 buttons" src="https://inclem.net/media/kivy_text_tutorials/03_two_buttons.png" style="width: 400px;" />
</div>
<p>Try setting the BoxLayout orientation to <tt class="docutils literal">'horizontal'</tt> to see
how it affects the result.</p>
<p>Resize the window, and note that the sizes and positions of the
buttons update automatically. This happens because the BoxLayout
repositions and resizes its children when its own size changes, and
because it is the root widget its own size tracks that of the
window. This is <strong>very important</strong>! If you replace the BoxLayout with
a plain Widget (<tt class="docutils literal">from kivy.uix.widget import Widget</tt>) this will
<em>not</em> happen, there won’t be any code to place the Buttons so they
will both have their default position and size in the bottom left of
the window. For this reason, you’ll want to use Layouts like BoxLayout
all the time to automatically position things, though you can also
create your own automatic bindings (see later tutorials on Kivy Properties).</p>
<p>With these basic ideas in hand, let’s proceed to add Widgets
representing our entire calculator interface:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.gridlayout</span> <span class="kn">import</span> <span class="n">GridLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'vertical'</span><span class="p">)</span>
<span class="n">output_label</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">size_hint_y</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">button_symbols</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'1'</span><span class="p">,</span> <span class="s1">'2'</span><span class="p">,</span> <span class="s1">'3'</span><span class="p">,</span> <span class="s1">'+'</span><span class="p">,</span>
<span class="s1">'4'</span><span class="p">,</span> <span class="s1">'5'</span><span class="p">,</span> <span class="s1">'6'</span><span class="p">,</span> <span class="s1">'-'</span><span class="p">,</span>
<span class="s1">'7'</span><span class="p">,</span> <span class="s1">'8'</span><span class="p">,</span> <span class="s1">'9'</span><span class="p">,</span> <span class="s1">'.'</span><span class="p">,</span>
<span class="s1">'0'</span><span class="p">,</span> <span class="s1">'*'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">,</span> <span class="s1">'='</span><span class="p">)</span>
<span class="n">button_grid</span> <span class="o">=</span> <span class="n">GridLayout</span><span class="p">(</span><span class="n">cols</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">size_hint_y</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">for</span> <span class="n">symbol</span> <span class="ow">in</span> <span class="n">button_symbols</span><span class="p">:</span>
<span class="n">button_grid</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="n">symbol</span><span class="p">))</span>
<span class="n">clear_button</span> <span class="o">=</span> <span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'clear'</span><span class="p">,</span>
<span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
<span class="n">height</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">output_label</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">button_grid</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">clear_button</span><span class="p">)</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>This introduces a couple of new ideas; the GridLayout is a new layout
class that arranges its child widgets in (you guessed it) a
grid. We’ve set its <tt class="docutils literal">cols</tt> property to <tt class="docutils literal">4</tt>, which means that after
every 4 widgets we add it will start a new row. Since we add 16
buttons altogether, that’s 4 rows of 4. Try adding an extra button or
two to understand exactly how it’s working.</p>
<p>The other new idea here is the <tt class="docutils literal">size_hint_y</tt> setting for the
output_label and button_grid. All widgets have a <tt class="docutils literal">size_hint_x</tt>
(horizontal) and <tt class="docutils literal">size_hint_y</tt> (vertical) that you can set. They
are used by Layout classes to set relative sizes; in this case, the
the one with <tt class="docutils literal">size_hint_y=2</tt> takes up twice as much vertical
space as the one with <tt class="docutils literal">size_hint_y=1</tt>.</p>
<p>You can also override the size hint to set a manual width and/or
height for your Widget, but you must do this explicitly, as shown here
with the ‘clear’ button. By setting <tt class="docutils literal">size_hint_y=None</tt>, we
ensure that its <tt class="docutils literal">height=100</tt> is never overridden, this Button
will have a height of 100 pixels no matter what.</p>
<p>Your final code should look something like the image below. You can
resize the window to see all the components move around and resize
automatically, thanks to the use of Layouts for positioning.</p>
<div class="figure align-center">
<img alt="Calculator gui image" src="https://inclem.net/media/kivy_text_tutorials/03_output.png" style="width: 400px;" />
</div>
<p>You are <em>strongly encouraged</em> to experiment with modifying this code
to see what happens. All the concepts used here are standard when
working with Kivy widget positioning.</p>
<p>The calculator <span class="caps">GUI</span> clearly doesn’t do anything yet (although you can
click on the buttons due to their default behaviour). Adding some
functionality is covered in the next tutorial.</p>
<p><strong>Next tutorial:</strong> <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_004_event_bindings/">Making the <span class="caps">GUI</span> do stuff, binding to events</a></p>
<div class="section" id="full-code">
<h2>Full code</h2>
<p>your_filename.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.gridlayout</span> <span class="kn">import</span> <span class="n">GridLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'vertical'</span><span class="p">)</span>
<span class="n">output_label</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">size_hint_y</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">button_symbols</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'1'</span><span class="p">,</span> <span class="s1">'2'</span><span class="p">,</span> <span class="s1">'3'</span><span class="p">,</span> <span class="s1">'+'</span><span class="p">,</span>
<span class="s1">'4'</span><span class="p">,</span> <span class="s1">'5'</span><span class="p">,</span> <span class="s1">'6'</span><span class="p">,</span> <span class="s1">'-'</span><span class="p">,</span>
<span class="s1">'7'</span><span class="p">,</span> <span class="s1">'8'</span><span class="p">,</span> <span class="s1">'9'</span><span class="p">,</span> <span class="s1">'.'</span><span class="p">,</span>
<span class="s1">'0'</span><span class="p">,</span> <span class="s1">'*'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">,</span> <span class="s1">'='</span><span class="p">)</span>
<span class="n">button_grid</span> <span class="o">=</span> <span class="n">GridLayout</span><span class="p">(</span><span class="n">cols</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">size_hint_y</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">for</span> <span class="n">symbol</span> <span class="ow">in</span> <span class="n">button_symbols</span><span class="p">:</span>
<span class="n">button_grid</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="n">symbol</span><span class="p">))</span>
<span class="n">clear_button</span> <span class="o">=</span> <span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'clear'</span><span class="p">,</span> <span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
<span class="n">height</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">output_label</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">button_grid</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">clear_button</span><span class="p">)</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
</div>
Kivy tutorial 002: Improving appearance, customising widgets using Kivy Properties2019-12-18T22:31:00+01:002019-12-18T22:31:00+01:00Alexander Taylortag:inclem.net,2019-12-18:/2019/12/18/kivy/kivy_tutorial_002_improving_appearance/<p>This is number 2 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Modifying Widget appearance, Kivy properties</p>
<p>It’s great to say Hello World, but it looks pretty boring, and you’d
expect that you’d be able to customise the appearance of
text. Fortunately, you’d be right …</p><p>This is number 2 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Modifying Widget appearance, Kivy properties</p>
<p>It’s great to say Hello World, but it looks pretty boring, and you’d
expect that you’d be able to customise the appearance of
text. Fortunately, you’d be right…so let’s do it.</p>
<p>We’ll continue modifying the code from last time, which was:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'Hello world!'</span><span class="p">)</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>The basic way to modify things in Kivy is to change <em>Kivy properties</em>
of the widgets. As far as we’re concerned right now, we can set these
by passing arguments at instantiation, or by treating them as
attributes of the class. For instance, we could also have set the text
as follows:</p>
<div class="highlight"><pre><span></span><span class="n">root_widget</span> <span class="o">=</span> <span class="n">Label</span><span class="p">()</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s1">'Hello world!'</span>
</pre></div>
<p>Let’s set ourselves three goals:</p>
<ul class="simple">
<li>Make the text larger</li>
<li>Italicise the text</li>
<li>Colour “Hello” and “world!” differently</li>
</ul>
<p>To customise the Label appearance, we must check the documentation to
find an appropriate Kivy property. For the text size, check the <a class="reference external" href="https://kivy.org/docs/api-kivy.uix.label.html">Label
doc</a> and find the
<tt class="docutils literal">font_size</tt> listing. It looks something like the following:</p>
<div class="figure align-center">
<img alt="Font size doc from Kivy website" src="https://inclem.net/media/kivy_text_tutorials/02_font_size.png" />
</div>
<p>Following the documentation, this lets us set the font size in pixels,
and it defaults to <tt class="docutils literal">'15sp'</tt>. This is a special Kivy syntax, the sp
units automatically scale the font size according to the <span class="caps">DPI</span> of the
display and the user’s font size setting (on some platforms); on
desktop on a non-hidpi display, it is just 15 pixels. For now let’s
just set a simple pixel number:</p>
<div class="highlight"><pre><span></span><span class="n">root_widget</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span>
<span class="n">text</span><span class="o">=</span><span class="s1">'Hello world!'</span><span class="p">,</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
</pre></div>
<p>You can run the code now to see the result.</p>
<p>To make the text italic, the procedure is the same. Check the <a class="reference external" href="https://kivy.org/docs/api-kivy.uix.label.html">Label doc</a> and find the
<tt class="docutils literal">italic</tt> property entry. you’ll see that this is a
BooleanProperty that defaults to False; just set it to True to enable
the underline:</p>
<div class="highlight"><pre><span></span><span class="n">root_widget</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span>
<span class="n">text</span><span class="o">=</span><span class="s1">'Hello world!'</span><span class="p">,</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
<span class="n">italic</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></div>
<p>Finally, we want to colour <tt class="docutils literal">Hello</tt> and <tt class="docutils literal">world!</tt>
differently. Things are a little different here as we can’t use a
single property setting to modify the whole string, since the two
words should be treated differently.</p>
<p>Instead we enable the <a class="reference external" href="https://kivy.org/docs/api-kivy.uix.label.html#kivy.uix.label.Label.markup">markup property</a>:</p>
<div class="highlight"><pre><span></span><span class="n">root_widget</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span>
<span class="n">text</span><span class="o">=</span><span class="s1">'Hello world!'</span><span class="p">,</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
<span class="n">underline</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
<span class="n">markup</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></div>
<p>You can now use Kivy’s <a class="reference external" href="https://kivy.org/docs/api-kivy.uix.label.html#markup-text">markup syntax</a> to
modify the text within the Label. Try the following:</p>
<div class="highlight"><pre><span></span><span class="n">root_widget</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
<span class="n">italic</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
<span class="n">markup</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s1">'[color=#ff0000]Hello[/color] [color=#00ff00]world![/color]'</span>
</pre></div>
<p>Now run the application again, <tt class="docutils literal">python your_filename.py</tt>. The
result should now look something like the following image.</p>
<div class="figure align-center">
<img alt="Output for example app." src="https://inclem.net/media/kivy_text_tutorials/02_output.png" style="width: 400px;" />
</div>
<p><strong>Note:</strong> This is just a basic introduction to customising Kivy
widgets, you can use similar methods to manipulate different widgets
in a huge range of different ways. Kivy properties also have other
important functionality, covered later in these tutorials.</p>
<p><strong>Next tutorial:</strong> <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_003_building_a_full_gui/">Building a full <span class="caps">GUI</span></a></p>
<div class="section" id="full-code">
<h2>Full code</h2>
<p>The full code for this exercise was:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
<span class="n">italic</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
<span class="n">markup</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">root_widget</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s1">'[color=#ff0000]Hello[/color] [color=#00ff00]world![/color]'</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
</div>
Kivy tutorial 001: Say Hello2019-12-18T22:30:00+01:002019-12-18T22:30:00+01:00Alexander Taylortag:inclem.net,2019-12-18:/2019/12/18/kivy/kivy_tutorial_001_say_hello/<p>This is number 1 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Starting an App, getting Kivy running</p>
<p>It’s essentially compulsory that the introduction to any programming
project should be a “Hello World!” application. Since Kivy is a <span class="caps">GUI</span>
framework, that means opening a window and displaying the …</p><p>This is number 1 in a <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">series of introductory Kivy tutorials</a>.</p>
<p><strong>Central themes:</strong> Starting an App, getting Kivy running</p>
<p>It’s essentially compulsory that the introduction to any programming
project should be a “Hello World!” application. Since Kivy is a <span class="caps">GUI</span>
framework, that means opening a window and displaying the words
on the screen. Start by adding each of the following lines to your program:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
</pre></div>
<p>This imports the <tt class="docutils literal">App</tt> class, which you’ll use as the core object of
any Kivy application. Your instance of this class will create the Kivy
window and serve as the top level of your application</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
</pre></div>
<p>This next import introduces one of Kivy’s most important components;
the Widget. Your entire application will be built with Widgets, each
of which does a single (relatively) small task. For instance, Label is
a Widget that displays some text, Button is (obviously) a button, and
Layouts are Widgets that contain other Widgets and control their
positions according to certain rules.</p>
<p>You can find the documentation for Label <a class="reference external" href="https://kivy.org/docs/api-kivy.uix.label.html">here</a>. We’ll need this later!.</p>
<p>In every Kivy application, your first task is to create an App
subclass as follows:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'Hello world!'</span><span class="p">)</span>
<span class="k">return</span> <span class="n">root_widget</span>
</pre></div>
<p>The <tt class="docutils literal">build</tt> method is the only important addition you have to make,
and is your application’s entry point. This method must instantiate
and return what will be your <strong>root widget</strong>, the top level widget of
your Kivy application, which will in turn contain all your other gui objects.</p>
<p>The root widget will automatically fill the window, so in this case
the Label text will appear right in the middle.</p>
<p>In our case, the application only needs a single Widget; the
Label displaying our text. We set the text by simply passing it as an
argument. This works automatically because <tt class="docutils literal">text</tt> is a <em>Kivy
property</em> of the Label widget…more on that later.</p>
<p>Finally, add a line of code to start the app:</p>
<div class="highlight"><pre><span></span><span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>This <em>instantiates</em> and <em>runs</em> the instance of your App. Any Kivy
application is created and started with some variation of these
basic steps.</p>
<p>Now…run the code!</p>
<div class="highlight"><pre><span></span><span class="n">python</span> <span class="n">your_filename</span><span class="o">.</span><span class="n">py</span>
</pre></div>
<p>You should see a Window something like the following
image. Congratulations, you’ve written and run your first Kivy application.</p>
<div class="figure align-center">
<img alt="Hello world application" src="https://inclem.net/media/kivy_text_tutorials/01_01.png" style="width: 250px;" />
</div>
<p><strong>Next tutorial:</strong> <a class="reference external" href="https://inclem.net/2019/12/18/kivy/kivy_tutorial_002_improving_appearance/">Improving appearances, customising widgets using Kivy Properties</a></p>
<div class="section" id="full-code">
<h2>Full code</h2>
<p>your_filename.py:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="k">class</span> <span class="nc">YourApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">root_widget</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'Hello world!'</span><span class="p">)</span>
<span class="k">return</span> <span class="n">root_widget</span>
<span class="n">YourApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
</div>
python-for-android 2019.08.09 released: running under Python 2 no longer supported2019-08-20T20:30:00+02:002019-08-20T20:30:00+02:00Alexander Taylortag:inclem.net,2019-08-20:/2019/08/20/kivy/p4a_2019_08_09_released/<p><a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a> is
a packaging tool for Python apps on Android. You can create your own
Python distribution including the modules and dependencies you want,
and bundle it in an <span class="caps">APK</span> along with your own code.</p>
<hr class="docutils" />
<p>python-for-android 2019.08.09 has just been released! I haven’t been
making a blog …</p><p><a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a> is
a packaging tool for Python apps on Android. You can create your own
Python distribution including the modules and dependencies you want,
and bundle it in an <span class="caps">APK</span> along with your own code.</p>
<hr class="docutils" />
<p>python-for-android 2019.08.09 has just been released! I haven’t been
making a blog post for every new version now that we’ve switched to a
monthly releases, but this one includes an especially major change:
python-for-android no longer runs under Python 2. If this affects you,
simply run python-for-android (or buildozer) under Python 3.6 or
newer, they should run exactly the same as ever.</p>
<p>Note that you <em>can still target Python 2</em> on Android, simply add
python2 to your requirements as you would normally. However, this
won’t last forever, support for Python 2 will probably be totally
removed early in 2020.</p>
<p>Other changes in this release include further testing improvements,
significantly improved documentation on developing with
python-for-android, and some preparations for further big changes in
the future. See the <a class="reference external" href="https://github.com/kivy/python-for-android/releases/tag/v2019.08.09">release note</a>
for full details.</p>
A delayed resize layout in Kivy2019-07-13T13:30:00+02:002019-07-13T13:30:00+02:00Alexander Taylortag:inclem.net,2019-07-13:/2019/07/13/kivy/delayed_resize_layout/<p>A user on the <a class="reference external" href="https://discordapp.com/invite/eT3cuQp">Kivy Discord</a>
just raised the question of how to delay widget updates during resize
events. The problem was that the widgets did some heavy processing
(generating matplotlib graphs) that would be very slow if called for
every tiny update during a larger resize event.</p>
<p>This is …</p><p>A user on the <a class="reference external" href="https://discordapp.com/invite/eT3cuQp">Kivy Discord</a>
just raised the question of how to delay widget updates during resize
events. The problem was that the widgets did some heavy processing
(generating matplotlib graphs) that would be very slow if called for
every tiny update during a larger resize event.</p>
<p>This is a good opportunity to return to the flexibility of Kivy
layouts. It’s very easy to add some simple behaviour that delays
updates until a short period has passed without the size
changing. Here’s a quick implementation I threw together:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.anchorlayout</span> <span class="kn">import</span> <span class="n">AnchorLayout</span>
<span class="kn">from</span> <span class="nn">kivy.clock</span> <span class="kn">import</span> <span class="n">Clock</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ObjectProperty</span><span class="p">,</span> <span class="n">NumericProperty</span>
<span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">partial</span>
<span class="k">class</span> <span class="nc">DelayedResizeLayout</span><span class="p">(</span><span class="n">AnchorLayout</span><span class="p">):</span>
<span class="n">do_layout_event</span> <span class="o">=</span> <span class="n">ObjectProperty</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">allownone</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">layout_delay_s</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mf">0.2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">do_layout</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">do_layout_event</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">do_layout_event</span><span class="o">.</span><span class="n">cancel</span><span class="p">()</span>
<span class="n">real_do_layout</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">do_layout</span>
<span class="bp">self</span><span class="o">.</span><span class="n">do_layout_event</span> <span class="o">=</span> <span class="n">Clock</span><span class="o">.</span><span class="n">schedule_once</span><span class="p">(</span>
<span class="k">lambda</span> <span class="n">dt</span><span class="p">:</span> <span class="n">real_do_layout</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">),</span>
<span class="bp">self</span><span class="o">.</span><span class="n">layout_delay_s</span><span class="p">)</span>
</pre></div>
<p>This layout could be used as the root widget of a whole application,
to delay the resizing of all the content, or somewhere within the app
to delay only a small part of it.</p>
<p>And a simple example:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="kn">from</span> <span class="nn">kivy.base</span> <span class="kn">import</span> <span class="n">runTouchApp</span>
<span class="n">button</span> <span class="o">=</span> <span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'example button'</span><span class="p">)</span>
<span class="n">layout</span> <span class="o">=</span> <span class="n">DelayedResizeLayout</span><span class="p">()</span>
<span class="n">layout</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">button</span><span class="p">)</span>
<span class="n">runTouchApp</span><span class="p">(</span><span class="n">layout</span><span class="p">)</span>
</pre></div>
Widget interactions between Python and Kv2019-06-20T22:00:00+02:002019-06-20T22:00:00+02:00Alexander Taylortag:inclem.net,2019-06-20:/2019/06/20/kivy/widget_interactions_between_python_and_kv/<p>One of the biggest Kivy confusions I see is how different widgets can
access one another when they’re in different parts of the
application. Fortunately, it’s generally straightforward to do so. This
post gives examples of methods you might use in different situations.</p>
<p>The emphasis here is on …</p><p>One of the biggest Kivy confusions I see is how different widgets can
access one another when they’re in different parts of the
application. Fortunately, it’s generally straightforward to do so. This
post gives examples of methods you might use in different situations.</p>
<p>The emphasis here is on what you <em>can</em> do, not what you <em>should</em>. If
you aren’t sure which way is best in a given situation, go ahead and
choose what seems best at the time, but don’t be afraid to revisit it later.</p>
<div class="section" id="how-can-a-widget-access-its-parent">
<h2>How can a widget access its parent?</h2>
<p>Use the <tt class="docutils literal">parent</tt> property of the widget:</p>
<div class="highlight"><pre><span></span><span class="n">child</span> <span class="o">=</span> <span class="n">Widget</span><span class="p">()</span>
<span class="n">parent</span> <span class="o">=</span> <span class="n">Widget</span><span class="p">()</span>
<span class="k">assert</span> <span class="n">child</span><span class="o">.</span><span class="n">parent</span> <span class="ow">is</span> <span class="bp">None</span> <span class="c1"># child widget isn't added to any parent</span>
<span class="n">parent</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">child</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">child</span><span class="o">.</span><span class="n">parent</span> <span class="o">==</span> <span class="n">parent</span>
</pre></div>
</div>
<div class="section" id="how-can-a-widget-access-its-children">
<h2>How can a widget access its children?</h2>
<p>Use the <tt class="docutils literal">children</tt> property. This is a list containing all the children you added.</p>
<div class="highlight"><pre><span></span><span class="n">child</span> <span class="o">=</span> <span class="n">Widget</span><span class="p">()</span>
<span class="n">parent</span> <span class="o">=</span> <span class="n">Widget</span><span class="p">()</span>
<span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">parent</span><span class="o">.</span><span class="n">children</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="c1"># no children added to parent</span>
<span class="n">parent</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">child</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">parent</span><span class="o">.</span><span class="n">children</span><span class="p">)</span> <span class="c1"># [<kivy.uix.widget.Widget object at 0x???>]</span>
<span class="k">assert</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">parent</span><span class="o">.</span><span class="n">children</span>
</pre></div>
<p><strong>Note:</strong> The <tt class="docutils literal">children</tt> list is actually backwards, i.e. the last
widget you add will by default be the first in the
list. This is a bit surprising, and only really happens for
backwards compatibility.</p>
</div>
<div class="section" id="how-can-a-widget-in-python-access-children-from-its-kv-rule">
<h2>How can a widget in Python access children from its kv rule?</h2>
<div class="section" id="option-1-ids">
<h3>Option 1: ids</h3>
<p>You can give the widgets in your kv rule ids to access them from Python.</p>
<div class="highlight"><pre><span></span><span class="c1"># main.py</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="k">class</span> <span class="nc">KvRuleWidget</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'We can get references to all the children using the ids dict.'</span><span class="p">)</span>
<span class="c1"># syntax is `self.ids.<id_text>`</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">ids</span><span class="o">.</span><span class="n">middle</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">ids</span><span class="o">.</span><span class="n">bottom</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span>
<span class="c1"># widgets can be accessed from deep in the kv rule</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">ids</span><span class="o">.</span><span class="n">top_left</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">ids</span><span class="o">.</span><span class="n">top_right</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span>
<span class="k">class</span> <span class="nc">ExampleApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">KvRuleWidget</span><span class="p">()</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># example.kv</span>
<span class="o"><</span><span class="n">KvRuleWidget</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">top_left</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'top left'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">top_right</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'top right'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">middle</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'middle'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">bottom</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'bottom'</span>
</pre></div>
<p><strong>Note:</strong> You <em>cannot</em> set up widget ids from Python code, if
you write e.g. <tt class="docutils literal">w = <span class="pre">Widget(id='some_name')</span></tt> this will not
crash but the id will not be available in any ids dictionary.</p>
</div>
<div class="section" id="option-2-properties">
<h3>Option 2: properties</h3>
<p>You can use Kivy properties to pass around references to widgets.</p>
<div class="highlight"><pre><span></span><span class="c1"># main.py</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ObjectProperty</span>
<span class="k">class</span> <span class="nc">KvRuleWidget</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="n">top_right_label</span> <span class="o">=</span> <span class="n">ObjectProperty</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'The top right label is {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">top_right_label</span><span class="p">))</span>
<span class="k">class</span> <span class="nc">ExampleApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">KvRuleWidget</span><span class="p">()</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># example.kv</span>
<span class="o"><</span><span class="n">KvRuleWidget</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">top_right_label</span><span class="p">:</span> <span class="n">top_right</span> <span class="c1"># note that we used an id to set the property</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">top_right</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'top left'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'top right'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'middle'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'bottom'</span>
</pre></div>
</div>
<div class="section" id="option-3-the-parent-and-children-properties">
<h3>Option 3: The <tt class="docutils literal">parent</tt> and <tt class="docutils literal">children</tt> properties</h3>
<p>It is possible to walk through the widget tree using the <tt class="docutils literal">parent</tt> and <tt class="docutils literal">children</tt> properties.</p>
<p>This is usually a bad idea and is prone to breakage if the structure
of the widget tree changes. However, it’s still possible.</p>
<div class="highlight"><pre><span></span><span class="c1"># main.py</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ObjectProperty</span>
<span class="k">class</span> <span class="nc">KvRuleWidget</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">on_touch_down</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="c1"># get a reference to the top right label only by walking through the widget tree</span>
<span class="n">top_right_label</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'The top right label is {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">top_right_label</span><span class="p">))</span>
<span class="k">class</span> <span class="nc">ExampleApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">KvRuleWidget</span><span class="p">()</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># example.kv</span>
<span class="c1"># note: this time there are no ids at all</span>
<span class="o"><</span><span class="n">KvRuleWidget</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'horizontal'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'top left'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'top right'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'middle'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'bottom'</span>
</pre></div>
</div>
</div>
<div class="section" id="how-can-a-widget-in-kv-access-children-defined-in-python">
<h2>How can a widget in Kv access children defined in Python?</h2>
<p>Sometimes you might have some children defined via a Kv rule, and
others created dynamically in Python. You can access the Python
widgets in kv by saving references to them in Kivy properties:</p>
<div class="highlight"><pre><span></span><span class="c1"># main.py</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ObjectProperty</span>
<span class="k">class</span> <span class="nc">KvRuleWidget</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="n">label_created_in_python</span> <span class="o">=</span> <span class="n">ObjectProperty</span><span class="p">()</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="c1"># add a widget from python code</span>
<span class="n">label</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'label created in Python'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">label</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">label_created_in_python</span> <span class="o">=</span> <span class="n">label</span> <span class="c1"># save a reference</span>
<span class="k">class</span> <span class="nc">ExampleApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">KvRuleWidget</span><span class="p">()</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># example.kv</span>
<span class="o"><</span><span class="n">KvRuleWidget</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'label created in Kv'</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'the label created in Python has text "{}"'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">root</span><span class="o">.</span><span class="n">label_created_in_python</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
</pre></div>
</div>
<div class="section" id="how-can-a-widget-defined-in-a-kv-rule-access-a-widget-defined-in-another-kv-rule">
<h2>How can a widget defined in a kv rule access a widget defined in another kv rule?</h2>
<p>Sometimes you might have two widgets in very different places that
need to talk to one another somehow. Usually the best way to achieve
this is to consider how they are related to one another, and pass
information between them via their common relations.</p>
<p>Also see the next Section for how to access any widget from anywhere,
without worrying about how the widgets are related. However, that
usually isn’t such a good choice in the long run.</p>
<p>The following example is deliberately very simple, but the same
principles can be used to link together widgets across your whole
program using references passed around where the kv rules meet.</p>
<div class="highlight"><pre><span></span><span class="c1"># main.py</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ObjectProperty</span>
<span class="k">class</span> <span class="nc">IncrementCounterButton</span><span class="p">(</span><span class="n">Button</span><span class="p">):</span>
<span class="n">counter</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">on_press</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">class</span> <span class="nc">CounterLabel</span><span class="p">(</span><span class="n">Label</span><span class="p">):</span>
<span class="n">counter</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">RootWidget</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">ExampleApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">RootWidget</span><span class="p">()</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># example.kv</span>
<span class="o"><</span><span class="n">IncrementCounterButton</span><span class="o">></span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'press me'</span>
<span class="o"><</span><span class="n">CounterLabel</span><span class="o">></span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'the counter value is {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">counter</span><span class="p">)</span> <span class="c1"># `app` in kv is equivalent to `App.get_running_app()` in Python</span>
<span class="o"><</span><span class="n">RootWidget</span><span class="o">></span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s1">'vertical'</span>
<span class="n">CounterLabel</span><span class="p">:</span>
<span class="n">counter</span><span class="p">:</span> <span class="n">button</span><span class="o">.</span><span class="n">counter</span> <span class="c1"># this means the CounterLabel's counter will always match the button's counter</span>
<span class="n">IncrementCounterButton</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">button</span>
</pre></div>
</div>
<div class="section" id="how-can-any-widget-access-any-other-widget-from-anywhere">
<h2>How can any widget access any other widget from anywhere?</h2>
<p>Sometimes you really do want widgets to interact with one another
without any good relationship between them. You can do this in a
convenient way by using a Kivy property in the App class.</p>
<p><strong>Note:</strong> This is notionally similar to using a global variable, and
is often bad practice for all the same reasons.</p>
<p>The following example is quite contrived to keep it simple. In this
case you could probably think of a better way to do the same thing,
perhaps using the methods from the previous Sections.</p>
<div class="highlight"><pre><span></span><span class="c1"># main.py</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ObjectProperty</span>
<span class="k">class</span> <span class="nc">IncrementCounterButton</span><span class="p">(</span><span class="n">Button</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">on_press</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># You can always access your App class from Python as follows:</span>
<span class="n">App</span><span class="o">.</span><span class="n">get_running_app</span><span class="p">()</span><span class="o">.</span><span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">class</span> <span class="nc">CounterLabel</span><span class="p">(</span><span class="n">Label</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">ExampleApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="n">counter</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">boxlayout</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'vertical'</span><span class="p">)</span>
<span class="n">label</span> <span class="o">=</span> <span class="n">CounterLabel</span><span class="p">()</span>
<span class="n">button</span> <span class="o">=</span> <span class="n">IncrementCounterButton</span><span class="p">()</span>
<span class="n">boxlayout</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">label</span><span class="p">)</span>
<span class="n">boxlayout</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">button</span><span class="p">)</span>
<span class="k">return</span> <span class="n">boxlayout</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># example.kv</span>
<span class="o"><</span><span class="n">IncrementCounterButton</span><span class="o">></span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'press me'</span>
<span class="o"><</span><span class="n">CounterLabel</span><span class="o">></span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s1">'the counter value is {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">counter</span><span class="p">)</span> <span class="c1"># `app` in kv is equivalent to `App.get_running_app()` in Python</span>
</pre></div>
</div>
An update on python-for-android: v2019.06.06 released and future plans2019-06-08T16:00:00+02:002019-06-08T16:00:00+02:00Alexander Taylortag:inclem.net,2019-06-08:/2019/06/08/kivy/an_update_on_python_for_android/<p><a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a> is
a packaging tool for Python apps on Android. You can create your own
Python distribution including the modules and dependencies you want,
and bundle it in an <span class="caps">APK</span> along with your own code.</p>
<hr class="docutils" />
<p>python-for-android 2019.06.06 has just been released! This release
contains 198 commits from 31 …</p><p><a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a> is
a packaging tool for Python apps on Android. You can create your own
Python distribution including the modules and dependencies you want,
and bundle it in an <span class="caps">APK</span> along with your own code.</p>
<hr class="docutils" />
<p>python-for-android 2019.06.06 has just been released! This release
contains 198 commits from 31 different contributors. Many thanks to
everyone involved.</p>
<p>Major changes in this release include:</p>
<ul class="simple">
<li>Added support for running your setup.py when packaging your app,
enabling your code to be installed as a module in the on-device
Python environment. This also makes it easy to build Cython or other
compiled components.</li>
<li>Added some tooling for requesting and checking for app permissions
at runtime.</li>
<li>Added initial support for matplotlib.</li>
<li>Updated many recipes, and especially the <span class="caps">SDL2</span> backend components,
for improved performance and stability on Android.</li>
<li>Ongoing improvements to our test coverage and infrastructure.</li>
<li>Removed a significant amount of dead code relating to
long-deprecated build configurations, and unified other parts of the
build to reduce duplication of effort between bootstraps.</li>
<li>Updated the release model to target regular, smaller releases.</li>
</ul>
<p>Of course there have also been a wide range of bugfixes and
improvements. See the <a class="reference external" href="https://github.com/kivy/python-for-android/releases/tag/v2019.06.06">release notes</a>
for a full changelog.</p>
<div class="section" id="release-model">
<h2>Release model</h2>
<p>In this release we’ve transitioned to a calendar-based version
system. Future releases will continue to be of the form
<span class="caps">YYYY</span>.<span class="caps">MM</span>.<span class="caps">DD</span>. We’re initially targeting one release every four
weeks. This scheme represents how python-for-android is normally best
used: many changes are driven by updates in the Android ecosystem and
build toolchain, and in practice it’s usually best to be working from
the most recent possible python-for-android release.</p>
<p>This has been made possible by the hard work of various contributors,
setting up and continuing to improve python-for-android’s suite of
tests and continuous integration services. In the past we haven’t done
a great job of keeping up releases alongside major improvements, but
this should now be much more straightforward.</p>
<p>If you’re using buildozer then this doesn’t directly affect you, as
buildozer was already configured to use a more recent
python-for-android version. Buildozer will now automatically
transition to use the latest release, represented by the master
branch in the python-for-android git repository.</p>
</div>
<div class="section" id="future-work">
<h2>Future work</h2>
<p>We’ve had some questions about Google’s plan to <a class="reference external" href="https://android-developers.googleblog.com/2017/12/improving-app-security-and-performance.html">require 64-bit
support for apps on Google Play</a>,
starting in August 2019. In fact python-for-android already supports
this, just pass the required architecture as an argument to the build:</p>
<div class="highlight"><pre><span></span>p4a apk --arch<span class="o">=</span>arm64-v8a
</pre></div>
<p>That said, we’re working to improve our testing and documentation
around 64-bit builds, to make sure everything works as expected.</p>
<p>We also don’t currently support multi-architecture builds in a single
output <span class="caps">APK</span>. This should be technically possible, but hasn’t ever
been a focus because it would significantly increase the <span class="caps">APK</span> size. We
may revisit this, but in the meantime you can upload one <span class="caps">APK</span> of each
type to Google Play and python-for-android will automatically handle
versioning different architectures correctly (i.e. arm64-v8a is an
‘upgrade’ for devices that support it, so that <span class="caps">APK</span> will be preferred).</p>
<p>The other current focus is on improving our test infrastructure,
especially increasing test coverage and automation. This should
further increase the ease of making regular releases, and our
confidence that everything continues to work correctly!</p>
<p>These points are just general goals for python-for-android. There is
always other maintenance work to do, and contributions of all types are
always welcome.</p>
</div>
Running buildozer in a virtual machine2019-05-19T17:00:00+02:002019-05-19T17:00:00+02:00Alexander Taylortag:inclem.net,2019-05-19:/2019/05/19/kivy/running_buildozer_in_a_virtual_machine/<p>This guide describes how to turn your Kivy/Python app into an <span class="caps">APK</span>,
by running the <a class="reference external" href="https://github.com/kivy/buildozer">buildozer build tool</a> in a virtual machine. This
is not the only way to run buildozer, it can work natively on Linux
or MacOS or be run from the Windows Subsystem for Linux. See …</p><p>This guide describes how to turn your Kivy/Python app into an <span class="caps">APK</span>,
by running the <a class="reference external" href="https://github.com/kivy/buildozer">buildozer build tool</a> in a virtual machine. This
is not the only way to run buildozer, it can work natively on Linux
or MacOS or be run from the Windows Subsystem for Linux. See the <a class="reference external" href="https://kivy.org/doc/stable/guide/packaging-android.html#packaging-android">Kivy
documentation</a>
for more general instructions.</p>
<div class="section" id="creating-a-virtual-machine">
<h2>Creating a Virtual Machine</h2>
<p>I’ll be using <a class="reference external" href="https://www.virtualbox.org/">VirtualBox</a>. Other
virtualisation software should also work, but you’ll need to adapt the
specific instructions.</p>
<p>We also need a target <span class="caps">OS</span>. I recommend Lubuntu 18.04, available <a class="reference external" href="https://lubuntu.me/downloads/">here</a> (or <a class="reference external" href="http://cdimage.ubuntu.com/lubuntu/releases/18.04/release/lubuntu-18.04.2-desktop-amd64.iso">direct download link</a>). Lubuntu
is a light weight <a class="reference external" href="https://www.ubuntu.com/">Ubuntu</a> variant. You
can also use a different distro if you like, but may need to adapt the
later instructions.</p>
<p>Once you have downloaded the Lubuntu iso file, start VirtualBox and
press <tt class="docutils literal">New</tt> to create a new virtual machine. You’ll see a dialog
like the following:</p>
<div class="figure align-center">
<img alt="New VM dialog" src="https://inclem.net/media/setup_buildozer_vm/001_new_vm_dialog.png" />
</div>
<p>Fill in the other options as shown in the image. It’s fine to set a
larger memory size if you have enough available, or less may also work
fine. Then press <tt class="docutils literal">Create</tt> to continue.</p>
<div class="figure align-center">
<img alt="Disk image creation dialog" src="https://inclem.net/media/setup_buildozer_vm/002_VM_memory.png" />
</div>
<p>You now need to select a file size for your virtual hard disk. 15 <span class="caps">GB</span>
should be sufficient, but it’s safest to double that. Leave the other
options unchanged and choose <tt class="docutils literal">Create</tt> to continue.</p>
<p>Now, select your new <span class="caps">VM</span> and click <tt class="docutils literal">Start</tt> in the main VirtualBox
<span class="caps">GUI</span>. You should be prompted to select a virtual optical disk to boot from:</p>
<div class="figure align-center">
<img alt="Choose iso to boot" src="https://inclem.net/media/setup_buildozer_vm/003_load_lubuntu.png" />
</div>
<p>Navigate to your Lubuntu 18.04 iso downloaded earlier, as shown, and
press <tt class="docutils literal">Start</tt> to continue. The first screen you see should look
something like the following:</p>
<div class="figure align-center">
<img alt="Select language" src="https://inclem.net/media/setup_buildozer_vm/004_select_language.png" />
</div>
<p>Select your language to see the boot menu:</p>
<div class="figure align-center">
<img alt="Choose boot option" src="https://inclem.net/media/setup_buildozer_vm/005_install_lubuntu.png" />
</div>
<p>Choose the second option, “Install Lubuntu”. It doesn’t matter if you
accidentally press enter to “Try Lubuntu without installing”, in this
case there should be an Install Lubuntu application on the desktop
that you can click to continue the install process.</p>
<p>You’ll be shown a series of dialogs to help prepare the install
process. Clicking through with the defaults is fine, or select other
options if you prefer.</p>
<p>The fourth screen will ask what kind of install to use, as shown:</p>
<div class="figure align-center">
<img alt="Choose install type" src="https://inclem.net/media/setup_buildozer_vm/006_normal_installation_and_download_updates.png" />
</div>
<p>The options shown above should be the defaults, and are what you want
to use, so go ahead and continue.</p>
<div class="figure align-center">
<img alt="Choose partitioning options" src="https://inclem.net/media/setup_buildozer_vm/007_erase_disk_and_install.png" />
</div>
<p>Next, select “Erase disk and install Lubuntu”. Note that this is only
erasing the (emtpy) virtual disk image created earlier, it won’t
affect your host operating system.</p>
<p>Click through again, and you’ll eventually reach the user creation
screen. It doesn’t matter what your username is, I used <tt class="docutils literal">kivyuser</tt>:</p>
<div class="figure align-center">
<img alt="User creation dialog" src="https://inclem.net/media/setup_buildozer_vm/008_create_user.png" />
</div>
<p>Click “Continue” to finally start the install. You’ll be asked a few
more questions, but eventually will just have to wait for the
installation to complete. This shouldn’t take too long. You’ll be
prompted to “Restart Now”, which you should go ahead and do.</p>
<div class="figure align-center">
<img alt="Restart Now screen" src="https://inclem.net/media/setup_buildozer_vm/009_restart_now.png" />
</div>
<p>If you have any issues with the virtual machine failing to reboot, go
ahead and select <tt class="docutils literal">Machine > Reset</tt> in the VirtualBox menu, it
doesn’t matter how you do it as long as the machine is reset. If all
goes well, Lubuntu should now automatically boot to a login screen -
congratulations, your virtual machine is ready to use! Enter your
username and password, and proceed to the next section of instructions.</p>
<div class="figure align-center">
<img alt="Login screen" src="https://inclem.net/media/setup_buildozer_vm/009_1_login_screen.png" />
</div>
</div>
<div class="section" id="setting-up-buildozer">
<h2>Setting up buildozer</h2>
<p>We can now go ahead and set up buildozer ready to build your app. Open
an LXTerminal as below:</p>
<div class="figure align-center">
<img alt="LXTerminal location in menu" src="https://inclem.net/media/setup_buildozer_vm/010_open_LXTerminal.png" />
</div>
<p>We now have to run a few commands to install everything buildozer
needs to run. Run the following command to do so, and enter your
user’s password if prompted:</p>
<div class="highlight"><pre><span></span>sudo apt-get install python3-pip openjdk-8-jdk autoconf libtool python3-venv
</pre></div>
<p>That should give us everything we need for a basic app, so we can go
ahead and install buildozer:</p>
<div class="highlight"><pre><span></span>python3 -m venv buildozer-env
<span class="nb">source</span> buildozer-env/bin/activate
pip install buildozer cython
</pre></div>
<p>Note that we installed cython as well, this is also required for
building the <span class="caps">APK</span>.</p>
<p>You only have to create the virtual environment once, but if you
reboot the virtual machine you’ll need to run <tt class="docutils literal">source
<span class="pre">buildozer-env/bin/activate</span></tt> again. See the <a class="reference external" href="https://docs.python.org/3/tutorial/venv.html">Python documentation</a> for more details.</p>
<p>The final step before running buildozer is to have your app ready in
the virtual machine. You can access a folder in your host machine
using VirtualBox shared folders (in the <tt class="docutils literal">Devices > Shared Folders</tt>
menu), but I won’t cover the details here. Note though that if you do
this you <em>must</em> copy the folder contents to a different folder within
the virtual machine, the buidozer process will not work if run within
a shared folder.</p>
<p>In the following instructions I’ll assume you’ve created a folder
named <tt class="docutils literal">app_dir</tt> and placed a <tt class="docutils literal">main.py</tt> file inside it containing
your application code. Navigate to this folder in the terminal (<tt class="docutils literal">cd
app_dir</tt>) and run:</p>
<div class="highlight"><pre><span></span>buildozer init
</pre></div>
<p>This will create a buildozer.spec file alongside your main.py:</p>
<div class="figure align-center">
<img alt="Creating buildozer.spec" src="https://inclem.net/media/setup_buildozer_vm/011_terminal_in_app_dir.png" />
</div>
<p>Edit the buildozer.spec to set any options you like. In this example
I’ve changed only the title and pacakge.name options:</p>
<div class="figure align-center">
<img alt="Editing buildozer.spec" src="https://inclem.net/media/setup_buildozer_vm/012_edit_buildozer_spec.png" />
</div>
<p>I recommend changing very little for this first build, to make sure
everything works. It won’t cause any problems if you edit the
buildozer.spec again later.</p>
</div>
<div class="section" id="running-buildozer">
<h2>Running buildozer</h2>
<p>We’re now ready to actually build the app into an <span class="caps">APK</span> file. Start the
process with:</p>
<div class="highlight"><pre><span></span>buildozer -v android debug
</pre></div>
<p>The <tt class="docutils literal"><span class="pre">-v</span></tt> option asks for verbose output. This is recommended so that
you can keep an eye on what’s happening - the details aren’t too
important, but you should be able to see that the process never stops
in one place for too long.</p>
<p>Buildozer will now download the Android tools it needs. This may take
a while.</p>
<p>At some point you’ll be asked to accept the Android <span class="caps">SDK</span> license
agreement, which is printed for you as in the following image:</p>
<div class="figure align-center">
<img alt="SDK license agreement" src="https://inclem.net/media/setup_buildozer_vm/014_license_agreement.png" />
</div>
<p>At this point, press “y” and then enter to accept the agreement (or
abort the process if you don’t agree). This is necessary even if you
don’t see any text asking you to do so, due to a bug in buildozer
(fixed in the next release).</p>
<p>After downloading everything it needs, buildozer will work through the
build process compiling and packaging each of the components for your
app. This may take a while, but as long as it doesn’t crash then
everything is fine. Future builds will be much faster unless you
change the build options, as only the contents of your app itself will
need re-packaging.</p>
<p>Eventually the build will complete, you’ll see a screen like the following:</p>
<div class="figure align-center">
<img alt="Build complete" src="https://inclem.net/media/setup_buildozer_vm/015_build_complete.png" />
</div>
<p>That’s it, you’re done! You can find the finished <span class="caps">APK</span> in the <tt class="docutils literal">bin</tt>
directory, as noted in the final message buildozer prints.</p>
</div>
Lazy Baduk: Leela Zero analysis tool for Android2019-03-01T21:00:00+01:002019-03-01T21:00:00+01:00Alexander Taylortag:inclem.net,2019-03-01:/2019/03/01/baduk/lazy_baduk_release/<p>A few days ago I released <a class="reference external" href="https://github.com/inclement/LazyBaduk">Lazy Baduk</a>, a Leela Zero analysis
tool for Android. You can download it <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.lazybaduk">from Google Play</a>,
or directly from the Github release (see below).</p>
<div class="figure align-center">
<img alt="Example Lazy Baduk usage showing Leela Zero analysis." src="https://inclem.net/media/lazy_baduk_screenshot_small.png" />
</div>
<p>Lazy Baduk focuses on making it as easy as possible to start entering and
analysing moves with Leela Zero, with …</p><p>A few days ago I released <a class="reference external" href="https://github.com/inclement/LazyBaduk">Lazy Baduk</a>, a Leela Zero analysis
tool for Android. You can download it <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.lazybaduk">from Google Play</a>,
or directly from the Github release (see below).</p>
<div class="figure align-center">
<img alt="Example Lazy Baduk usage showing Leela Zero analysis." src="https://inclem.net/media/lazy_baduk_screenshot_small.png" />
</div>
<p>Lazy Baduk focuses on making it as easy as possible to start entering and
analysing moves with Leela Zero, with core support for pondering,
variation display, <span class="caps">AI</span> play (including autoplay for black and/or
white), and a winrate graph. I’ve kept the interface as simple as
possible for that reason, although I may in the future add features
such as <span class="caps">SGF</span> save/load.</p>
<p>The version on Google Play uses the last 15x192 network, from July
last year, as a tradeoff between strength and analysis speed. Larger
networks are both noticeably slower to load on a phone, and slower to
generate playouts. I’m not sure what is the best choice to balance
strength and speed, but the current version seems to perform fairly well.</p>
<p>I’ve also made available some other network versions for direct
download. The apps have different names and can be installed
simultaneously if you want:</p>
<ul class="simple">
<li><a class="reference external" href="https://github.com/inclement/LazyBaduk/releases/download/0.6/lzviewer15x192-0.6-release-signed.apk">Latest 15x192 network d351f06e</a>: the same as the version on Google Play</li>
<li><a class="reference external" href="https://github.com/inclement/LazyBaduk/releases/download/0.6/lzviewer20x256-0.6-release-signed.apk">Latest 20x256 network 33986b7f</a></li>
<li><a class="reference external" href="https://github.com/inclement/LazyBaduk/releases/download/0.6/lzviewer40x256-0.6-release-signed.apk">Latest 40x256 network 85a93684</a>: this one especially is much larger and slower to load than the 15x192 version</li>
<li><a class="reference external" href="https://github.com/inclement/LazyBaduk/releases/download/0.6/lzviewerElfv2-0.6-release-signed.apk">Elfv2</a>: the <a class="reference external" href="https://github.com/pytorch/elf"><span class="caps">ELF</span> OpenGo version 2</a> weights converted for use with Leela Zero. <span class="caps">ELF</span> is Facebook’s own implementation of a zero knowledge algorithm, and is very strong (network size 20x256).</li>
<li><a class="reference external" href="https://github.com/inclement/LazyBaduk/releases/download/0.6/lzviewer9x9-0.6-release-signed.apk">9x9</a>: uses the 9x9 weights generated by @sbf2000 <a class="reference external" href="https://github.com/leela-zero/leela-zero/issues/1291">here</a> (these may not be the strongest 9x9 weights available)</li>
<li><a class="reference external" href="https://github.com/inclement/LazyBaduk/releases/download/0.6/lazybaduk13x13-0.6-release-signed.apk">13x13</a>: uses the 13x13 weights generated <a class="reference external" href="https://github.com/leela-zero/leela-zero/issues/2240#issuecomment-466476336">by @alreadydone</a> from the 19x19 weights #205 (the linked issue describes this conversion process), apparently this network is quite strong</li>
</ul>
<p>Lazy Baduk’s source is available <a class="reference external" href="https://github.com/inclement/LazyBaduk">on Github</a> if anyone is interested,
although it’s a messy combination of the old code for my <span class="caps">SGF</span> editor
noGo and new code to interface with Leela Zero. The app is written in
Python using the <a class="reference external" href="https://kivy.org/#home">Kivy graphical framework</a>, so it can also be run on the desktop. It
works by including a version of Leela Zero compiled for <span class="caps">ARM</span>, running
only on the <span class="caps">CPU</span> although <span class="caps">GPU</span> support might be possible in the future
for some devices. Leela Zero does use as much <span class="caps">CPU</span> power as it can,
which can drain the battery very fast - consider charging your phone
while using the app!</p>
<p>If this is interesting to you, you might also be interested in <a class="reference external" href="https://github.com/featurecat/lizzie">lizzie</a> on the desktop. Or on
Android <a class="reference external" href="https://play.google.com/store/apps/details?id=cn.ezandroid.aq">Ah Q Go</a> is
a very full featured <span class="caps">SGF</span> client supporting many bot play options, at
the cost of less focus on diving into quick analysis.</p>
Kivy Hackathon and FOSDEM 20192019-02-01T23:25:00+01:002019-02-01T23:25:00+01:00Alexander Taylortag:inclem.net,2019-02-01:/2019/02/01/kivy/fosdem_hackathon_2019/<p>For the last 3 days several of the Kivy Core Developers gathered in
Brussels, Belgium for the first ever core developer hackathon. Not
only is this the first time we’ve gathered to work on Kivy framework
issues, but for most of us the first time we’ve ever met …</p><p>For the last 3 days several of the Kivy Core Developers gathered in
Brussels, Belgium for the first ever core developer hackathon. Not
only is this the first time we’ve gathered to work on Kivy framework
issues, but for most of us the first time we’ve ever met - even after
years of collaboration!</p>
<div class="figure align-center">
<img alt="From left to right: Peter Badida (KeyWeeUsr), Gabriel Pettier (tshirtman), Mathieu Virbel (tito), Alexander Taylor (inclement) and Andre Miras (AndreMiras)" src="https://inclem.net/media/hackathon-2019-02-01.jpg" />
</div>
<p>From left to right the attendees are Peter Badida (KeyWeeUsr), Gabriel
Pettier (tshirtman), Mathieu Virbel (tito), Alexander Taylor
(inclement) and Andre Miras (AndreMiras).</p>
<p>We used this opportunity to make amazing progress on a range of Kivy
issues. Most especially:</p>
<ul class="simple">
<li>55 closed issues and 36 merged pull requests across all Kivy
projects, specifically targeting long standing bugs and the most
important current issues.</li>
<li>A full update of <a class="reference external" href="https://github.com/kivy/buildozer">buildozer</a>
to work well with the latest <span class="caps">SDK</span>, <span class="caps">NDK</span> and other dependencies for
Android development. New release to follow!</li>
<li>Rapid progress on <a class="reference external" href="https://github.com/kivy/ncis">ncis</a>, a new
remote inspector/debugger for all types of Python application.</li>
<li>Many improvements to the <a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a> continuous
integration, and some major bugfixes for stability with Python 3 and
the latest toolchain. Preliminary 0.7.0 release already available
via PyPI and buildozer, more to follow.</li>
<li>Extensive cleanup and improvements for <a class="reference external" href="https://github.com/kivy/pyjnius">PyJNIus</a>.</li>
<li>A further ~170 closed <a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a> issues and ~110
<a class="reference external" href="https://github.com/kivy/buildozer">buildozer</a> issues, as a
general clean up of stale and outdated reports to focus on the ones
that really matter.</li>
</ul>
<p>For the next two days, the same team will be attending <a class="reference external" href="https://fosdem.org/2019/"><span class="caps">FOSDEM</span> 2019</a>. Let us know by <a class="reference external" href="https://chat.kivy.org/">discord</a> or <a class="reference external" href="https://kivy.org/#aboutus">other communication</a> if you’d like to say hello!</p>
Cached and templated files in python-for-android builds2018-11-22T22:00:00+01:002018-11-22T22:00:00+01:00Alexander Taylortag:inclem.net,2018-11-22:/2018/11/22/kivy/p4a_project_dirs/<p>I’ve more than once seen people confused by how python-for-android
constructs an Android project that can be compiled into an <span class="caps">APK</span>. Since
p4a uses various cached and templated files, it’s easy to get confused
trying to edit things only to find your changes are overwritten when
you run …</p><p>I’ve more than once seen people confused by how python-for-android
constructs an Android project that can be compiled into an <span class="caps">APK</span>. Since
p4a uses various cached and templated files, it’s easy to get confused
trying to edit things only to find your changes are overwritten when
you run p4a again.</p>
<p>Here’s a quick summary of what’s what.</p>
<div class="section" id="bootstraps">
<h2>Bootstraps</h2>
<p>The core project files are all in the <a class="reference external" href="https://github.com/kivy/python-for-android/tree/master/pythonforandroid/bootstraps">bootstraps directory</a>. A
bootstrap is basically an Android project, containing java sources,
<span class="caps">JNI</span> stuff to be compiled, and project management files like the
<tt class="docutils literal">AndroidManifest.xml</tt> and <tt class="docutils literal">build.gradle</tt>.</p>
<p>Note: Some of these files are built from templates, e.g. in the main
<a class="reference external" href="https://github.com/kivy/python-for-android/tree/master/pythonforandroid/bootstraps/sdl2/build"><span class="caps">SDL2</span> bootstrap</a>
you can find <tt class="docutils literal">build.tmpl.gradle</tt>, <tt class="docutils literal">AndroidManifest.tmpl.xml</tt>
etc. in the <a class="reference external" href="https://github.com/kivy/python-for-android/tree/master/pythonforandroid/bootstraps/sdl2/build/templates">templates subdirectory</a>.</p>
<p>If you edit the code in these bootstrap directories, the changes will
<em>always</em> take effect if you rebuild your whole project, but <em>might
not</em> affect anything if you repeat a previous build. This is due to
caching of previous build components. You can always guarantee that
everything is cleared to be rebuilt using <tt class="docutils literal">p4a clean dists builds</tt>
(or there are other tricks if you look into it).</p>
</div>
<div class="section" id="build-directories">
<h2>Build directories</h2>
<p>Most of the individual python-for-android recipes are built in
separate build directories. The location of these depends on the <span class="caps">OS</span>:
on Linux the default is <tt class="docutils literal"><span class="pre">~/.local/share/python-for-android</span></tt>,
especially <tt class="docutils literal"><span class="pre">~/.local/share/python-for-android/other_builds</span></tt>. When
you build a recipe, it is copied here for the build to be run. These
folders also cache builds, so if you e.g. build two different projects
using <tt class="docutils literal">python3</tt> then the same build directory is used both times,
reducing time spent compiling.</p>
<p>You can edit code in
<tt class="docutils literal"><span class="pre">~/.local/share/python-for-android/other_builds</span></tt>, but it isn’t
recommended unless you have a good idea what python-for-android will
overwrite or cache on each run. It is, however, sometimes useful
during development.</p>
</div>
<div class="section" id="dists">
<h2>Dists</h2>
<p>Dists are p4a’s fully compiled Android projects, including the bundled
output of all the different requirements specified by the user. On
Linux, their default location is
<tt class="docutils literal"><span class="pre">~/.local/share/python-for-android/dists</span></tt>.</p>
<p>Every time you build an <span class="caps">APK</span> using <tt class="docutils literal">p4a apk ...</tt>, this essentially
runs gradle from an appropriate dist directory (i.e. the one with the
recipes you wanted). At this point, nothing new is built or copied
from the build directories, so changes you make to p4a’s recipes or
build directories have no effect on the output. However, templated
files such as <tt class="docutils literal">AndroidManifest.tmpl.xml</tt> are rebuilt <em>every
time</em>. That means that you must edit the templates themselves if you
want your changes to make it to the <span class="caps">APK</span>.</p>
<p>You can always run <tt class="docutils literal">p4a clean dists</tt> to delete the existing Android
projects. Next time you run <tt class="docutils literal">p4a apk ...</tt>, a new dist will be created.</p>
</div>
<div class="section" id="buildozer">
<h2>Buildozer</h2>
<p>When using buildozer, everything works basically the same except that
the build and dist directories can be found in
<tt class="docutils literal"><span class="pre">$PROJECT_DIR/.buildozer/android/platform/build</span></tt> instead of
<tt class="docutils literal"><span class="pre">~/.local/share/python-for-android</span></tt>. The python-for-android source
code is stored in
<tt class="docutils literal"><span class="pre">$PROJECT_DIR/.buildozer/android/platform/python-for-android</span></tt>.</p>
</div>
python-for-android 0.6 released2017-11-27T23:00:00+01:002017-11-27T23:00:00+01:00Alexander Taylortag:inclem.net,2017-11-27:/2017/11/27/kivy/python_for_android_0_6_0_released/<p>We’ve just officially released <a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android 0.6</a>. The new version can
be downloaded via pip, or will be used by buildozer automatically in
new installations. This release contains about 130 new commits from
14 different contributors. Thanks to everyone involved!</p>
<p><a class="reference external" href="http://python-for-android.readthedocs.io/en/latest/">python-for-android</a> is a
packaging tool for turning Python scripts …</p><p>We’ve just officially released <a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android 0.6</a>. The new version can
be downloaded via pip, or will be used by buildozer automatically in
new installations. This release contains about 130 new commits from
14 different contributors. Thanks to everyone involved!</p>
<p><a class="reference external" href="http://python-for-android.readthedocs.io/en/latest/">python-for-android</a> is a
packaging tool for turning Python scripts and apps into Android
APKs. It was originally created for use with the <a class="reference external" href="https://kivy.org/#home">Kivy graphical
framework</a>, but now supports multiple kinds
of Python app including Kivy, PySDL2, a webview interface with Flask
or other webserver backend, plain Python scripts without a <span class="caps">GUI</span>, or other
possibilities such as Python builds for use in other applications.</p>
<p>As planned following the release of python-for-android 0.5, the new
version includes some relatively major changes and improvements. In
particular, python-for-android should now work with all recent
versions of the Android <span class="caps">SDK</span> and <span class="caps">NDK</span>. On the <span class="caps">SDK</span> side this means
python-for-android now uses gradle if available, although this doesn’t
require any changes to the configuration on the user side.</p>
<p>For the next release I intend to focus work on checking and updating
python-for-android’s build recipes to make sure they are all properly
compatible with one another and with different build
configurations. I’d also like to improve our automated testing, in
order to more easily detect and fix issues as they appear.</p>
<p>For full instructions and further information, see the
<a class="reference external" href="https://python-for-android.readthedocs.io/en/latest/">python-for-android documentation</a>.</p>
python-for-android 0.5 released2017-08-26T20:54:00+02:002017-08-26T20:54:00+02:00Alexander Taylortag:inclem.net,2017-08-26:/2017/08/26/kivy/python_for_android_0_5_released/<p>We’ve just officially released <a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android 0.5</a>. The new version can
be downloaded via pip, or will be used by buildozer automatically in
new installations. This release contains about 300 commits from
almost 40 different contributors. Thanks to everyone involved!</p>
<p><a class="reference external" href="http://python-for-android.readthedocs.io/en/latest/">python-for-android</a> is a
packaging tool for turning Python scripts …</p><p>We’ve just officially released <a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android 0.5</a>. The new version can
be downloaded via pip, or will be used by buildozer automatically in
new installations. This release contains about 300 commits from
almost 40 different contributors. Thanks to everyone involved!</p>
<p><a class="reference external" href="http://python-for-android.readthedocs.io/en/latest/">python-for-android</a> is a
packaging tool for turning Python scripts and apps into Android
APKs. It was originally created for use with the <a class="reference external" href="https://kivy.org/#home">Kivy graphical
framework</a>, but now supports multiple kinds
of Python app including Kivy, PySDL2, a webview interface with Flask
or other webserver backend, plain Python scripts without a <span class="caps">GUI</span>, or other
possibilities such as Python builds for use in other applications.</p>
<p>This release contains many fixes and improvements to all parts of the
toolchain. Now that these have been released as stable, we intend to
move quickly to make some larger improvements including supporting
gradle builds, and better support for Python 3 in some recipes.</p>
<p>For full instructions and further information, see the
<a class="reference external" href="https://python-for-android.readthedocs.io/en/latest/">python-for-android documentation</a>.</p>
Pyonic interpreter 1.3 released: Adds support for loading and executing files2017-04-09T22:31:00+02:002017-04-09T22:31:00+02:00Alexander Taylortag:inclem.net,2017-04-09:/2017/04/09/kivy/pyonic_interpreter_1_3_0released/<p>I’ve just released Pyonic interpreter 1.3. As usual you can download it
from Google Play, for <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Python 2.7</a>
or <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">Python 3.6</a>.
The APKs can also be downloaded directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v1.3.0">from Github</a>
(where the <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter">source code</a> is also available).</p>
<div class="figure align-center">
<img alt="The new filebrowser interface in Pyonic interpreter." src="https://inclem.net/media/pyonic_1_3_filebrowser.png" style="width: 300px;" />
</div>
<p>The main addition in this release is a file …</p><p>I’ve just released Pyonic interpreter 1.3. As usual you can download it
from Google Play, for <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Python 2.7</a>
or <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">Python 3.6</a>.
The APKs can also be downloaded directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v1.3.0">from Github</a>
(where the <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter">source code</a> is also available).</p>
<div class="figure align-center">
<img alt="The new filebrowser interface in Pyonic interpreter." src="https://inclem.net/media/pyonic_1_3_filebrowser.png" style="width: 300px;" />
</div>
<p>The main addition in this release is a file browser interface, which
gives Pyonic the ability to load and execute files in the
interpreter. This is mildly useful on its own, and I’ve had comments
that people would like to be able to do it, but it’s also groundwork
for full support for file editing support. I hope to add these
features in a future version.</p>
Pyonic interpreter 1.2 released: Now supports Python 3.6 and input functions2017-02-25T22:15:00+01:002017-02-25T22:15:00+01:00Alexander Taylortag:inclem.net,2017-02-25:/2017/02/25/kivy/pyonic_interpreter_1_2_released/<p>I’ve just released Pyonic interpreter 1.2. As usual, you can get it
from Google Play, now for <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Python 2.7</a>
or <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">Python 3.6</a>.
The APKs can also be downloaded directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v1.2.0">from Github</a>
(where the <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter">source code</a> is also available).</p>
<div class="figure align-center">
<img alt="Pyonic interpreter showing docstring and autocompletion options" src="https://inclem.net/media/pyonic_1_2_input.png" style="width: 300px;" />
</div>
<p>This is the first release to target Python …</p><p>I’ve just released Pyonic interpreter 1.2. As usual, you can get it
from Google Play, now for <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Python 2.7</a>
or <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">Python 3.6</a>.
The APKs can also be downloaded directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v1.2.0">from Github</a>
(where the <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter">source code</a> is also available).</p>
<div class="figure align-center">
<img alt="Pyonic interpreter showing docstring and autocompletion options" src="https://inclem.net/media/pyonic_1_2_input.png" style="width: 300px;" />
</div>
<p>This is the first release to target Python 3.6 on Android (not just
Python 3.5), which is made possible by recent additions to
python-for-android. I expect to do a separate python-for-android
release to announce this shortly.</p>
<p>The main change to the app this release is support for the <tt class="docutils literal">input</tt>
and (in Python 2) <tt class="docutils literal">raw_input</tt> functions. These would previously
crash as the interpreter isn’t really being run in a shell, so the way
they try to take input doesn’t work. They are now overridden with new
replacements, which should hopefully behave roughly the same way as the
originals are supposed to, but via a more convenient popup gui for the
text to be entered.</p>
<p>I’m still working on file editing and other Python management
functions, but there didn’t seem to be any reason to delay a release
since according to the Google Play reviews people are trying and
failing to use the input functions.</p>
Pyonic interpreter 1.1 released: Python 2/3 interpreter for Android, now with autocompletion2016-12-11T23:05:00+01:002016-12-11T23:05:00+01:00Alexander Taylortag:inclem.net,2016-12-11:/2016/12/11/kivy/pyonic_interpreter_1_1_released/<p>I’ve just released Pyonic interpreter 1.1. As usual, you can get it
from Google Play for <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Python 2.7</a>
or <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">Python 3.5</a>,
or download the APKs directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v.1.1.0">from Github</a>
(where the <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter">source code</a> is also available).</p>
<div class="figure align-center">
<img alt="Pyonic interpreter showing docstring and autocompletion options" src="https://inclem.net/media/pyonic_1_1_jedi.png" style="width: 300px;" />
</div>
<p>The major feature in this release is autocompletion support via the …</p><p>I’ve just released Pyonic interpreter 1.1. As usual, you can get it
from Google Play for <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Python 2.7</a>
or <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">Python 3.5</a>,
or download the APKs directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v.1.1.0">from Github</a>
(where the <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter">source code</a> is also available).</p>
<div class="figure align-center">
<img alt="Pyonic interpreter showing docstring and autocompletion options" src="https://inclem.net/media/pyonic_1_1_jedi.png" style="width: 300px;" />
</div>
<p>The major feature in this release is autocompletion support via the
excellent <a class="reference external" href="https://github.com/davidhalter/jedi">jedi library</a>, as
is used by many editors and IDEs. Pyonic now automatically gives
a list of autocompletion options as you write Python code, any of
which can be selected by tapping it. There’s also a new help button,
which when pressed shows the call signature and docstring of the
Python object reference currently under the cursor.</p>
<p>As a further bonus, I’ve reduced the size of the Python 3 <span class="caps">APK</span> by a
further ~25%, it’s now around <span class="caps">11MB</span>. This is probably still a little
larger than it needs to be, but is much better than the massive <span class="caps">19MB</span>
version that I first published! I’ll continue to try to improve this
with tweaks in python-for-android’s Python 3 build process.</p>
<p>For the next release, I intend to go back to improving Pyonic’s
process handling, and from there to add support for pip installation
of new modules and file editing. The latter of these will also benefit
from the new autocompletion integration.</p>
Pyonic interpreter 1.0 released2016-12-04T22:30:00+01:002016-12-04T22:30:00+01:00Alexander Taylortag:inclem.net,2016-12-04:/2016/12/04/kivy/pyonic_interpreter_1_0_released/<p>I’ve just released Pyonic interpreter 1.0. You can get it from Google
Play for <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Python 2.7</a>
or <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">Python 3.5</a>,
or download the APKs directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v1.0.1">from Github</a>
(update: changed to v1.0.1 following a small bugfix).</p>
<p>The primary change in this release is that both APKs …</p><p>I’ve just released Pyonic interpreter 1.0. You can get it from Google
Play for <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Python 2.7</a>
or <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">Python 3.5</a>,
or download the APKs directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v1.0.1">from Github</a>
(update: changed to v1.0.1 following a small bugfix).</p>
<p>The primary change in this release is that both APKs are about 25%
smaller than before, thanks to optimisations in the Python
distributions that I’ve added to python-for-android - in particular,
making sure Python files are shipped as .pyo files (which may also
speed things up a bit) and stripping unneeded symbols from object
files with Python 3. Both of these were things python-for-android has
been missing for a while, so it’s nice to get them working and
immediately see the benefits.</p>
<p>I’ve also been working on some backend improvements in Pyonic and
python-for-android in order to support multiple interpreter processes.
This will be convenient for using pip and running Python code from
files, but isn’t ready yet and so hasn’t made it into this release.</p>
Pyonic interpreter 0.7 released: Python for Android, now supports Python 32016-11-05T10:30:00+01:002016-11-05T10:30:00+01:00Alexander Taylortag:inclem.net,2016-11-05:/2016/11/05/kivy/pyonic_interpreter_0_7_released/<p>Pyonic interpreter 0.7 has just been released. There are now two
versions on Google Play, one <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">for Python 2.7</a>
and one <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">for Python 3.5</a>. The
APKs are also available directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v0.7">from Github</a>. Other
features in this release include a new settings screen and improved
gui arrangement.</p>
<div class="figure align-center">
<img alt="Three screenshots of Pyonic interpreter." src="https://inclem.net/media/pyonic_0_7_images.png" style="width: 700px;" />
</div>
<p>The …</p><p>Pyonic interpreter 0.7 has just been released. There are now two
versions on Google Play, one <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">for Python 2.7</a>
and one <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter3">for Python 3.5</a>. The
APKs are also available directly <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter/releases/tag/v0.7">from Github</a>. Other
features in this release include a new settings screen and improved
gui arrangement.</p>
<div class="figure align-center">
<img alt="Three screenshots of Pyonic interpreter." src="https://inclem.net/media/pyonic_0_7_images.png" style="width: 700px;" />
</div>
<p>The app is written in Python using Kivy, and uses exactly the same
code under both Python versions. This code is open source and
available online <a class="reference external" href="https://github.com/inclement/Pyonic-interpreter">on Github</a>.</p>
<p>This release includes most of the short term improvements I had
planned, since supporting Python 3 didn’t raise any major issues. I
expect that development will now focus on adding a few more usability
tweaks, then working with the Python packaging to add features like
pip installs for new modules, code editing (rather than just the
interpreter interface), and support for <span class="caps">GUI</span> creation via Kivy.</p>
Pyonic interpreter: a Python interpreter GUI for Android, written in Python2016-10-25T20:14:00+02:002016-10-25T20:14:00+02:00Alexander Taylortag:inclem.net,2016-10-25:/2016/10/25/kivy/pyonic_interpreter_0_6_released/<p>I’ve just released a new app, <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Pyonic Python 2 interpreter</a>.
Pyonic interpreter is a Python interpreter app for Android, providing
a convenient interface adapted to mobile devices. The app itself is
written entirely in Python using <a class="reference external" href="https://kivy.org/#home">Kivy</a>.</p>
<div class="figure align-center">
<img alt="Screenshot of the interpreter app." src="https://inclem.net/media/pyonic_0_5_images.png" style="width: 700px;" />
</div>
<p>I put this together because I’ve always thought it would be …</p><p>I’ve just released a new app, <a class="reference external" href="https://play.google.com/store/apps/details?id=net.inclem.pyonicinterpreter">Pyonic Python 2 interpreter</a>.
Pyonic interpreter is a Python interpreter app for Android, providing
a convenient interface adapted to mobile devices. The app itself is
written entirely in Python using <a class="reference external" href="https://kivy.org/#home">Kivy</a>.</p>
<div class="figure align-center">
<img alt="Screenshot of the interpreter app." src="https://inclem.net/media/pyonic_0_5_images.png" style="width: 700px;" />
</div>
<p>I put this together because I’ve always thought it would be nice to
have a Python interpreter app that is itself written in Python, and in
principle Kivy and <a class="reference external" href="http://python-for-android.readthedocs.io/en/latest/">python-for-android</a> provide all
the necessary components. In practice this worked even better than I
expected, Kivy handled almost everything perfectly - I actually
underestimated its maturity here! As part of the project, I’ve tried
to round a number of corners that Kivy apps sometimes can to have, so
that the interpreter (hopefully) behaves nicely in all
situations. Within the interpreter, all of the standard library is
available, and it’s possible to interrupt execution (equivalent to the
normal ctrl+c behaviour) or to restart the interpreter process. No
external modules are included yet except those necessary for the app
to run, but I’ll probably include some major ones like numpy in a
future release, and in the long term the aim is to support pip
installs of new modules.</p>
<p>This has also been a great stimulus for working on python-for-android;
I’ve fixed a number of bugs, added several new features, and improved
documentation in several places, just thanks to needing these things
in a real app.</p>
<p>On a technical level, Pyonic interpreter runs under Python 2,
consisting of the app itself and a background Service running a second
instance of the interpreter. I’ll be working on Python 3 support in
the near future, in fact I originally wrote the app using Python 3 but
switched to Python 2 partly due to incompatibilities in Kivy’s osc
library (which should be easily fixed or avoided by just using a
better communication library) and partly the more well-tested nature
of python-for-android’s Python 2 build.</p>
<p>The interpreter works by passing submitted Python code to the
background Python process where it is parsed as ast and compiled in
‘exec’ or ‘single’ mode as appropriate to replicate the output
printing behaviour of the normal Python interpreter. Doing things this
way is a little awkward and feels like reinventing the wheel, although
I’m not sure how to better achieve the same thing. An alternative
might be to just call the python binary in a subprocess and manipulate
its stdin/stdout - I’ll be looking into this option, but it doesn’t
eliminate the need for message passing and may need some small changes
in python-for-android, assuming also that android doesn’t impose any
important limits on subprocessing.</p>
<p>In the short term future, I expect to work first on releasing an
improved version that adds a number of useful settings options (sneak
peek in the image below), followed by working on a Python 3 version,
and then to investigate some of these technical questions. I’d like to
look into iOS support, as everything should work almost the same way
there, but I don’t have the hardware or developer mempership for iOS
development; if anyone would like to try it, let me know. Longer term,
Pyonic interpreter is an experimental step towards creating a larger
suite of mobile Python tools, in tandem with using this experience to
improve python-for-android. There are many features to be added
directly to the interpreter, but I’d also like to add surrounding
tools including a full code editor, the ability to use pip to install
other modules locally, and <span class="caps">GUI</span> support via additional Kivy activities.</p>
<div class="figure align-center">
<img alt="Screenshot of the settings screen in the development version of Pyonic interpreter." src="https://inclem.net/media/pyonic_android_beta_settings_small.png" style="width: 200px;" />
<p class="caption">Settings screen in development to appear in the next release.</p>
</div>
python-for-android 0.4 released, now available on PyPI2016-06-18T23:06:00+02:002016-06-18T23:06:00+02:00Alexander Taylortag:inclem.net,2016-06-18:/2016/06/18/kivy/python_for_android_0_4_released/<p>We’ve just officially released python-for-android 0.4, and pushed it
to PyPI for the first time!</p>
<p><a class="reference external" href="http://python-for-android.readthedocs.io/en/latest/">python-for-android</a> is a
packaging tool for turning Python scripts and apps into Android
APKs. It was originally created for use with the <a class="reference external" href="https://kivy.org/#home">Kivy graphical
framework</a>, but now supports multiple kinds
of Python app …</p><p>We’ve just officially released python-for-android 0.4, and pushed it
to PyPI for the first time!</p>
<p><a class="reference external" href="http://python-for-android.readthedocs.io/en/latest/">python-for-android</a> is a
packaging tool for turning Python scripts and apps into Android
APKs. It was originally created for use with the <a class="reference external" href="https://kivy.org/#home">Kivy graphical
framework</a>, but now supports multiple kinds
of Python app including Kivy, PySDL2, a webview interface with Flask
or other webserver backend, plain Python scripts without a <span class="caps">GUI</span>, or other
possibilities such as Python builds for use in other applications.</p>
<p>This release is the culmination of all the work over the last year to
replace Kivy’s old Android toolchain with something more flexible and
useful for other projects. Major features added in this time include
the fully Python toolchain itself, support for <span class="caps">SDL2</span> and other
bootstraps, (experimental) python3 support via the <a class="reference external" href="https://www.crystax.net/">CrystaX <span class="caps">NDK</span></a>, multiple architecture support, and many
general improvements to the backend. Many thanks to all the
contributors who have made this possible!</p>
<p>From now on we intend to move to regular versioned releases rather
than the previous rolling master branch. Short term targets for the
next release include bringing the python3 build up to full
functionality and stability, and some argument restructuring to make
command line usage simpler and clearer.</p>
<p>As of this release, you can now install python-for-android with simply:</p>
<div class="highlight"><pre><span></span>pip install python-for-android
</pre></div>
<p>For full instructions and further information, see the
<a class="reference external" href="https://python-for-android.readthedocs.io/en/latest/">python-for-android documentation</a>.</p>
Kivy at PyCon2016-06-03T08:13:00+02:002016-06-03T08:13:00+02:00Alexander Taylortag:inclem.net,2016-06-03:/2016/06/03/kivy/kivy_at_pycon/<p>Jacob Kovac, Kivy core developer and creator of the KivEnt game
engine, is at PyCon 2016.</p>
<p>Click <a class="reference external" href="https://youtu.be/XyzGPj-Q8hY">here</a> or see below to watch
his talk, Revitalizing Python Game Development: Packaging,
Performance, and Platforms.</p>
<iframe width="700" height="393" src="https://www.youtube.com/embed/XyzGPj-Q8hY" frameborder="0" allowfullscreen></iframe><p>Jacob Kovac, Kivy core developer and creator of the KivEnt game
engine, is at PyCon 2016.</p>
<p>Click <a class="reference external" href="https://youtu.be/XyzGPj-Q8hY">here</a> or see below to watch
his talk, Revitalizing Python Game Development: Packaging,
Performance, and Platforms.</p>
<iframe width="700" height="393" src="https://www.youtube.com/embed/XyzGPj-Q8hY" frameborder="0" allowfullscreen></iframe>Kivy 1.10 released2016-05-07T18:00:00+02:002016-05-07T18:00:00+02:00Alexander Taylortag:inclem.net,2016-05-07:/2016/05/07/kivy/kivy_1_10_0_released/<p>We’re pleased to announce a new stable version of Kivy, version 1.10. You
can see the full detailed changelog on the <a class="reference external" href="https://groups.google.com/forum/#!topic/kivy-users/2zusmq8NXPE">mailing list announcement</a>,
and download the new version from the <a class="reference external" href="http://kivy.org/#download">Kivy website</a> or via your package manager.</p>
<p>This release includes many new features. Highlights include:</p>
<ul class="simple">
<li>Python 3 …</li></ul><p>We’re pleased to announce a new stable version of Kivy, version 1.10. You
can see the full detailed changelog on the <a class="reference external" href="https://groups.google.com/forum/#!topic/kivy-users/2zusmq8NXPE">mailing list announcement</a>,
and download the new version from the <a class="reference external" href="http://kivy.org/#download">Kivy website</a> or via your package manager.</p>
<p>This release includes many new features. Highlights include:</p>
<ul class="simple">
<li>Python 3.5/3.6+ support on Windows. Support for these versions was
previously available in nightly builds due to changes in the
compiler toolchain, but official wheels are now available.</li>
<li>A new RecycleView widget replaces the old ListView. It is
faster and more efficient, but with a simpler <span class="caps">API</span>.</li>
<li>Graphics backend improvements including <span class="caps">ANGLE</span> support on Windows and
a mock backend for headless Kivy.</li>
<li>Several new Window options when using the <span class="caps">SDL2</span> backend, for much
improved cross-platform behaviour.</li>
<li>Improvements in Clock performance.</li>
</ul>
<p>There are also many other new features and bugfixes, spread across
over 1500 commits from almost 100 different developers. Thanks to everyone who contributed!</p>
Android apps with Python, Flask and a WebView2016-05-07T18:00:00+02:002016-05-07T18:00:00+02:00Alexander Taylortag:inclem.net,2016-05-07:/2016/05/07/kivy/python_for_android_webview_support/<p><a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a>
has just gained support for a new webview app interface, an
alternative to the existing <span class="caps">SDL2</span> or Pygame backends. Under this mode
of operation the app gui consists entirely of a browser window
directed to open a webpage on localhost, and the Python backend can
then run any web …</p><p><a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a>
has just gained support for a new webview app interface, an
alternative to the existing <span class="caps">SDL2</span> or Pygame backends. Under this mode
of operation the app gui consists entirely of a browser window
directed to open a webpage on localhost, and the Python backend can
then run any web framework (I tested with Flask, but others like
Bottle or even Django should work), serving this website and managing
the app backend.</p>
<div class="figure align-center">
<img alt="Example Flask app running on Android" src="https://inclem.net/media/flask_on_android.png" style="width: 200px;" />
</div>
<p>This idea is not itself new; I think <span class="caps">SL4A</span> has supported a kind of
webview interface for some time and certainly <a class="reference external" href="https://github.com/ainsophical/DROID_PYTHON">does so now</a>, and we’ve previously
seen users running web servers alongside Kivy. The difference to other
projects is that apps can take advantage of python-for-android’s
relatively extensive toolchain including python3.5 support, the
ability to build popular libraries like numpy, support for multiple
architectures, and access to the Android <span class="caps">API</span> via <a class="reference external" href="https://pyjnius.readthedocs.io/en/latest/">PyJNIus</a> or <a class="reference external" href="https://github.com/kivy/plyer">Plyer</a> rather than <span class="caps">SL4A</span>.</p>
<p>In the image of my testing app above, each of the vibration and orientation
buttons sends a request to a Flask url that calls the Android
<span class="caps">API</span> with PyJNIus to achieve the desired result.</p>
<div class="section" id="building-a-webview-app">
<h2>Building a webview app</h2>
<p>You can use the webview backend by adding <code>--bootstrap=webview</code>
to your python-for-android command line (see <a class="reference external" href="http://python-for-android.readthedocs.io/en/latest/quickstart/">the documentation</a>
for more details), or including <code>webviewjni</code> in your
<code>--requirements</code> argument list. Note that this is incompatible
with using <span class="caps">SDL</span> or Kivy because the webview bootstrap does not start or
manage an OpenGL context. If for any reason you want to run a web
server alongside a Kivy app, this is possible but you’ll need to use a
different bootstrap and manage the webview yourself via PyJNIus from
your Kivy code.</p>
<p>You should also add your chosen web framework to the
<code>--requirements</code> argument, or include it your app directory so
that it will be imported locally. If there isn’t a recipe for it and
it’s a pure Python module, make sure you also add its Python
dependencies as these aren’t automatically included right now (letting
pip resolve dependencies causes issues when they include compiled
modules that must be built separately). python-for-android now
includes a recipe for Flask that automatically installs its
dependencies (jinja2, werkzeug, markupsafe, itsdangerous and click),
so you only need to add <code>flask</code> to the requirements in that case.</p>
</div>
<div class="section" id="technical-details">
<h2>Technical details</h2>
<p>It turns out that very little hackery is necessary to make a webview
type app work. The <span class="caps">APK</span> seems to need the <span class="caps">INTERNET</span> permission to use a
WebView, but Android is very happy for the Python code to run a web
server with no further problems.</p>
<p>Making PyJNIus work required a little extra work, as it previously
relied on the now-absent <span class="caps">SDL</span> to access a pointer to the current
JNIEnv. This was fairly simple to fix by using only the relevant code
from <span class="caps">SDL2</span> - the important parts are only a small fraction
of what <span class="caps">SDL</span> provides, as <span class="caps">SDL</span> has to worry about all the app input and
output going via <span class="caps">JNI</span>. For now, python-for-android just patches PyJNIus
before building it, but now that there are three different ways to get
the JNIEnv on Android this will need addressing somehow in PyJNIus itself.</p>
</div>
Kivy Android app showcase2016-01-15T23:00:00+01:002016-01-15T23:00:00+01:00Alexander Taylortag:inclem.net,2016-01-15:/2016/01/15/kivy/kivy_android_app_showcase/<p>A natural question when people hear about <a class="reference external" href="https://kivy.org/#home">Kivy</a> as a way to create Android apps in Python
is…what can you do with it? Is it performant enough for games, can
you call the Android APIs, do all apps look the same? One of the best
resources for these kinds …</p><p>A natural question when people hear about <a class="reference external" href="https://kivy.org/#home">Kivy</a> as a way to create Android apps in Python
is…what can you do with it? Is it performant enough for games, can
you call the Android APIs, do all apps look the same? One of the best
resources for these kinds of question are existing apps, and in this
post I’ll give a quick impression of three of my favourites. This is
obviously highly subjective, but I’m focusing in particular on
features of technical interest, apps that push Kivy beyond what’s
normal to show what it is capable of.</p>
<p>If you’re interested in other examples, there’s a fairly extensive
(but far from exhaustive) list on the <a class="reference external" href="https://github.com/kivy/kivy/wiki/List-of-Kivy-Projects">Kivy wiki</a>,
including winners of our programming contests and many contributions
from users. If you’d like to make your own apps in Python, check out <a class="reference external" href="https://kivy.org/#home">Kivy</a> (which also runs on Windows, Linux, <span class="caps">OS</span> X
and iOS) and <a class="reference external" href="http://python-for-android.readthedocs.org/en/latest/">python-for-android</a> (which can
also package non-Kivy Python apps).</p>
<div class="section" id="boardz">
<h2>Boardz</h2>
<p>You can download Boardz <a class="reference external" href="https://play.google.com/store/apps/details?id=org.chozabu.boardzfree">here</a>.</p>
<p>I’ve put Boardz first because it’s my single favourite Kivy app. It’s
actually a work in progress (and in fact hasn’t been updated for a
while), but is already a fun game showcasing some of Kivy’s more
impressive performance potential.</p>
<div class="figure align-center">
<img alt="Boardz homescreen and gameplay" src="https://inclem.net/media/kivy_screenshots/boardz_homescreen_input.png" />
<p class="caption">Boardz homescreen (left) and gameplay (right). The black ring on
the right is the input circle controlling rider posture.</p>
</div>
<p>Boardz is a snowboarding physics game; you control your snowboarder by
touching the screen, then moving your finger with respect to its
initial position to control your posture; quick movements
throw your weight around and can cause you to jump, spin, or fall
over, while just positioning the rider differently helps you to pick
up speed or navigate barriers. The objective of the game is to get to
the end of each stage, with different obstacles including
slopes and jumps, collapsing structures, falling rocks, or even
multidirectional gravity and rocket boosters. You can fail if your
head collides with another object with too much force, or if you
simply get stuck and can no longer reach the finish.</p>
<p>What’s immediately impressive is that all this runs well as a Python
powered game running on a smartphone. It achieves this by being built
using the <a class="reference external" href="http://kivent.org/">KivEnt game engine</a> developed by
Jacob Kovac, one of Kivy’s core developers. This entity based system
lets you write game code in Python but internally is highly optimised
in Cython, using Kivy’s OpenGL <span class="caps">API</span> extremely efficently as well as
interfacing with the popular <a class="reference external" href="https://chipmunk-physics.net/">Chipmunk Physics engine</a>.</p>
<div class="figure align-center">
<img alt="Boardz wipeout failure and ad example" src="https://inclem.net/media/kivy_screenshots/boardz_wipeout_ad.png" />
<p class="caption">Boardz wipeout failure by fatal collision (left), and an ad (right).</p>
</div>
<p>Boardz betrays its in-progress nature in other ways; you can see in
the above screenshots that its <span class="caps">UI</span> isn’t very polished, and in this
sense it’s the worst of the apps I’m showing here. However, it makes
up for this with its surprisingly engaging gameplay, and a breadth of
entertaining features not showcased here, including leader boards,
racing your ghost, and different riders with different physics attributes.</p>
<p>A final technical feature interesting to Kivy app developers is that
Boardz includes ad integration. Regardless of your feelings about ads
themselves, the ability to use them is a major feature enquiry from
new Kivy users. The problem here is that integrating with a normal ad
provider normally requires adding to the Java components of your app,
which it may not be immediately obvious how to do from Python. There
are actually a number of resources for this nowadays, with a key point
being that <a class="reference external" href="http://python-for-android.readthedocs.org/en/latest/">python-for-android</a> tries to make
it easy to include extra Java code, with which you can interact from
Python using <a class="reference external" href="https://pyjnius.readthedocs.org/en/latest/">Pyjnius</a>. KivEnt’s
implementation, pictured above, is a nice demonstration.</p>
</div>
<div class="section" id="kognitivo">
<h2>Kognitivo</h2>
<p>You can download Kognitivo <a class="reference external" href="https://play.google.com/store/apps/details?id=org.kognitivo.kognitivo">here</a>.</p>
<p>Kognitivo is perhaps the single most polished Kivy app on the Play
store, being relatively complex, extensively customised, and
exhibiting a number of nice Android <span class="caps">API</span> interactions. It is also
(deservedly) possibly the most popular Kivy app on Google Play.</p>
<div class="figure align-center">
<img alt="Kognitivo introduction and main screens" src="https://inclem.net/media/kivy_screenshots/kognitivo_intro_instructions_capacity.png" />
<p class="caption">Kognitivo tutorial (left), game instructions (centre) and
homescreen (right).</p>
</div>
<p>Kognitivo is a brain training and performance monitoring app. The
basic structure is to perform a series of simple exercises intended to
test different aspects of cognitive performance, being rated on
accuracy and speed, and with the results compiled over time in order
to detect and act on trends.</p>
<p>As I’ve said already, the nice thing about Kognitivo is its huge amount
of polish. It is extensively themed such that no trace of the Kivy
defaults remains, runs extremly smoothly, and includes many nice
animation tweaks (unfortunately not captured in screenshots) to feel
responsive and active.</p>
<div class="figure align-center">
<img alt="Kognitivo gameplay, Android notification and in-app purchase" src="https://inclem.net/media/kivy_screenshots/kognitivo_game_notification_iap.png" />
<p class="caption">Kognitivo training game (left), Android notification (centre) and
in-app purchase option (right).</p>
</div>
<p>On the technical side, Kognitivo exhibits a number of features not
normally included in Kivy applications but possible through
interaction with the Android <span class="caps">API</span> via some Java code and/or the
aforementioned <a class="reference external" href="https://pyjnius.readthedocs.org/en/latest/">Pyjnius</a>. These include notifications, interaction with
your calendar, and in-app purchases.</p>
<p>The author of Kognitivo, Sergey Cheparev, has his own writeup of
Kognitivo’s development <a class="reference external" href="http://cheparev.com/kognitivo-challenge-your-brain/">on his blog</a>, including
discussion of the advantages and disadvantages of Kivy development,
and of the experience of putting together all these features. This is
a great resource on its own; I don’t agree with some of the author’s
criticisms and some of Kivy’s features have been improved since then,
but it’s an excellent overview of the experience. Most of all, he
enthusiastically captures some of my own reasons for finding Python on
Android interesting:</p>
<blockquote>
I think the most beautiful thing in it is to use the almightiness
of [Python]’s frameworks. I used sqlalchemy and sqlite as a
backend, and it worked like a charm! Python is the most powerfull
language because of it’s frameworks, you can even start Django on
your smartphone! It’s amazing! Or twisted for asynchro
communication with server. Or nltk for in-app natural language
processing. Or maybe you want make a mobile equations solver with
scipy and numpy. This makes all the dreams come true.</blockquote>
</div>
<div class="section" id="barly">
<h2>Barly</h2>
<p>You can download Barly <a class="reference external" href="https://play.google.com/store/apps/details?id=org.topbanana.barly">here</a>,
or visit its <a class="reference external" href="http://www.barlyapp.com/">own website</a>.</p>
<p>Barly is the most recent of these apps to appear on Google Play. I’ve
chosen to include it as a nice example of pulling off its concept
quite well while making good use of Kivy; like Kognitivo the app is
themed very differently to Kivy’s defaults (though it doesn’t look
like a normal Android app either), and is generally well put together.</p>
<div class="figure align-center">
<img alt="Barly homescreen, palate options, and beer example" src="https://inclem.net/media/kivy_screenshots/barly_homescreen_palate_beer.png" />
<p class="caption">Barly homescreen (left), palate options (centre) and a beer search
result (right).</p>
</div>
<p>To quote its Google Play blurb, Barly is ‘your personal beer
expert’. It provides a convenient interface to browse beers via data
from standard popular websites, and according to your description of
your own palate. Barly’s most interesting feature is the ability to
take a picture of a beer menu and have it automatically detect what
beers are listed, followed by downloading information about them to
help you choose. That kind of image analysis has to be tricky, but
actually didn’t perform badly when I tested it.</p>
<p>The interface to this functionality is quite nice, switching to the
Android camera app to get the image before uploading it to a server
for processing (during which you can input your preferences). This
functionality is possible with <a class="reference external" href="https://pyjnius.readthedocs.org/en/latest/">Pyjnius</a> as mentioned previously, but
actually in this case is an <span class="caps">API</span> also exposed in pure Python by <a class="reference external" href="https://github.com/kivy/plyer">Plyer</a> (another Kivy sister project,
wrapping platform-specific APIs in a Python frontend). Not all APIs
can be conveniently exposed this way, and actually Barly may not even
be using this particular method, but it’s a good example of
functionality that can be achieved with Kivy in a particularly
cross-platform way.</p>
<p>Beyond this, Barly does not make such wide use of the Android <span class="caps">API</span> or
unusual Kivy features, but nor does it try to; it is a nice example of
a complete and self-contained Kivy app using the power of Python for
an unusual and interesting goal.</p>
</div>
python-for-android now supports Python 3 APKs2016-01-10T15:00:00+01:002016-01-10T15:00:00+01:00Alexander Taylortag:inclem.net,2016-01-10:/2016/01/10/kivy/python3_support_in_python_for_android/<p>It’s been a long time coming, but we can finally make the
announcement… python-for-android now supports Python 3 Android apps!
This naturally includes Kivy, but also should work for anything else
you can package with python-for-android, such as apps made with
PySDL2. Using Python 3 remains experimental for now …</p><p>It’s been a long time coming, but we can finally make the
announcement… python-for-android now supports Python 3 Android apps!
This naturally includes Kivy, but also should work for anything else
you can package with python-for-android, such as apps made with
PySDL2. Using Python 3 remains experimental for now, it works but
doesn’t yet perform all possible optimisations and hasn’t been as
widely tested as Python 2. However, there should be no extra
application requirements (beyond actually being written for Python 3),
and the remaining issues and optimisations are being worked on.</p>
<div class="section" id="overview">
<h2>Overview</h2>
<p><a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a> is
a packaging tool for turning Python applications into Android APKs. It
was originally created to make apps with the very cross-platform <a class="reference external" href="http://kivy.org/">Kivy
graphical framework</a> (though it didn’t arise in a
vacuum, I think it built in particular on previous work by the <a class="reference external" href="http://www.renpy.org/">Ren’Py project</a>). However, the original python-for-android
had flaws including being quite inconvenient to modify for non-Kivy
apps (several other projects seem to have used modified versions, but
each was performing similar changes), hard to extend for multiple
architecture support, hard to extend internally (both in general and
from the perspective of new contributors, as much of the toolchain was a
big shell script), and only supported building apps with Python 2.</p>
<p>We recently completed and released a fully revamped version of
python-for-android aimed at fixing all of these problems, as discussed
in several previous posts on this blog (originally <a class="reference external" href="https://inclem.net/2015/07/18/kivy/python_for_android_revamped/">here</a>). Almost all of the original
goals are now complete, with Python 3 support the major missing one
until now, though not for lack of trying. Some technical details and
basic instructions for testing the new support are given below, and
you can also see the <a class="reference external" href="http://python-for-android.readthedocs.org/en/latest/">online documentation</a> for further information.</p>
<p>Our Python 3 support depends on the prebuilt Python distributions
provided with the <a class="reference external" href="https://www.crystax.net/en">CrystaX <span class="caps">NDK</span></a>, a
drop-in replacement for Google’s own Android <span class="caps">NDK</span> with many fixes and
improvements. The technical details of this choice are given below,
and we’ll try to further support a locally-built Python 3 option in the
future. Thanks to the CrystaX team for making it so (relatively) easy!</p>
</div>
<div class="section" id="technical-details">
<h2>Technical details</h2>
<p>python-for-android works by bundling a Python interpreter, compiled
for Android devices and architectures (usually arm, though other
choices are supported), into an Android <span class="caps">APK</span>. The <span class="caps">APK</span> includes a simple
Java bootstrap application, which mostly starts a Python script via
<span class="caps">JNI</span>. The script then runs essentially as normal, almost all of the
Python standard library is present and works fine, and
python-for-android supports including other modules or non-Python
dependencies. Pure Python modules will mostly work without special
treatment, though things requiring compilation need a special
recipe. Many common modules such as numpy and sqlalchemy are supported
this way. Following its revamp python-for-android is now designed to
support multiple kinds of java bootstrap, but the current main support
is for <span class="caps">GUI</span> apps via Pygame (for Kivy’s old Android support) or <span class="caps">SDL2</span>
(both for Kivy and for anything else that can use it); <span class="caps">SDL2</span> also now
does much of the heavy lifting of handling events etc. itself, via its
own Android support.</p>
<p>The main problems with compiling and including Python are first that
it must be patched to compile (as Android’s libc doesn’t
support some things very well or at all), and second that it must be
unpacked and started on the device via its C <span class="caps">API</span>. The second point is
fiddly but ultimately not that different to working this way on the
desktop. The first is (in my opinion) harder because it needs some
understanding of Python’s internals, of Android’s limitations, of
appropriate fixes, and of how to test and debug these problems.</p>
<p>Such patches have been made by a number of different people for
different Android versions, and I believe there has been activity on
Python itself to fix some issues (including <a class="reference external" href="http://bugs.python.org/issue23496">this current issue</a> to make Python build natively
on Android). For Python 2, I think python-for-android’s original
patches came from <a class="reference external" href="http://randomsplat.com/id5-cross-compiling-python-for-embedded-linux.html">here</a>,
though extended with further modifications. However, the main thing
holding up my efforts to get Python 3 working was the inability to find
a similar working patchset; I tried a few sources, achieving a working
compilation using <span class="caps">SL4A</span>’s <a class="reference external" href="https://github.com/kuri65536/python-for-android/tree/master/python3-alpha">python3 patches</a>,
but I couldn’t get Python working on the device. I’m sure this was my
own technical mistakes, since other projects do have it working, but
it’s what was holding up the feature.</p>
<p>I eventually resolved this by using the new Python on Android support
from the <a class="reference external" href="https://www.crystax.net/en">CrystaX <span class="caps">NDK</span></a>. As mentioned
above, this is a drop-in replacement for Google’s own <span class="caps">NDK</span> (the Native
Development Kit providing compilers etc for targeting Android with
non-Java code), including many improvements to the build
environment. As of version 10.3.0, they provide prebuilt Python
packages for Android on all architectures - and all of their <span class="caps">NDK</span>
improvements mean that Python no longer even needs patching for this
compilation to work. Python is provided as a zipped standard library
(Python can automatically load modules from zip files), and a folder
of the compiled components like ctypes (as it’s hard to dynamically
load from zips). From the perspective of python-for-android,
supporting Python 3 means modifying the build to load CrystaX’s
prebuilt components (both in the Android project structure and in
python-for-android’s support for compiling other modules), and
modifying the C initialisation code for Python 3. This takes some
work, but all told wasn’t very hard and the Python bundles worked with
no issues, so we owe a lot to CrystaX; thanks again.</p>
<p>I’d still like to come back to the issue of local python3 compilation;
CrystaX’s versions are fine, but I’ve learned a lot from making them
work, and have a much better idea of what I may have been doing
wrong. However, the focus for now will be on resolving the remaining
issues with what’s already working.</p>
</div>
<div class="section" id="building-apps-with-python-3">
<h2>Building apps with Python 3</h2>
<p>Building Python 3 APKs is only supported in the revamped
python-for-android toolchain which was merged to the master branch a
while ago. It can be installed and used as described <a class="reference external" href="http://python-for-android.readthedocs.org/en/latest/quickstart/">in its
documentation</a>. If
you use Buildozer, it currently does not support this new toolchain,
though tito has been working on this. There is also the new
restriction that you must (for obvious reasons) use the CrystaX <span class="caps">NDK</span>,
which can be obtained <a class="reference external" href="https://www.crystax.net/en/download">here</a>;
simply refer to its filepath when setting the <span class="caps">NDK</span> directory, and
everything else should work automatically.</p>
<p>To build for Python 3, add the <tt class="docutils literal">python3crystax</tt> recipe to the
requirements option,
e.g. <code>--requirements=python3crystax,kivy</code>. It should mostly work
automatically with existing recipes, though at this stage there
may be bugs or problems with a few, and some will need
modification. The exact syntax above may also change in the future as
the Python 3 support becomes better integrated, but not significantly.</p>
<p>There’s also one big change whose importance I’m not sure about; the
Python 3 mechanism doesn’t currently build a local Python 3 to use as a
hostpython, instead using the system python. This means that you must
have python3.5 (3.4 may also work) installed locally in order for
python-for-android to build Python 3 APKs. This will be fixed soon,
adding a hostpython3 recipe to avoid weird bugs with system-specific
differences, but you need to bear it in mind for now.</p>
</div>
<div class="section" id="future-work">
<h2>Future work</h2>
<p>For now, this Python 3 support remains experimental. I anticipate no
major issues, but it’s internally a quite different method to the
Python 2 support and needs further work to duplicate some of the old
optimisations, and undoubtedly to fix bugs in the toolchain that will
appear as it stabilises. Amongst other things, Python 3 shared
libraries are not currently collected and merged (with Python 2 we did
this originally to get around an Android limit but also for
optimisation), python files are not precompiled to bytecode (it can
make a big loading speed difference), and some features of the old
pygame bootstrap have not yet been implemented in <span class="caps">SDL2</span>. All this and
more will come in the future, but shouldn’t be hard to add now that
the toolchain all works.</p>
<p>The <span class="caps">SDL2</span> bootstrap is also missing a few features that users of the
old toolchain will be used to, like the splash screen image and at
least one Kivy-specific function. These too are being actively worked
on, especially as more people start to move their apps to <span class="caps">SDL2</span>.</p>
<p>I’ve also phrased this as Python 2 built locally vs Python 3 from
CrystaX, but actually CrystaX also supports Python 2.7 and I hope to
add this option in the near future. As discussed in the technical
details, it also should absolutely be possible to have a local Python 3
build, which I’d like to eventually come back to.</p>
</div>
Kivy 1.9.1 released2016-01-02T13:45:00+01:002016-01-02T13:45:00+01:00Alexander Taylortag:inclem.net,2016-01-02:/2016/01/02/kivy/kivy_1_9_1_released/<p>We’ve just released a new stable version of Kivy, version 1.9.1. You
can see the changelog on the <a class="reference external" href="https://groups.google.com/forum/#!topic/kivy-users/7LTIHnRCuG4">mailing list announcement</a>,
and download the new version from the <a class="reference external" href="http://kivy.org/#download">Kivy website</a> or via your package manager.</p>
<p>This is mainly a bugfix and tidying release following the major
version …</p><p>We’ve just released a new stable version of Kivy, version 1.9.1. You
can see the changelog on the <a class="reference external" href="https://groups.google.com/forum/#!topic/kivy-users/7LTIHnRCuG4">mailing list announcement</a>,
and download the new version from the <a class="reference external" href="http://kivy.org/#download">Kivy website</a> or via your package manager.</p>
<p>This is mainly a bugfix and tidying release following the major
version 1.9 last year, but includes many bugfixes, smaller new
features, and improvements to our surrounding infrastructure across
almost 1000 new commits from over 70 different contributors.</p>
<p>One major improvement for Windows users is that we now have a fully
working installation method using pip and wheels for both Kivy and its
non-python binary dependencies, rather than our older standalone kivy
distribution. This should make it easy to install Kivy in any existing
Python installation. <span class="caps">OS</span> X distribution has also seen improvement,
including better support for working with homebrew.</p>
<p>We’ve also improved app packaging particularly on <span class="caps">OS</span> X, with a new
packaging method that should be easier than pyinstaller (though
pyinstaller is still supported), a buildozer backend for <span class="caps">OS</span> X
packaging (now buildozer works with Android, iOS <em>and</em> <span class="caps">OS</span> X!), and
generally improved and updated <a class="reference external" href="http://kivy.org/docs/guide/packaging-osx.html">documentation</a> for the packaging
process. The documentation for Windows and Linux packaging has
similarly been updated, and the new packaging methods and buildozer
support will hopefully be added for these in the future.</p>
<p>Packaging for Android with <a class="reference external" href="http://python-for-android.readthedocs.org/">python-for-android</a> is not tied to the
Kivy update schedule in the same way, but has been seeing significant
improvements and updates in the last few months, including a full
revamp of the toolchain and support for many new features, which you
can see in several of the recent previous posts on this blog.</p>
<p>In the future, we’re heading towards Kivy 2.0, which we’ve had in mind
for a while to be a major release with some big new features and
potentially removal of some long-deprecated components. We aren’t sure
on the timescale for this yet, but if it takes too long there will be
other minor releases first. For other updates, watch this blog or the
standard Kivy support channels.</p>
<p>Thanks to all our contributors, and enjoy the new release!</p>
python-for-android status update2015-12-06T14:00:00+01:002015-12-06T14:00:00+01:00Alexander Taylortag:inclem.net,2015-12-06:/2015/12/06/kivy/python_for_android_update/<p>It’s been a while since Kivy’s <a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android project</a> was revamped, so here’s
a quick status update.</p>
<p>There have since been well over 200 commits from 15 different
contributors, cleaning up the missing pieces of the new toolchain and
adding new features that weren’t previously possible. Thanks …</p><p>It’s been a while since Kivy’s <a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android project</a> was revamped, so here’s
a quick status update.</p>
<p>There have since been well over 200 commits from 15 different
contributors, cleaning up the missing pieces of the new toolchain and
adding new features that weren’t previously possible. Thanks to
everyone who has contributed.</p>
<p>These fixes include progress on the remaining major goals of the
python-for-android revamp. In particular, compilation is now supported
for multiple target architectures - in principle anything targeted by
the Android <span class="caps">SDK</span> (i.e. <span class="caps">ARM</span>, ARMv7a, x86, x86_64 and <span class="caps">MIPS</span> options),
though I’ve tested only with the <span class="caps">ARM</span> and x86 ones. This means that
Kivy applications, or other Python projects built with these tools,
can be built for devices with e.g. intel atom processors. Even without
this compilation it was often possible to run Kivy apps as many
devices include libhoudini, but directly targeting them means such
apps should now always work. A further advantage is that Kivy apps can
be built for and tested on the Android emulator, which was not
previously possible.</p>
<p>The architecture target support does need some more work to create
fully multiarch APKs (i.e. including .so files for each target, so a
single <span class="caps">APK</span> can work on different types of device). The problem here is
that we need to duplicate as little as possible, as Kivy APKs are
already made large by including the python interpreter, and it is
undesirable to include two or more copies of everything. Using a
single python installation and loading the .so dependencies as
appropriate should be possible but needs more work. However, this is
not a problem if uploading an <span class="caps">APK</span> to a store like Google Play; in this
case you can include multiple APKs, one for each arch target, and the
user will receive one that is appropriate.</p>
<p>Another important feature that I’ve worked on, but unfortunately
unsuccessfully so far, is support for python3 APKs. The problem to be
solved is to patch the interpreter to compile for Android (it cannot
do so by default, due to problems with the Android platform like poor
locale support), to modify the python-for-android bootstrap to load it
correctly (it builds things a little differently to python2), and to
modify the initialisation code to have it start successfully. I’ve
only partially succeeded with the first two of these; using <a class="reference external" href="https://github.com/kuri65536/python-for-android/tree/master/python3-alpha/patches">patches
from the <span class="caps">SL4A</span> python-for-android tools</a>
(plus extras for our own modifications to python loading) allows the
interpreter to be built, but it fails during Py_Initialize when run on
the device, apparently raising an exception when calculating the
python install path. Work on this will continue, but it’s hard to know
how long it might take to resolve this error. If you know of any other
projects patching python3.4+ for Android, I’d love to heard about it
to compare their methods.</p>
python-for-android revamp replaces master2015-10-04T21:00:00+02:002015-10-04T21:00:00+02:00Alexander Taylortag:inclem.net,2015-10-04:/2015/10/04/kivy/python_for_android_revamp_replaces_master/<p>This post is to announce that the revamped python-for-android
toolchain, introduced <a class="reference external" href="https://inclem.net/2015/07/18/kivy/python_for_android_revamped/">in this previous post</a>, has now been merged into
<a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android’s master branch</a>. This is now the
master branch going forward.</p>
<p>The revamp project is largely (but not quite) feature complete with
the old toolchain, supporting almost all the …</p><p>This post is to announce that the revamped python-for-android
toolchain, introduced <a class="reference external" href="https://inclem.net/2015/07/18/kivy/python_for_android_revamped/">in this previous post</a>, has now been merged into
<a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android’s master branch</a>. This is now the
master branch going forward.</p>
<p>The revamp project is largely (but not quite) feature complete with
the old toolchain, supporting almost all the same options when
building pygame-based APKs. It also supports a new and much better
<span class="caps">SDL2</span> backend, which Kivy will move to in the future, but which also
supports other kinds of python projects such as Vispy as described in
the previous post.</p>
<p>We’ve done our best to minimise problems arising from this change. The
old toolchain (with distribute.sh and build.py) is still available in
the <a class="reference external" href="https://github.com/kivy/python-for-android/tree/old_toolchain">old_toolchain branch</a>. Issues
and PRs relating to this branch are still accepted, though existing
PRs will need to be retargeted or merged manually (we’ll try to do
what’s easiest case by case, if necessary).</p>
<p>If you use buildozer, this now pulls from the old_toolchain branch and
so will work exactly as before. However, you will need to install the
latest version from pypi or github (at least version 0.30) for this to
work. Older versions of buildozer will continue to build APKs fine
with existing projects, but if you create a new one they will download
the new and incompatible python-for-android master. The revamp includes
a fake distribute.sh executable giving these same instructions, so if
this happens the problem and solution should be clearly displayed.</p>
<p>The new toolchain is currently documented (temporarily) <a class="reference external" href="http://inclem.net/files/p4a_revamp_doc/">here</a>. We’ll push the new
documentation to the <a class="reference external" href="http://python-for-android.rtfd.org">normal readthedocs site</a> as soon as possible, which
will also include the legacy doc for the old toolchain so nothing is lost.</p>
<p>In the slightly longer term, the new toolchain will receive an
official release and hopefully be released itself on pypi; unlike the
old toolchain, it behaves as a fully installable python module with an
improved command line interface.</p>
python-for-android revamped2015-07-18T00:15:00+02:002015-07-18T00:15:00+02:00Alexander Taylortag:inclem.net,2015-07-18:/2015/07/18/kivy/python_for_android_revamped/<p>I’ve recently been working on a significantly revamped version of
<a class="reference external" href="http://python-for-android.readthedocs.org/en/latest/">python-for-android</a>, the
<a class="reference external" href="http://kivy.org/#home">Kivy-project</a> tools that take a Python
program and package it - along with any dependencies and the Python
interpreter itself - into an Android <span class="caps">APK</span> that can be run and
distributed just like a normal Android app. This rewrite …</p><p>I’ve recently been working on a significantly revamped version of
<a class="reference external" href="http://python-for-android.readthedocs.org/en/latest/">python-for-android</a>, the
<a class="reference external" href="http://kivy.org/#home">Kivy-project</a> tools that take a Python
program and package it - along with any dependencies and the Python
interpreter itself - into an Android <span class="caps">APK</span> that can be run and
distributed just like a normal Android app. This rewrite is driven by
the problem that although the current python-for-android is fairly
robust as far as the build process goes, it’s quite set in its ways
and hard to modify to make large changes such as to support backends
other than Kivy.</p>
<p>To this end, the revamp project has several major goals:</p>
<ul class="simple">
<li>Rewrite python-for-android to a fully Python toolchain that can be
more easily modified and extended.</li>
<li>Support multiple bootstrap targets for different kinds of Python
scrips to run on Android, starting with a new <span class="caps">SDL2</span> backend for Kivy applications.</li>
<li>Support Python 3 on Android.</li>
<li>Support some kind of binary distribution, enabling
easier and cross-platform distribution…this should work on Windows!</li>
<li>Be a standalone PyPI module with a more convenient and standard
interface, potentially interfacing with setuptools.</li>
<li>Support (less painfully) multiple Android architectures, or fat APKs
supporting more than one.</li>
</ul>
<p>Each of these is individually a popularly requested feature, and this
is a great opportunity to go for all of them at once!</p>
<p>I’m making this post to announce that the python-for-android revamp
project has reached a usable state, with several of these goals either
implemented or significantly advanced, and all of them at least made
much more accessible. The core change is that all of the original
toolchain has been rewritten in Python, with the initial structure
based on the recent Kivy-iOS rewrite. It’s also much lighter, all
heavy pygame stuff is downloaded on demand instead of built in, and is
designed to be accessed by a single set of commands and the new
python-for-android executable rather than by the separate invocation
of different scripts in different places. I won’t go into the
technical details here, but you can find the (<span class="caps">WIP</span>) documentation
temporarily hosted <a class="reference external" href="http://inclem.net/files/p4a_revamp_doc/">here</a>. If anyone would like to
test it you can try the instructions there, but the project is in an
experimental state right now and it’s likely you may encounter bugs or
missing features, the current focus is ironing these out. I’m very
happy to discuss these on the <a class="reference external" href="https://groups.google.com/forum/#!forum/kivy-users">kivy-users mailing list</a> or #kivy irc
channel on irc.freenode.net.</p>
<p>Another of the core goals of this rewrite was to support multiple app
backends; in Kivy’s case in particular we want to move from Pygame to
a new <span class="caps">SDL2</span> bootstrap, but this could also include support for other
Python module backends. As of now, the new python-for-android supports
the old Pygame bootstrap mostly as before, but also implements an <span class="caps">SDL2</span>
based PythonActivity that works very well with Kivy - highly anecdotal
testing found, amongst other things, app start time reduced to 60% of
what it was with Pygame. It also simplifies maintenance as <span class="caps">SDL2</span>’s java
components fulfil the same role as those maintained in the Pygame
backend, but no longer require much special treatment as functionality
such as touch interaction and pausing are now accessed with the <span class="caps">SDL2</span>
api just as on desktop platforms.</p>
<p>Further, python-for-android can now build non-Kivy apps! The first
project with this support built in is the <a class="reference external" href="http://vispy.org/">Vispy scientific
visualisation library</a>. This uses the same new
<span class="caps">SDL2</span> backend but Kivy is not involved, and the build process does not
use Cython (unlike with Kivy); instead, <span class="caps">SDL2</span> and OpenGL are called
entirely with ctypes, using <a class="reference external" href="https://pysdl2.readthedocs.org/en/latest/">pysdl2</a> and Vispy’s own gloo
wrapper respectively. I didn’t even have to patch things much for
this, Vispy is mostly self contained and already quite platform
independent, barring a couple of possible small bugs and a hack to
avoid the lack of a supported font provider on Android. Vispy also
uses numpy heavily, but this was already supported by
python-for-android and seems to work fine. The Vispy support is itself
quite experimental and there are some performance issues that will
need resolving, but it was very simple to set up with the new
toolchain. Here’s a screenshot of one of Vispy’s 3D examples running
on Android - there are a few small visual artifacts, but I think these
are small bugs in Vispy’s OpenGL <span class="caps">ES</span> 2 support that the Vispy team are
actively addressing:</p>
<div class="figure align-center">
<img alt="Image of Vispy running on Android" src="https://inclem.net/media/vispy_android_example.png" />
</div>
<p>Support for binary distribution and multiple architectures are both
partially implemented but (at the time of writing) not yet
working. However, the toolchain is built around them, so there should
be no major issues. The initial idea with binary distribution will be
to simply make available a number of prebuilt distributions
(i.e. Android projects with the Python interpreter) that include
common dependencies, so that when the user adds modules as
requirements when calling python-for-android they can automatically be
checked and an appropriate choice downloaded, with this process being
transparent to the user and not requiring any special options. This
should not only make many builds faster but also work on Windows, one
of our most requested features but something that was not possible
when the toolchain required that everything be locally
compiled. Likewise, the toolchain has semi-implemented support for
multiple architectures internally, but none other than armeabi are yet
supported and there will be bugs to work out when more are
enabled. Still, these will (fingers crossed) be things to look forward
to in the relatively near future.</p>
<p>I should note here that this model of binary distribution is what I
initially targeted as a natural extension of python-for-android’s
distribution system - although we never made much of it, the first
step is already to build a standalone android package which later can
be distributed separately and populated with app details by a user,
and the above just involves making such prebuilt distributions
available to download and use automatically. I found more recently the
method of the pybee project/Toga toolkit’s <a class="reference external" href="https://github.com/pybee/Python-Android-template">Python-Android-template</a>. This is a
similar idea (built by a modified python-for-android) but implemented
much better as a standalone project with app details populated by
cookiecutter and the packaging of the user’s Python modules taken care
of using ant itself, the normal <span class="caps">APK</span> build and deployment interface - I
didn’t know this was even possible! This means that the user can just
write their Python code, drop it in place, and run the <span class="caps">APK</span> build, a
very neat process. It should be quite easy to modify
python-for-android’s dist output to easily create such templates, its
dist system is functionally the same thing with a different and less
standard-Android interface, and I hope that doing so could make it
even more convenient for users to build and distribute different kinds
of Python apps.</p>
<p>This leaves the elephant in the room, support for Python 3 on Android,
which is perhaps the most requested feature for Kivy itself. The new
toolchain makes significant progress on this in a couple of ways. The
first is by removing hardcoded use of Python 2, so that now (barring
remaining bugs) a Python 3 build should be well behaved once a recipe
and appropriate Android patch set are added. The second is that the
old Kivy Android bootstrap probably needed significant changes to
support Python 3, but this is sidestepped entirely by moving to the
new <span class="caps">SDL2</span> backend which should have no issues supporting Python3 more
or less the same as on the desktop. However, the missing link here is
still actually being able to compile Python 3 for Android, and I don’t
yet know how to do this. Some of Kivy’s main Python 2 patches come
from <a class="reference external" href="http://randomsplat.com/">this site</a>, but this has Python 3
patches only up to 3.2.2 and it would be ideal to target 3.4 or 3.5
(and to be able to support new versions as they appear). I’ve looked
around and seen a few different discussions of this, but I’m not sure
what’s the best direction to try. If anyone has any information about
places to find more up to date patch sets I would be very
interested. I can’t make any other predictions about this as I don’t
know much about the Python compilation process or what difficulties
might be involved in doing the work we need.</p>
<p>That’s all for now. I’ll note again that this is an initial
announcement of the new toolchain; I hope that people may be
interested to look and try it, and it should support most of what the
old toolchain does when it comes to compiling Pygame APKs, but there
are likely to be bugs and missing features particularly surrounding
(but not limited to) the new additions. If you’re interested in making
this toolchain work with other modules or backends, or just have any
questions, comments or criticisms, let us know! If you want to
keep informed, watch this space, I’ll make further announcements as
things develop. If there is developer interest and people start
switching from the old toolchain, I hope development will speed up a
lot and quickly approach proper feature parity.</p>
<p>tl;dr (I was told I should have one): Kivy’s python-for-android build
tools have been revamped to have a better interface, build apps based
on <span class="caps">SDL2</span>, build non-Kivy apps (currently Vispy apps), and be more
extensible. Further semi-complete features include binary
distribution, Windows support, and multiple architecture
targets. Python 3 is brought closer but needs direct compilation work.</p>
Kivy core developers interviewed on Podcast.__init__2015-05-01T23:20:00+02:002015-05-01T23:20:00+02:00Alexander Taylortag:inclem.net,2015-05-01:/2015/05/01/kivy/kivy_core_devs_interviewed/<p>Some of the Kivy core developers were recently interviewed on
<a class="reference external" href="http://podcastinit.com/episode-3-kivy-core-developers.html">Podcast.__init__</a>,
including discussion of how Kivy got started, the different things
it’s being used for, and the future of the project. Click the link to
listen to the podcast.</p>
<p>Some of the Kivy core developers were recently interviewed on
<a class="reference external" href="http://podcastinit.com/episode-3-kivy-core-developers.html">Podcast.__init__</a>,
including discussion of how Kivy got started, the different things
it’s being used for, and the future of the project. Click the link to
listen to the podcast.</p>
Python on Android2015-04-29T23:32:00+02:002015-04-29T23:32:00+02:00Alexander Taylortag:inclem.net,2015-04-29:/2015/04/29/kivy/python-on-android/<p>There are an increasing number of resources about different ways of
running Python on Android. Kivy (and its subprojects) are commonly
mentioned, as one of the most mature and popular ways to do so, but
one thing that gets less attention is the details of what you can do
with …</p><p>There are an increasing number of resources about different ways of
running Python on Android. Kivy (and its subprojects) are commonly
mentioned, as one of the most mature and popular ways to do so, but
one thing that gets less attention is the details of what you can do
with Python itself once it’s running on the device - what are the
limitations of this? Can we use any Python module? What about calling
Android APIs, can we perform all of the functions of a Java
application? These are all somewhat leading questions, they are things
addressed by Kivy or its associated projects, and in this post I’ll
summarise some of the most interesting and important details.</p>
<div class="section" id="python-for-android">
<h2>python-for-android</h2>
<p>Before anything else, let’s look quickly at the tool Kivy actually
uses to get Python on Android; the unimaginatively-named
<a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android project</a>. The basic
functionality of this tool is to first build a <em>distribution</em>, an
Android project directory that includes all the components Kivy needs
to run, compiled for Android by its <span class="caps">NDK</span>. This includes in particular
the Python interpreter itself, plus Kivy and the libraries it depends
on - currently Pygame and <span class="caps">SDL</span> amongst others, although we are working
to modernise this bit. The distribution also includes a Java
bootstrap, a normal app structure whose job is to display Kivy’s
OpenGL surface and to mediate between Kivy and Android. All these
components can then be bundled into an <span class="caps">APK</span> with the user’s Python
script and different settings (icon, name, orientation etc.) to taste.</p>
<p>This is only the basic procedure, the <span class="caps">APK</span> can (and does) include much
more than just these essentials. Amongst other things, most of the
Python standard library is built in by default, and pure Python
modules can be included easily so in general you can perform tasks
using just the same libraries you would on the desktop. Libraries with
compiled components are more complex, but can be built and included as
long as python-for-android has a compilation recipe for them (or you
provide your own) - these are often quite simple, just setting some
compilation flags and running the normal build procedure, although
some modules need additional patching. Python-for-android includes
quite a few recipes by default, including very popular modules like
numpy, sqlite3, twisted and even django!</p>
<p>The above is the basics of how python-for-android works but is far from
the whole story, and you can check the documentation for more
information about building your own APKs - in particular, we recommend
using <a class="reference external" href="https://github.com/kivy/buildozer">Buildozer</a>, which gives
python-for-android a more convenient interface and can manage some
dependencies (in particular the Android <span class="caps">SDK</span> and <span class="caps">NDK</span>)
automatically. This is also quite focused on Kivy itself, but we’re
trying to move to make it easier for other projects to use the same
toolchain - the core process of building and including Python should
be similar, but there’s no need for the bootstrap app at the end to
support only Kivy’s specific needs.</p>
</div>
<div class="section" id="calling-android-apis-with-pyjnius">
<h2>Calling Android APIs with PyJNIus</h2>
<p>In normal Android application development, interaction with the
Android <span class="caps">API</span> is an important part of how your app behaves - getting
sensor data, creating notifications, vibrating, pausing and
restarting, or just about anything else. Kivy takes care of the
essentials for you, but many of these are things you’ll still want to
manage yourself from Python. For this reason we have the <a class="reference external" href="https://github.com/kivy/pyjnius">PyJNIus</a> project, also developed under the
Kivy organisation, which automatically wraps Java code in a Python interface.</p>
<p>As a simple example, here’s the Python code to have an Android device
vibrate for 10s:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">jnius</span> <span class="kn">import</span> <span class="n">autoclass</span>
<span class="c1"># We need a reference to the Java activity running the current</span>
<span class="c1"># application, this reference is stored automatically by</span>
<span class="c1"># Kivy's PythonActivity bootstrap:</span>
<span class="n">PythonActivity</span> <span class="o">=</span> <span class="n">autoclass</span><span class="p">(</span><span class="s1">'org.renpy.android.PythonActivity'</span><span class="p">)</span>
<span class="n">activity</span> <span class="o">=</span> <span class="n">PythonActivity</span><span class="o">.</span><span class="n">mActivity</span>
<span class="n">Context</span> <span class="o">=</span> <span class="n">autoclass</span><span class="p">(</span><span class="s1">'android.content.Context'</span><span class="p">)</span>
<span class="n">vibrator</span> <span class="o">=</span> <span class="n">activity</span><span class="o">.</span><span class="n">getSystemService</span><span class="p">(</span><span class="n">Context</span><span class="o">.</span><span class="n">VIBRATOR_SERVICE</span><span class="p">)</span>
<span class="n">vibrator</span><span class="o">.</span><span class="n">vibrate</span><span class="p">(</span><span class="mi">10000</span><span class="p">)</span> <span class="c1"># the argument is in milliseconds</span>
</pre></div>
<p>If you’re familiar with the Android <span class="caps">API</span>, you’ll notice that this is
very similar to the Java code you’d use for the same task; PyJNIus
just lets us call the same <span class="caps">API</span> directly from Python. Most of the
Android <span class="caps">API</span> can be called from Python in the same way, letting you
achieve the same things as a normal Java application.</p>
<p>The main disadvantages of using PyJNIus directly are that it requires
some understanding of how the Android <span class="caps">API</span> is structured, and that it
is quite verbose - though the latter just reflects the nature of the
equivalent Java code. For this reason, the Kivy project set includes
<em>Plyer</em>.</p>
</div>
<div class="section" id="plyer-a-platform-independent-api-for-platform-specific-features">
<h2>Plyer: A platform-independent <span class="caps">API</span> for platform-specific features</h2>
<p>The <a class="reference external" href="https://github.com/kivy/plyer">Plyer</a> project takes a step
back from the specific implementation details of individual platforms
in order to try to create a simple, pythonic interface for a subset of
(mostly) shared functionality. For instance, the vibration example
above would become</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">plyer.vibrator</span> <span class="kn">import</span> <span class="n">vibrate</span>
<span class="n">vibrate</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="c1"># in Plyer, the argument is in seconds</span>
</pre></div>
<p>Further, Plyer is not just for Android but would try to do something
appropriate on any of its supported platforms - currently Android,
iOS, Linux, Windows and <span class="caps">OS</span> X (on iOS, <a class="reference external" href="https://github.com/kivy/plyer">PyOBJus</a> fulfils a similar role to PyJNIus
on Android). The vibrator is actually a bad example as only Android is
currently implemented, but other APIs such as checking the battery
(<code>from plyer import battery; print(battery.status)</code>) or
text-to-speech (<code>from plyer import tts; tts.speak('hello
world')</code>) would already work on both desktop and mobile devices, and
others such as the compass or gyroscope sensors or sending <span class="caps">SMS</span>
messages would work on both Android and iOS.</p>
<p>Plyer is very much under development, with new <span class="caps">API</span> wrapper
contributions very welcome, and is the subject of a (second) GSoC
project this year. We hope that it will become increasingly feature-complete.</p>
</div>
<div class="section" id="not-just-for-kivy">
<h2>Not just for Kivy</h2>
<p>All of these tools have been shaped in their current form by the needs
of Kivy, but are really more generic Python tools; Plyer specifically
avoids any Kivy dependency, and PyJNIus only makes an assumption about
how to access the <span class="caps">JNI</span> environment on Android. We hope that these tools
can be more generally useful to anyone running Python on Android; for
instance, you can already experiment with PyJNIus using the <a class="reference external" href="https://play.google.com/store/apps/details?id=com.hipipal.qpyplus">QPython
Android app</a>. Python-for-android
is more tied to Kivy’s current toolchain but this is a detail under
review, and we’re happy to discuss the details of Android compilation
with anyone interested.</p>
<p>Overall, a lot is possible with Python on Android, despite how
different the Python environment is to the Java development that is
directly targeted. But there’s much more that could be done - if
you’re interested, now is a great time to dive in!</p>
</div>
Kivy 1.9 released2015-04-02T20:20:00+02:002015-04-02T20:20:00+02:00Alexander Taylortag:inclem.net,2015-04-02:/2015/04/02/kivy/kivy_1.9_released/<p>Kivy 1.9 has just been released! This has been a long time in the
making, for no very good reason, but now you can take advantage of all
our many new features in the stable branch. You can find the full
changelog at the official <a class="reference external" href="https://groups.google.com/forum/#!topic/kivy-users/PZpI1g-W3do">mailing list announcement</a>.</p>
<p>This …</p><p>Kivy 1.9 has just been released! This has been a long time in the
making, for no very good reason, but now you can take advantage of all
our many new features in the stable branch. You can find the full
changelog at the official <a class="reference external" href="https://groups.google.com/forum/#!topic/kivy-users/PZpI1g-W3do">mailing list announcement</a>.</p>
<p>This big release includes almost 2500 new commits (about 30% of the
total in Kivy!) from nearly 100 different contributors, including both
significant changes and many smaller fixes. I’ll showcase a few of
the most interesting ones below; these are also listed in the
changelog above, along with more information about the many other changes.</p>
<p>One of the most major internal changes is a shift to using <span class="caps">SDL2</span> as our
window and other backend provider on almost all platforms - only
Android still uses the old pygame/<span class="caps">SDL</span> backend. This shouldn’t change
the external user <span class="caps">API</span> at all, but directly makes available features
that Pygame lacked such as proper support for high-<span class="caps">DPI</span> displays and
the ability to retain an OpenGL context on resize (previously lacking
in Windows and <span class="caps">OS</span> X), as well as resolving some old Pygame related
bugs and hopefully making further low level customisation easier in
the future. Although this doesn’t change at all how you interact with
Kivy, it’s a big improvement behind the scenes. This also means that
Pygame is now deprecated on platforms where <span class="caps">SDL2</span> already works; we’ll
continue to support it for a while and it’s unlikely to stop working
even after that, but it’s no longer a focus.</p>
<div class="figure align-center">
<img alt="Image of Kivy on a retina display with high DPI mode" src="https://inclem.net/media/kivy_retina.png" />
</div>
<div class="line-block">
<div class="line"><br /></div>
</div>
<div class="figure align-center">
<img alt="Image of Kivy on a retina display without high DPI mode" src="https://inclem.net/media/kivy_nonretina.png" />
</div>
<div class="line-block">
<div class="line"><br /></div>
</div>
<p>These images show the difference in Kivy rendering on the same (<span class="caps">OS</span> X
retina) screen, first with the new <span class="caps">SDL2</span> high <span class="caps">DPI</span> mode enabled so that
Kivy has full awareness of the true resolution, and second letting the
operating system scale up a smaller rendered result - the latter is
default for applications that do not declare <span class="caps">DPI</span> awareness, but Kivy
will now always render properly as in the first image. The
difference is dramatic, and we’re glad to be able to properly support
these resolutions. This improvement is currently enabled only on <span class="caps">OS</span> X,
but the equivalent Windows fix will be merged shortly and the
behaviour should already be correct on Linux.</p>
<p>A different change that may be more directly useful in your
applications is the new EffectWidget, which behaves as a normal
RelativeLayout but also lets you add one or more shader effects to its
rendered output. The <span class="caps">API</span> is designed to make it very easy to create
simple effects even without knowing about <span class="caps">GLSL</span>, in a way that can
easily be combined with existing applications.</p>
<div class="figure align-center">
<img alt="Image of Kivy effectwidget" src="https://inclem.net/media/effectwidget_example.png" />
</div>
<div class="line-block">
<div class="line"><br /></div>
</div>
<p>This above screenshot demonstrates the EffectWidget via one of the new
Kivy examples; the kv code of the left and right is identical,
except the right hand side includes colour mixing and pixelation
effects. Since these are applied at a very low level they are very
efficient (although not optimised for too many effects at once) and
can be applied even to video or moving scenes such as in games.</p>
<div class="figure align-center">
<img alt="Image of the Kivy SVG example, including the famous svg tiger" src="https://inclem.net/media/kivy_svg_example.png" />
</div>
<div class="line-block">
<div class="line"><br /></div>
</div>
<p>One feature that has been heavily requested by users is <span class="caps">SVG</span> support
for Kivy, and preliminary support is included in 1.9! This is still
experimental and currently supports only Python 2, but much of the
work has been done and even complex <span class="caps">SVG</span> images are reproduced
well. The above image shows one of the new <span class="caps">SVG</span> examples, including the
famous tiger.</p>
<p>There are also some nice new features that can’t be captured so easily
in a screenshot. One is the addition of a <tt class="docutils literal">rebind</tt> option to Kivy
properties. This resolves a problem that arose with code like</p>
<div class="highlight"><pre><span></span><span class="c1"># In python</span>
<span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">ObjectProperty</span>
<span class="k">class</span> <span class="nc">MyButton</span><span class="p">(</span><span class="n">Button</span><span class="p">):</span>
<span class="n">some_ref</span> <span class="o">=</span> <span class="n">ObjectProperty</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">rebind</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="c1"># And in kv language</span>
<span class="o"><</span><span class="n">MyButton</span><span class="o">></span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">some_ref</span><span class="o">.</span><span class="n">text</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">some_ref</span> <span class="k">else</span> <span class="s1">''</span>
</pre></div>
<p>The problem here was that kv could only bind automatically to the
first value of self.some_ref, so the text of the <tt class="docutils literal">MyButton</tt> instance
would never update, and it is difficult to improve this internally
without dramatic slowdowns from checking if many objects have
changed. The new rebind option makes it possible to enable this second
level of binding in select places where appropriate; it won’t be
necessary or useful to everyone, but it’s a convenient feature when
really necessary.</p>
<p>Other new features include a new, faster video provider via Cython
and ffmpeg, Window modes to detect and react to the presence of a
software keyboard on Android, an internal rewiring of focus handling
for widgets, and many many other bugfixes and smaller new features.</p>
<p>Thanks to all our contributors, and enjoy the new release!</p>
Kivy in GSOC 20152015-03-12T20:25:00+01:002015-03-12T20:25:00+01:00Alexander Taylortag:inclem.net,2015-03-12:/2015/03/12/kivy/kivy_gsoc_2015/<p>Kivy will be participating in the <a class="reference external" href="https://www.google-melange.com/gsoc/homepage/google/gsoc2015">Google Summer of Code 2015</a>
(<span class="caps">GSOC</span>), under the <a class="reference external" href="https://wiki.python.org/moin/SummerOfCode/2015">Python Software Foundation umbrella</a>. Applications are
welcomed not just for the Kivy framework itself but on all the
projects managed by the Kivy organisation including
Python-for-Android, Kivy-iOS, PyJNIus, PyOBJus, Plyer and Buildozer.
As such, <span class="caps">GSOC</span> projects …</p><p>Kivy will be participating in the <a class="reference external" href="https://www.google-melange.com/gsoc/homepage/google/gsoc2015">Google Summer of Code 2015</a>
(<span class="caps">GSOC</span>), under the <a class="reference external" href="https://wiki.python.org/moin/SummerOfCode/2015">Python Software Foundation umbrella</a>. Applications are
welcomed not just for the Kivy framework itself but on all the
projects managed by the Kivy organisation including
Python-for-Android, Kivy-iOS, PyJNIus, PyOBJus, Plyer and Buildozer.
As such, <span class="caps">GSOC</span> projects can range in focus and difficulty from those
that should be accessible to intermediate Python users to low level
work making use of Cython, or interacting with Java and Objective C on
mobile platforms.</p>
<p>Our page of <span class="caps">GSOC</span> information and suggested projects is available <a class="reference external" href="http://kivy.org/docs/gsoc.html">here</a> and includes ideas touching all of
these areas. However, these are just guidelines and suggestions; if
you have a different idea, we’re happy to discuss it. Prospective
<span class="caps">GSOC</span> students should introduce themselves on the <a class="reference external" href="https://groups.google.com/forum/#!forum/kivy-dev">kivy-dev mailing
list</a>, and also
say hello on our <a class="reference external" href="http://kivy.org/docs/contact.html#irc"><span class="caps">IRC</span> channel</a>, discussions there will be
an important part of any application.</p>
<p>Beyond that, check out the project page linked above, and good luck
with your applications.</p>
Hy (lisp) and Kivy2014-10-15T22:28:00+02:002014-10-15T22:28:00+02:00Alexander Taylortag:inclem.net,2014-10-15:/2014/10/15/kivy/hy_and_kivy/<p>I was recently reminded of the super cool <a class="reference external" href="http://hy.readthedocs.org/en/latest/">Hy</a> project. Hy is a lisp that
compiles to python’s own abstract syntax tree, so it works perfectly
with existing Python code (including with Cython etc.) but also
exposes all the power of lisp.</p>
<p>For instance, here’s a simple Kivy …</p><p>I was recently reminded of the super cool <a class="reference external" href="http://hy.readthedocs.org/en/latest/">Hy</a> project. Hy is a lisp that
compiles to python’s own abstract syntax tree, so it works perfectly
with existing Python code (including with Cython etc.) but also
exposes all the power of lisp.</p>
<p>For instance, here’s a simple Kivy application that simply displays a
Label with the obligatory Hy pun, but written in Hy. I’ve included the
normal Python code as comments so you can see exactly what the code is
doing. If you’re new to Kivy and want to understand what the code
actually does, check out my <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">Kivy crash course</a>:</p>
<div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">import</span> <span class="nv">[kivy.app</span> <span class="nv">[App]]</span>
<span class="nv">[kivy.uix.label</span> <span class="nv">[Label]]</span><span class="p">)</span>
<span class="c1">;; from kivy.app import App</span>
<span class="c1">;; from kivy.uix.label import Label</span>
<span class="p">(</span><span class="nb">defclass</span> <span class="nv">HyApp</span> <span class="nv">[App]</span>
<span class="nv">[[build</span>
<span class="p">(</span><span class="nv">fn</span> <span class="nv">[self]</span>
<span class="p">(</span><span class="nb">apply</span> <span class="nv">Label</span> <span class="nv">[]</span> <span class="nv">{</span><span class="s">"text"</span> <span class="s">"Hy world!"</span>
<span class="s">"font_size"</span> <span class="mi">100</span>
<span class="s">"color"</span> <span class="p">(</span><span class="o">,</span> <span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span><span class="p">)</span><span class="nv">}</span><span class="p">))</span><span class="nv">]]</span><span class="p">)</span>
<span class="c1">;; class HyApp(App):</span>
<span class="c1">;; def build(self):</span>
<span class="c1">;; return Label(text="Hy world!",</span>
<span class="c1">;; font_size=100,</span>
<span class="c1">;; color=(0, 1, 0, 1))</span>
<span class="p">(</span><span class="o">.</span><span class="nv">run</span> <span class="p">(</span><span class="nv">HyApp</span><span class="p">))</span>
<span class="c1">;; HyApp().run()</span>
</pre></div>
<p>This works great, though only with python3 due to a small bug in
Kivy - the kwargs of Label are eventually read in cython with a
variable typed as <tt class="docutils literal">str</tt>, which in python2 excludes the unicode Hy
passes. Still, that’s not surprising even if it’s cool - part of the
point of Hy is to interoperate perfectly with Python.</p>
<p>A tougher problem is how to use Kivy’s kv language with Hy. kv is a
simple domain-specific language for declaring widget trees, making it
easy to define event-driven interactions between the different
properties of widgets. It’s really useful and we tend to recommend
using it as much as possible, so it’d be great to have it work with Hy.
I won’t explain the language here (you can see the <a class="reference external" href="http://kivy.org/docs/guide/lang.html">Kivy doc</a> or my own tutorials), but
the key point is that much of it consists of interpreting normal
python code, which I’d like to replace with Hy code.</p>
<p>It turns out making this work is actually really easy. Here’s the
relevant part of <tt class="docutils literal">lang.py</tt> in Kivy’s source, the file containing the
code for the kv parser:</p>
<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">co_value</span> <span class="o">=</span> <span class="nb">compile</span><span class="p">(</span><span class="n">value</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">filename</span> <span class="ow">or</span> <span class="s1">'<string>'</span><span class="p">,</span>
<span class="n">mode</span><span class="p">)</span>
</pre></div>
<p><tt class="docutils literal">value</tt> is the string of Python code whose output will set a
property of a widget or be run when an event is registered. For
instance, a line of kv code might be <tt class="docutils literal">color: (1, 0, 0,
some_function_of(self.alpha))</tt>, in which case <tt class="docutils literal">value</tt> would be
<tt class="docutils literal">"(1, 0, 0, <span class="pre">some_function_of(self.alpha))"</span></tt>.</p>
<p>To make a line of Hy code work instead of Python, we can do an awful
hack, replacing the above line with:</p>
<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">value</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">:]</span> <span class="o">==</span> <span class="s1">'#hy'</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">hy.importer</span> <span class="kn">import</span> <span class="p">(</span><span class="n">ast_compile</span><span class="p">,</span>
<span class="n">import_buffer_to_ast</span><span class="p">)</span>
<span class="kn">from</span> <span class="nn">hy.compiler</span> <span class="kn">import</span> <span class="n">hy_compile</span>
<span class="kn">import</span> <span class="nn">ast</span>
<span class="n">ast_part</span> <span class="o">=</span> <span class="n">import_buffer_to_ast</span><span class="p">(</span><span class="n">value</span><span class="p">[:</span><span class="o">-</span><span class="mi">3</span><span class="p">],</span> <span class="s1">'<stdin>'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">mode</span> <span class="o">==</span> <span class="s1">'eval'</span><span class="p">:</span>
<span class="n">ast_part</span> <span class="o">=</span> <span class="n">ast</span><span class="o">.</span><span class="n">Expression</span><span class="p">(</span><span class="n">ast_part</span><span class="o">.</span><span class="n">body</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">co_value</span> <span class="o">=</span> <span class="n">ast_compile</span><span class="p">(</span><span class="n">ast_part</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">filename</span> <span class="ow">or</span> <span class="s1">'<string>'</span><span class="p">,</span>
<span class="n">mode</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">co_value</span> <span class="o">=</span> <span class="nb">compile</span><span class="p">(</span><span class="n">value</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">filename</span> <span class="ow">or</span> <span class="s1">'<string>'</span><span class="p">,</span>
<span class="n">mode</span><span class="p">)</span>
</pre></div>
<p>This new code checks if the line of Python ends with <tt class="docutils literal">#hy</tt>, and if
so runs the code through Hy’s own equivalent of <tt class="docutils literal">compile</tt>
(effectively parsing the Hy code to ast before doing the same thing as
the normal Python code). I also have the extra muckiness of taking
apart this ast if the compilation is in <tt class="docutils literal">eval</tt> mode, because I
couldn’t get Hy to return an <tt class="docutils literal">ast.Expression</tt> in the first place.
This is probably very easily and neatly fixed, but I’ve left it like
this because a silly hack is good enough for a proof of concept. All
credit for this part goes to the friendly Hy people on their irc
channel, #hy on Freenode.</p>
<p>With this in place, we can write a new Python program, but this time
use our Hy+kv language to define the widget tree. Here’s the new
code on the Python (now Hy) side:</p>
<div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">import</span> <span class="nv">[kivy.app</span> <span class="nv">[App]]</span>
<span class="nv">[kivy.lang</span> <span class="nv">[Builder]]</span><span class="p">)</span>
<span class="c1">;; from kivy.app import App</span>
<span class="c1">;; from kivy.lang import Builder</span>
<span class="p">(</span><span class="nv">setv</span> <span class="nv">root</span> <span class="p">(</span><span class="nv">Builder.load_file</span> <span class="s">"hy.kv"</span><span class="p">))</span>
<span class="c1">;; root = Builder.load_file("hy.kv")</span>
<span class="p">(</span><span class="nb">defclass</span> <span class="nv">HyApp</span> <span class="nv">[App]</span>
<span class="nv">[[build</span>
<span class="p">(</span><span class="nv">fn</span> <span class="nv">[self]</span>
<span class="nv">root</span><span class="p">)</span><span class="nv">]]</span><span class="p">)</span>
<span class="c1">;; class HyApp(App):</span>
<span class="c1">;; def build(self):</span>
<span class="c1">;; return root</span>
<span class="p">(</span><span class="o">.</span><span class="nv">run</span> <span class="p">(</span><span class="nv">HyApp</span><span class="p">))</span>
<span class="c1">;; HyApp().run()</span>
</pre></div>
<p>This obviously depends on our new kv file, “hy.kv”, whose contents are
as below. Kivy users will notice this file would normally be loaded
automatically because the app name starts with <tt class="docutils literal">Hy</tt>, but something
about Hy seems to have broken this so I manually loaded it with the Builder.</p>
<div class="highlight"><pre><span></span><span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">orientation</span><span class="p">:</span> <span class="s2">"vertical"</span>
<span class="n">Label</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">label</span>
<span class="n">text</span><span class="p">:</span> <span class="s2">"What is your name?"</span>
<span class="n">TextInput</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">ti</span>
<span class="n">text</span><span class="p">:</span> <span class="s2">""</span>
<span class="n">Button</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="p">(</span><span class="o">.</span><span class="n">format</span> <span class="s2">"Greet me as {}"</span> <span class="n">ti</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> <span class="c1">#hy</span>
<span class="n">on_press</span><span class="p">:</span> <span class="p">(</span><span class="n">setv</span> <span class="n">label</span><span class="o">.</span><span class="n">text</span> <span class="p">(</span><span class="o">.</span><span class="n">format</span> <span class="s2">"Hy there {}"</span> <span class="n">ti</span><span class="o">.</span><span class="n">text</span><span class="p">))</span> <span class="c1">#hy</span>
<span class="c1"># as normal kv, except the final 2 rules would normally be:</span>
<span class="c1"># text: "Greet me as {}".format(ti.text)</span>
<span class="c1"># on_press: label.text = "Hy there {}".format(ti.text)</span>
</pre></div>
<p>Running the code…it works perfectly! Here’s a picture after typing
my name and clicking the button:</p>
<img alt="Image of Kivy program after running Hy code" src="https://inclem.net/media/hy_example.png" />
<p>For those not familiar with kv, one of its features is that it
automatically detects property changes and updates dependent
properties - in this case, the text of the button should change every
time <tt class="docutils literal">ti.text</tt> changes (i.e. every time a letter is typed in the
TextInput). This works too with the new Hy interface, because the
parser detects the dependency by searching the string for substrings
like <tt class="docutils literal">ti.text</tt>, and these have been unmodified by the move to Hy. Hy
does support syntax that would break this relationship, but it’s quite
convenient as it is.</p>
<p>So…there we go, Hy support in Kivy! The hack to make kv language
work is pretty terrible, but it looks like a proper solution with this
basis would work fine - we could subclass the kv parsing Builder
to support a Hy loading option, removing the need for the <tt class="docutils literal">#hy</tt> at
the end of each Hy line.</p>
Updating canvas instructions declared in Python2014-10-10T23:46:00+02:002014-10-10T23:46:00+02:00Alexander Taylortag:inclem.net,2014-10-10:/2014/10/10/kivy/kivy_update_instructions/<p>Continuing the theme of my last few posts, a common problem for new
kivy users is creating canvas instructions that follow their parent
widgets. For instance, here’s some code for a custom widget that tries
to draw a red rectangle in its upper-right corner - this is fairly
standard kivy …</p><p>Continuing the theme of my last few posts, a common problem for new
kivy users is creating canvas instructions that follow their parent
widgets. For instance, here’s some code for a custom widget that tries
to draw a red rectangle in its upper-right corner - this is fairly
standard kivy code to draw directly on the widget canvas, and is
documented <a class="reference external" href="http://kivy.org/docs/api-kivy.graphics.html">here</a>.</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span>
<span class="k">class</span> <span class="nc">CornerRectangleWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">CornerRectangleWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># set the colour to red</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">center</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">width</span><span class="o">/</span><span class="mf">2.</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">height</span><span class="o">/</span><span class="mf">2.</span><span class="p">))</span>
</pre></div>
<p>This looks like it will create a red rectangle, whose length is half the
parent size in both directions, and whose position is the parent centre.</p>
<p>The surprise is that this is actually not what happens. Instead, the
rectangle is always positioned at (50, 50) with size (50, 50),
regardless of where the widget appears.</p>
<p>The reason for this is that these values really are based on the pos
and size of the widget at the point where the canvas code was run; all
widgets have a default position of (0, 0) and size of (100, 100), and
this will not necessarily be updated (for instance by a parent layout
class) until after their <tt class="docutils literal">__init__</tt> is run. However, the Rectangle
properties receive only these initial values, and don’t know about the
new position of the widget.</p>
<p>The solution is to simply hook into kivy’s event system to update the
rectangle pos and size ourselves whenever the widget changes:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">from</span> <span class="nn">kivy.graphics</span> <span class="kn">import</span> <span class="n">Rectangle</span><span class="p">,</span> <span class="n">Color</span>
<span class="k">class</span> <span class="nc">CornerRectangleWidget</span><span class="p">(</span><span class="n">Widget</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">CornerRectangleWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># set the colour to red</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">center</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">width</span><span class="o">/</span><span class="mf">2.</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">height</span><span class="o">/</span><span class="mf">2.</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">pos</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rect</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">update_rect</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_rect</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
</pre></div>
<p>Now whenever the widget <tt class="docutils literal">pos</tt> or <tt class="docutils literal">size</tt> changes, our new method is
called and the rectangle resized or repositioned as necessary. It will
always track the widget’s upper right corner, so we get the visual
effect we were originally looking for.</p>
<p>Of course, an even better solution (where possible) is to use kv language:</p>
<div class="highlight"><pre><span></span><span class="o"><</span><span class="n">CornerRectangleWidget</span><span class="nd">@Widget</span><span class="o">></span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Color</span><span class="p">:</span>
<span class="n">rgba</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">center</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">width</span> <span class="o">/</span> <span class="mf">2.</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">height</span> <span class="o">/</span> <span class="mf">2.</span>
</pre></div>
<p>This is shorter, simpler and clearer. We don’t need to manually set up the
binding because kv automatically detects that we referred to
properties of the parent widget and creates it automatically -
something that isn’t really possible in python. This is
one reason that we recommend using kv language wherever possible.</p>
Wrapping text in Kivy’s Label2014-07-05T23:14:00+02:002014-07-05T23:14:00+02:00Alexander Taylortag:inclem.net,2014-07-05:/2014/07/05/kivy/kivy_label_text/<p>Another Kivy question that I often see (particularly recently for some
reason) is about using the Label widget - how to have text wrap
automatically, or the opposite, how to have the label automatically
grow to accommodate its text. I’ve covered this before in the 9th
<a class="reference external" href="https://www.youtube.com/watch?v=WdcUg_rX2fM">Kivy crash course video …</a></p><p>Another Kivy question that I often see (particularly recently for some
reason) is about using the Label widget - how to have text wrap
automatically, or the opposite, how to have the label automatically
grow to accommodate its text. I’ve covered this before in the 9th
<a class="reference external" href="https://www.youtube.com/watch?v=WdcUg_rX2fM">Kivy crash course video</a>, but here’s a quick
write up of the basics.</p>
<p>The first thing to realise is how the Label works by default, it takes
the text and <em>draws</em> it to a texture - in practical terms that’s an
image of the characters. Everything you might want to do with the
Label revolves around what this texture is really doing. By default,
it does <em>not</em> wrap the text (unless you put in linebreak characters
manually), it just makes one long image on a single row. This image
is is placed right in the middle of the label, centered in both
directions, which is fine for short text snippets but will
overhang the Label on both sides if the text is too long.</p>
<p>This also leads to some other annoying behaviour - as well as the
text not wrapping, you might have observed that the halign and valign
properties seem to do nothing by default. This is because they orient
things not inside the widget, but inside the texture…which
is the exact size it needs to contain the text so alignments change nothing.</p>
<p>To solve all these problems, you can manually set the size of the
texture with <code>text_size</code>, a tuple of width and height, e.g.</p>
<div class="highlight"><pre><span></span><span class="n">Label</span><span class="p">:</span>
<span class="n">text_size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
</pre></div>
<p>This reverses the default behaviour - instead of the texture growing
to fit the text, the text will be wrapped to fit the texture! If there
is space to spare, it is aligned within the texture according to the
<code>halign</code> and <code>valign</code> properties.</p>
<p>The Label also has another useful property, the <code>texture_size</code>,
which holds the <em>actual</em> size of the texture. You can use that do bind
behaviour to the size of the text. For instance, a common requirement
is to create a Label that grows as long as it needs to contain its
text, but which wraps it to a certain width. We can combine both of
the above ideas to accomplish this:</p>
<div class="highlight"><pre><span></span><span class="n">Label</span><span class="p">:</span>
<span class="n">size_hint_y</span><span class="p">:</span> <span class="bp">None</span>
<span class="n">text_size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="bp">None</span>
<span class="n">height</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">texture_size</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</pre></div>
<p>If you (for instance) place this label in a ScrollView, it will be
Scrollable over exactly the right distance to fit in all the text.</p>
Kivy Contest 20142014-04-07T18:19:00+02:002014-04-07T18:19:00+02:00Alexander Taylortag:inclem.net,2014-04-07:/2014/04/07/kivy/kivy_contest_2014/<p>Just to announce here for anyone that hasn’t seen already…Kivy
recently announced the Kivy org second programming contest! You can
check out all the details at <a class="reference external" href="http://kivy.org/#contest">http://kivy.org/#contest</a>!</p>
<p>To cover the key details here, entries are open now (you can sign up
at the link above …</p><p>Just to announce here for anyone that hasn’t seen already…Kivy
recently announced the Kivy org second programming contest! You can
check out all the details at <a class="reference external" href="http://kivy.org/#contest">http://kivy.org/#contest</a>!</p>
<p>To cover the key details here, entries are open now (you can sign up
at the link above), and the contest starts in full on 15th
April. There will be a broad theme, announced on that day, but many
different kinds of app will be possible. You’ll have 4 weeks from the
start date to complete your entry.</p>
<p>Entries will be judged on a range of criteria, so don’t be afraid to
jump in!</p>
New Kivy Crash Course video - 14: Using a ScreenManager2014-04-04T22:13:00+02:002014-04-04T22:13:00+02:00Alexander Taylortag:inclem.net,2014-04-04:/2014/04/04/kivy-crash-course/notification-14_using_a_screenmanager/<p>New Kivy Crash Course video released, <a class="reference external" href="https://www.youtube.com/watch?v=xx-NLOg6x8o">14: Using a ScreenManager</a>.</p>
<p>This time I cover the basics behind using a ScreenManager widget,
which can display one Screen at a time whilst making it easy to switch
to other screens including using fancy transitions. The ScreenManager
isn’t too hard, but it …</p><p>New Kivy Crash Course video released, <a class="reference external" href="https://www.youtube.com/watch?v=xx-NLOg6x8o">14: Using a ScreenManager</a>.</p>
<p>This time I cover the basics behind using a ScreenManager widget,
which can display one Screen at a time whilst making it easy to switch
to other screens including using fancy transitions. The ScreenManager
isn’t too hard, but it’s a core component of many apps, so it’s useful
to know how to get started with it!</p>
Kivy’s bind method2014-03-11T10:47:00+01:002014-03-11T10:47:00+01:00Alexander Taylortag:inclem.net,2014-03-11:/2014/03/11/kivy/kivy_bind_method/<p>One of the most common problems for new Kivy users is misunderstanding
how the bind method works, especially amongst newer Python users who
haven’t fully formed their intuition about function calls. For
instance, a user will write code like:</p>
<div class="highlight"><pre><span></span><span class="n">some_screenmanager</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">current</span><span class="o">=</span><span class="n">a_function</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">))</span>
</pre></div>
<p>Here, the idea is …</p><p>One of the most common problems for new Kivy users is misunderstanding
how the bind method works, especially amongst newer Python users who
haven’t fully formed their intuition about function calls. For
instance, a user will write code like:</p>
<div class="highlight"><pre><span></span><span class="n">some_screenmanager</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">current</span><span class="o">=</span><span class="n">a_function</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">))</span>
</pre></div>
<p>Here, the idea is that when the <code>current</code> property changes, it
will call <code>a_function</code> with the arguments <code>arg1</code> and
<code>arg2</code>.</p>
<p>The problem is that Python itself doesn’t work like
this; the bind method doesn’t know about the existence of
<code>a_function</code> or its arguments, it only receives the <em>result</em> of
this function call. This often leads to confusion when a user doesn’t
understand why the binding is only called once, during the declaration
of the binding.</p>
<p>Stepping back, our real goal is to call <code>a_function</code> with the
given arguments, but <code>bind</code> needs to be passed a function if it
is to work correctly. That means we can solve our problem by creating
a new function with these arguments already passed (and discarding the
extra arguments automatically passed by bind).</p>
<p>It’s usually convenient to do this with the <code>partial</code> function
from the functools module:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">partial</span>
<span class="n">some_screenmanager</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">current</span><span class="o">=</span><span class="n">partial</span><span class="p">(</span><span class="n">a_function</span><span class="p">,</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">))</span>
</pre></div>
<p><code>partial</code> returns a new function that will automatically be
passed arg1 and arg2, exactly as we want. You can also pass kwargs
this way.</p>
<p>This isn’t the only way to solve the problem, we could have created a
lambda function (though the syntax is longer and can have scope
problems) or an entire new function with <code>def</code> syntax, but both
of these are more complicated than the simple use of
<code>partial</code>. So if you need to do a binding in Python, look at
this way first!</p>
No Kivy crash course videos for a few weeks2014-02-23T17:06:00+01:002014-02-23T17:06:00+01:00Alexander Taylortag:inclem.net,2014-02-23:/2014/02/23/kivy/no_videos/<p>There won’t be any Kivy crash course videos for the next couple of
weeks, as I won’t have the time or tools to make them. I haven’t
stopped making them though, they’ll resume afterwards.</p>
<p>There won’t be any Kivy crash course videos for the next couple of
weeks, as I won’t have the time or tools to make them. I haven’t
stopped making them though, they’ll resume afterwards.</p>
New Kivy Crash Course video - 13: Using Kivy’s settings panel2014-02-23T17:03:00+01:002014-02-23T17:03:00+01:00Alexander Taylortag:inclem.net,2014-02-23:/2014/02/23/kivy-crash-course/notification-13_using_kivys_settings_panel/<p>New Kivy Crash Course video released, <a class="reference external" href="https://www.youtube.com/watch?v=oQdGWeN51EE">13: Using Kivy’s settings panel</a>.</p>
<p>In this video I cover how to store your application settings in a
file, how to read them in when necessary, and how to have Kivy
semi-automatically construct a widget so the user can modify
settings in real …</p><p>New Kivy Crash Course video released, <a class="reference external" href="https://www.youtube.com/watch?v=oQdGWeN51EE">13: Using Kivy’s settings panel</a>.</p>
<p>In this video I cover how to store your application settings in a
file, how to read them in when necessary, and how to have Kivy
semi-automatically construct a widget so the user can modify
settings in real time.</p>
New Kivy Crash Course video - 12: Using Android APIs2014-02-16T15:05:00+01:002014-02-16T15:05:00+01:00Alexander Taylortag:inclem.net,2014-02-16:/2014/02/16/kivy-crash-course/notification-12_using_android_apis/<p>New Kivy Crash Course video released, <a class="reference external" href="https://www.youtube.com/watch?v=8Jwp1PTvECI">12: Using Android APIs</a>.</p>
<p>In this video I quickly cover 3 (related) ways to access Android’s
APIs straight from Python, making it easy to use hardware features
like the accelerometer as well as tasks like sending an email or
displaying a notification. It …</p><p>New Kivy Crash Course video released, <a class="reference external" href="https://www.youtube.com/watch?v=8Jwp1PTvECI">12: Using Android APIs</a>.</p>
<p>In this video I quickly cover 3 (related) ways to access Android’s
APIs straight from Python, making it easy to use hardware features
like the accelerometer as well as tasks like sending an email or
displaying a notification. It’s just a quick overview, but hopefully
gives a good idea of how to get started.</p>
<p>I’ll start making a short post about each video when it’s
released. It’ll let people keep up to date via <span class="caps">RSS</span>, as well as
hopefully reminding me to post other things!</p>
Creating a Kivy layout: the SparseGridLayout2014-01-27T22:37:00+01:002014-01-27T22:37:00+01:00Alexander Taylortag:inclem.net,2014-01-27:/2014/01/27/kivy/sparsegridlayout/<p>I thought for a change I’d try for a shorter post on a single quick
subject, so I’m going to quickly explain a simple Kivy layout I
created, the <code>SparseGridLayout</code>. The post is standalone, but would go
well with ideas from my <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">Kivy Crash Course</a>, especially the recent …</p><p>I thought for a change I’d try for a shorter post on a single quick
subject, so I’m going to quickly explain a simple Kivy layout I
created, the <code>SparseGridLayout</code>. The post is standalone, but would go
well with ideas from my <a class="reference external" href="https://inclem.net/pages/kivy-crash-course/">Kivy Crash Course</a>, especially the recent videos
trying to draw ideas together to make widgets or specifically focusing
on layouts.</p>
<p>The point here is that Kivy has a built in GridLayout, but it doesn’t
cover all use cases for widgets in a grid, which is sometimes
confusing to users who’ve seen grid widgets in other places and
expected a slightly different behaviour. The problem is that the
GridLayout fills widgets in from the top. That means for instance if there are 3
columns, you can’t place a widget in the third row without first
adding <em>6</em> widgets to fill the first two rows. That’s just inefficient
and wasteful if all you wanted was a small number of widgets in a grid.</p>
<p>So, my SparseGridLayout is a very simple layout that has a number of
rows or columns, and checks its children for a <cite>row</cite> and <cite>column</cite>
property. It then resizes them automatically to be placed in the right
grid cell. That means we don’t need any extra widgets, which is much
more efficient if we want a grid with not many entries, hence <em>sparse</em>
grid layout.</p>
<p>We can start with a <code>FloatLayout</code> base, then all we need to do is set
our grid children’s <code>size_hint</code> and <code>pos_hint</code> properties
appropriately for the grid and call the <cite>FloatLayout</cite>‘s normal layout
method to actually place them in the grid positions/shapes for us.</p>
<p>We can start by making our layout class:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.floatlayout</span> <span class="kn">import</span> <span class="n">FloatLayout</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">NumericProperty</span><span class="p">,</span> <span class="n">ReferenceListProperty</span>
<span class="k">class</span> <span class="nc">SparseGridLayout</span><span class="p">(</span><span class="n">FloatLayout</span><span class="p">):</span>
<span class="n">rows</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">columns</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">shape</span> <span class="o">=</span> <span class="n">ReferenceListProperty</span><span class="p">(</span><span class="n">rows</span><span class="p">,</span> <span class="n">columns</span><span class="p">)</span>
</pre></div>
<p>That creates a basic class that doesn’t have any more actual behavior
than the <code>FloatLayout</code> alone, but has a few new properties. It’ll
hopefully all make sense if you’ve used Kivy a little or followed my
crash course, though the <code>ReferenceListProperty</code> may be new - this
takes multiple other properties and lets us access them as a list, so
for instance referencing or setting <code>shape[0]</code> really updates the
<code>rows</code> property, including calling all its associated events etc.. At
the same time, the <code>shape</code> is also a real property, with its own
events. Do experiment with this if the explanation is not clear.</p>
<p>Now, to make our layout actually rearrange its children to the grid,
we need to override its <code>do_layout</code> method, which is what’s called
whenever it or its children are updated.</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">do_layout</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="n">shape_hint</span> <span class="o">=</span> <span class="p">(</span><span class="mf">1.</span> <span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">columns</span><span class="p">,</span> <span class="mf">1.</span> <span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">rows</span><span class="p">)</span>
<span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">:</span>
<span class="n">child</span><span class="o">.</span><span class="n">size_hint</span> <span class="o">=</span> <span class="n">shape_hint</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="s1">'row'</span><span class="p">):</span>
<span class="n">child</span><span class="o">.</span><span class="n">row</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="s1">'column'</span><span class="p">):</span>
<span class="n">child</span><span class="o">.</span><span class="n">column</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">child</span><span class="o">.</span><span class="n">pos_hint</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'x'</span><span class="p">:</span> <span class="n">shape_hint</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">child</span><span class="o">.</span><span class="n">row</span><span class="p">,</span>
<span class="s1">'y'</span><span class="p">:</span> <span class="n">shape_hint</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="n">child</span><span class="o">.</span><span class="n">column</span><span class="p">}</span>
<span class="nb">super</span><span class="p">(</span><span class="n">SparseGridLayout</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">do_layout</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
</pre></div>
<p>This iterates over all the <cite>SparseGridLayout</cite>‘s children, setting their
size_hint so that they’ll fit exactly in a grid cell (as per the
<code>rows</code> and <code>columns</code> properties we set above). It then checks if
they have a <code>row</code> or <code>column</code> property, setting it to <code>0</code> if not - I’ve
chosen that my rows and columns should be zero-indexed, you could
modify that if you like. After that, it sets their <code>pos_hint</code> such
that they’re placed in the right place. I’ve deliberately let this
work with floats, so for instance they could be in column 2.5 to be
halfway between the integer columns, so the layout is extra flexible.</p>
<p>The final step is calling the original <code>do_layout</code> method of the
<code>FloatLayout</code>. The magic is that all we did is set the child widgets
<code>size_hint</code> and <code>pos_hint</code> so that the widgets align to a grid - the
<code>FloatLayout</code> itself already knows how to actually set their positions
and sizes based on this information. By making use of Kivy’s existing
layout abilities, we’ve saved ourself a lot of work.</p>
<p>Finally, I also added a class to represent entries in the grid:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">GridEntry</span><span class="p">(</span><span class="n">EventDispatcher</span><span class="p">):</span>
<span class="n">row</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">column</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
<p>This is very simple, but it means you can do for example:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">GridLabel</span><span class="p">(</span><span class="n">Label</span><span class="p">,</span> <span class="n">GridEntry</span><span class="p">):</span>
<span class="k">pass</span>
</pre></div>
<p>The GridLabel is thereby a <code>Label</code> that already has row and column
properties, so it will behave properly in our <code>SparseGridLayout</code>. We
don’t strictly need to do this, we could add the <code>row</code> and <code>column</code>
any other way, but this is neat and makes it totally clear what we’re
using our widgets for.</p>
<p>That’s everything! With just a few simple modifications we’ve made a
whole new Layout widget that can place its children in rows and
columns of a grid. Maybe you’ll find that useful, but more generally I
hope this demonstrates the general principles of thinking about
Layouts and using Kivy’s existing mechanisms to do most of the work.</p>
<p>You can find all this code at my <a class="reference external" href="https://github.com/inclement/sparsegridlayout/blob/master/__init__.py">sparsegridlayout github repository</a>,
which also includes a simple demonstration App so you can test the new
layout if you like.</p>
Shall I keep going?2014-01-17T22:10:00+01:002014-01-17T22:10:00+01:00Alexander Taylortag:inclem.net,2014-01-17:/2014/01/17/kivy-crash-course/shall_i_keep_going/<p>To anyone reading these write-ups of my Kivy Crash Course…shall I
keep going? I’ll keep writing up the individual videos if people are
finding them useful, but I’d also like to spend more time blogging
about other things (including other Kivy features rather than just
following the …</p><p>To anyone reading these write-ups of my Kivy Crash Course…shall I
keep going? I’ll keep writing up the individual videos if people are
finding them useful, but I’d also like to spend more time blogging
about other things (including other Kivy features rather than just
following the videos).</p>
<p>If you’re finding the write-ups useful and would like me to continue,
it would be great if you could email me at
<a class="reference external" href="mailto:alexanderjohntaylor@gmail.com">alexanderjohntaylor@gmail.com</a>. It doesn’t have to be long
or complex, even the simplest ‘keep going!’ is useful to me!</p>
<p>To be clear, this is only about the standalone text write-ups. I’m not
going to stop making videos.</p>
Kivy Crash Course 3 - More interesting widget interactions2014-01-17T22:05:00+01:002014-01-17T22:05:00+01:00Alexander Taylortag:inclem.net,2014-01-17:/2014/01/17/kivy-crash-course/3_more_interesting_widget_interactions/<div class="section" id="introduction">
<h2>Introduction</h2>
<p>(<a class="reference external" href="https://www.youtube.com/watch?v=-NvpKDReKyg">Original video</a>)</p>
<p>This is the standalone write-up of my third Kivy Crash Course video,
linked above. In this entry, I head back to Python to add some more
complex and interesting behaviour to our simple program from the
<a class="reference external" href="https://inclem.net/2014/01/09/kivy-crash-course/1_making-a-simple-app/">first article</a>.</p>
<p>If you want to follow along, you can copy …</p></div><div class="section" id="introduction">
<h2>Introduction</h2>
<p>(<a class="reference external" href="https://www.youtube.com/watch?v=-NvpKDReKyg">Original video</a>)</p>
<p>This is the standalone write-up of my third Kivy Crash Course video,
linked above. In this entry, I head back to Python to add some more
complex and interesting behaviour to our simple program from the
<a class="reference external" href="https://inclem.net/2014/01/09/kivy-crash-course/1_making-a-simple-app/">first article</a>.</p>
<p>If you want to follow along, you can copy down the state of the code
from the end of the first tutorial, as below. You can also find it
<a class="reference external" href="https://github.com/inclement/kivycrashcourse/blob/master/video3-more_interesting_widget_interactions/before.py">on github</a>.</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.scatter</span> <span class="kn">import</span> <span class="n">Scatter</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.uix.floatlayout</span> <span class="kn">import</span> <span class="n">FloatLayout</span>
<span class="k">class</span> <span class="nc">TutorialApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">FloatLayout</span><span class="p">()</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">Scatter</span><span class="p">()</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s2">"Hello!"</span><span class="p">,</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
<span class="k">return</span> <span class="n">f</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">TutorialApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>As explained in my original article, this codes for a simple app with
a label saying ‘Hello!’ that you can click and drag around. You can
also scale and rotate it by using multiple touches if you have a
multitouch interface, or on a desktop/laptop by right clicking to use
Kivy’s touch emulation.</p>
</div>
<div class="section" id="adding-some-more-behaviour">
<h2>Adding some more behaviour</h2>
<p>So how are we going to change the app? I want to add some more complex
behaviour - at the moment, it’s nice that we can move the text around,
but this is all handled by the Scatter widget and it isn’t yet clear
how we could create our own widget interactions in a useful way.</p>
<p>I’m going to demonstrate a simple widget interaction by adding a
<code>TextInput</code>, a textbox widget that the user can type into. I’ll then
create a binding so that the Label automatically updates to match this
text, so as soon as the user types anything the text is automatically
propagated to the Label, which we’ll still be able to drag around via
the Scatter widget.</p>
<p>Let’s start by importing the widgets we need:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.textinput</span> <span class="kn">import</span> <span class="n">TextInput</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
</pre></div>
<p>The BoxLayout is a kind of layout that automatically places its
children in a row, either horizontally (the default) or
vertically. We’re going to use this as our new top level root widget
containing our entire application. That’s because we don’t want to
change any of the existing app structure (the Label in a Scatter in a
FloatLayout), but instead to place our new TextInput next to
it. That’s what the BoxLayout will do, plus it can also handle more
advanced stuff like proportional sizes, which I’ll cover later in this article.</p>
<p>Now we have a plan, so lets create our new widgets. We can add the
following at the top of our <code>build()</code> method:</p>
<div class="highlight"><pre><span></span><span class="n">b</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">()</span> <span class="c1"># The default BoxLayout, no</span>
<span class="c1"># extra properties set</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">TextInput</span><span class="p">(</span><span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>
</pre></div>
<p>The TextInput font_size could also be left as the default, setting it
to 150 pixels is just an example customisation. Both the <a class="reference external" href="http://kivy.org/docs/api-kivy.uix.boxlayout.html">BoxLayout</a> and <a class="reference external" href="http://kivy.org/docs/api-kivy.uix.textinput.html">TextInput</a> have many other
properties you could set, which you can find in the linked Kivy
documentation if interested.</p>
<p>Having created our new widgets, we need to add them to the widget tree
of the existing build method. We now want to <code>return</code> the BoxLayout
(it’s going to be our top level widget), and we’ll need to add both
the FloatLayout and the TextInput to that one, so that the BoxLayout
contains them both and can place them next to one another. That means
the full build method can become:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">()</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">TextInput</span><span class="p">(</span><span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">FloatLayout</span><span class="p">()</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">Scatter</span><span class="p">()</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s2">"Hello!"</span><span class="p">,</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
<span class="n">b</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">b</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="k">return</span> <span class="n">b</span>
</pre></div>
<p>Before going any further, lets see exactly what this looks like!</p>
<img alt="App with added TextInput" class="align-center" src="https://inclem.net/images/kivycrashcourse/2_middle.png" style="width: 65ex;" />
<p>The above image is exactly the code from above, plus I moved the
Scatter a little and typed into the TextInput. Everything seems to
work as expected. Note that the BoxLayout aligns its child widgets
horizontally, so the first one added (the FloatLayout) is on the left,
whilst the second (the TextInput) is on the right.</p>
<p>Now we can focus on making changes to achieve the original goal - I
want the TextInput to be at the top of the screen, and it doesn’t need
to take up half of it (the default setting), but only to have a fixed
height large enough to fit in a line of text. I also want the
TextInput text to propagate straight to the Label, updating the
movable text.</p>
<p>The first step here is reorienting the BoxLayout - by default it has
placed its two children horizontally adjacent, but we can make it
vertical instead by changing a single line:</p>
<div class="highlight"><pre><span></span><span class="n">b</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'vertical'</span><span class="p">)</span>
</pre></div>
<p>Let’s also set the height of the TextInput to a specific value. The
first thing to do is a minor Kivy subtlety, we have to set its
<code>size_hint_y</code> to <code>None</code>. All widgets have a default size_hint of 1 in
both the x and y directions, and it’s this number that the BoxLayout
is using to resize its child widgets proportionally - since both have
1, they both have the same height or width. Whenever we want
to set a manual size, we must first set the appropriate size_hint to
None, after which we can manually set the height or width and have the
widget maintain that specific value.</p>
<p>If that isn’t clear to you, I suggest playing with changing the
size_hint and seeing how it changes the relative widget sizes. After
that, you can replace the TextInput declaration with the
following. I’ve also given it some default text so that it doesn’t
start off empty.</p>
<div class="highlight"><pre><span></span><span class="n">t</span> <span class="o">=</span> <span class="n">TextInput</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'default'</span><span class="p">,</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">,</span>
<span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
<span class="n">height</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
</pre></div>
<p>The height is set in the default unit of pixels, so it’s just a little
larger than the font_size and will easily fit in a line of text.</p>
<p>The final layout change is to add the TextInput <em>before</em> the
FloatLayout. The BoxLayout places its children in order from left to
right (if horizontal) or from top to bottom (if vertical), so we need
to add the TextInput first for it to be at the top of the screen. You
can simply switch the order of the <code>add_widget</code> calls as follows:</p>
<div class="highlight"><pre><span></span><span class="n">b</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="n">b</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
</pre></div>
<p>With our layout all set up, we can move to creating the <em>binding</em> that
will cause the Label to automatically update when text is changed in
the TextInput. The syntax is as follows:</p>
<div class="highlight"><pre><span></span><span class="n">t</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="n">some_function</span><span class="p">)</span>
</pre></div>
<p>This would mean that when the <code>text</code> of the widget <code>t</code> changes,
<code>some_function</code> is automatically called. That <code>some_function</code> could be
absolutely any function, it could change your gui, or print to the
console, or communicate on a network, or anything else that you can
program in Python. This is a very useful and general way to make
things happen in response to changes (e.g. from user interaction) in
your widgets.</p>
<p>We’ll need to use a very specific function, we need one that takes the
modified text (which is automatically passed as an argument) and uses
it to set the text of our label to the same thing. Of course we could
write our own function to do this and use that function in the
binding, but actually Kivy has a convenient alternative method:</p>
<div class="highlight"><pre><span></span><span class="n">t</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="n">l</span><span class="o">.</span><span class="n">setter</span><span class="p">(</span><span class="s1">'text'</span><span class="p">))</span>
</pre></div>
<p>Remember, <code>l</code> is our Label. The <code>setter</code> method is available for any
Kivy widget (and some other Kivy objects), and it always returns a
function that <em>sets</em> the given property. That’s exactly what we want,
so overall the effect is that when the <code>text</code> of the Textinput
changes, it calls the returned function, which updates the text of the
Label. Therefore the Label text will always change immediately to
match the TextInput, and we’ll get the behaviour I originally wanted.</p>
<p>This could be a little confusing, but again I encourage you to
experiment to see what happens. A good exercise would be to replace
the setter call with your own function that (for instance) prints its
arguments to the terminal. That way you can see exactly when the
function is called (as you type in the TextInput), and exactly what
arguments it receives.</p>
<p>After that…we’re done! We’ve changed the arrangement of our widgets,
and our new binding should update the Label as we type in the
TextInput. The full program should look something like the following:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.scatter</span> <span class="kn">import</span> <span class="n">Scatter</span>
<span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.uix.floatlayout</span> <span class="kn">import</span> <span class="n">FloatLayout</span>
<span class="kn">from</span> <span class="nn">kivy.uix.textinput</span> <span class="kn">import</span> <span class="n">TextInput</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="k">class</span> <span class="nc">TutorialApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">BoxLayout</span><span class="p">(</span><span class="n">orientation</span><span class="o">=</span><span class="s1">'vertical'</span><span class="p">)</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">TextInput</span><span class="p">(</span><span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">,</span>
<span class="n">size_hint_y</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
<span class="n">height</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">FloatLayout</span><span class="p">()</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">Scatter</span><span class="p">()</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s2">"Hello!"</span><span class="p">,</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
<span class="n">b</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="n">b</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">t</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="n">l</span><span class="o">.</span><span class="n">setter</span><span class="p">(</span><span class="s1">'text'</span><span class="p">))</span>
<span class="k">return</span> <span class="n">b</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">TutorialApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>When you run the code and type in the TextInput, you should get
something like this, with the Label updating to match the TextInput as expected:</p>
<img alt="App with added TextInput" class="align-center" src="https://inclem.net/images/kivycrashcourse/2_finished.png" style="width: 65ex;" />
<p>This post has been a very quick introduction to some basic Kivy layout
techniques, and a simple way to bind our own behaviours when widget
properties change. In the next post, I’ll talk about Kivy’s own
domain specific language for creating widget trees, which includes a
different but extremely powerful and convenient method for doing many
of these tasks.</p>
<p>You can download the finished code from the end of the article <a class="reference external" href="https://github.com/inclement/kivycrashcourse/blob/master/video3-more_interesting_widget_interactions/after.py">here</a>.</p>
</div>
Kivy Crash Course 2 - Building an Android APK2014-01-12T11:34:00+01:002014-01-12T11:34:00+01:00Alexander Taylortag:inclem.net,2014-01-12:/2014/01/12/kivy-crash-course/2_building_an_android_apk/<div class="section" id="introduction">
<h2>Introduction</h2>
<p>(<a class="reference external" href="https://www.youtube.com/watch?v=t8N_8WkALdE">Original video</a>)</p>
<p>In this writeup of my second Kivy Crash Course video, I describe how
to use the <a class="reference external" href="https://github.com/kivy/buildozer">buildozer</a> tool to
compile a Kivy application into a fully functional standalone Android
<span class="caps">APK</span>. For reference, you can find the original video <a class="reference external" href="https://www.youtube.com/watch?v=t8N_8WkALdE">here</a>.</p>
<p>This particular article may have some overlap/redundancy …</p></div><div class="section" id="introduction">
<h2>Introduction</h2>
<p>(<a class="reference external" href="https://www.youtube.com/watch?v=t8N_8WkALdE">Original video</a>)</p>
<p>In this writeup of my second Kivy Crash Course video, I describe how
to use the <a class="reference external" href="https://github.com/kivy/buildozer">buildozer</a> tool to
compile a Kivy application into a fully functional standalone Android
<span class="caps">APK</span>. For reference, you can find the original video <a class="reference external" href="https://www.youtube.com/watch?v=t8N_8WkALdE">here</a>.</p>
<p>This particular article may have some overlap/redundancy with the main
Kivy documentation on buildozer, but I wanted to write it up anyway as
a standalone guide. I’ll also try to keep it up to date with any
future developments if necessary. For reference, the Kivy website has
its own page on <a class="reference external" href="http://kivy.org/docs/guide/packaging-android.html">building an <span class="caps">APK</span></a>, including more
information on the tools and methods that I do not cover here.</p>
</div>
<div class="section" id="background-information">
<h2>Background information</h2>
<p>Before explaining the build procedure, I’d like to quickly explain a
few general things about the Kivy/Android build process. Feel free to
skip to the next section if you just want to get started, but this one
may contain useful information if you have any problems or would like
a little more general information.</p>
<div class="section" id="problems-and-support">
<h3>Problems and support</h3>
<p>First, it’s worth noting that you may hit some problems on the
way. You <em>shouldn’t</em>, and don’t worry about the possibility unless it
happens, but compiling for Android involves a lot of behind the scenes
stuff that can occasionally fail - including potentially for unusual
reasons that may be specific to your hardware. If you do happen to hit
some problem, don’t panic, Kivy has a great community that’s very good
at helping to fix these kinds of problem. In particular you may find
it useful to ask questions or report problems at:</p>
<ul class="simple">
<li>The kivy-users <a class="reference external" href="https://groups.google.com/forum/#!forum/kivy-users">mailing list</a>.</li>
<li>The #kivy irc channel on irc.freenode.net.</li>
<li>The <a class="reference external" href="https://github.com/kivy/kivy/issues?milestone=22&state=open">github issue tracker</a> (for
bugs in kivy itself or feature requests).</li>
</ul>
<p>This information can also be found on the <a class="reference external" href="http://kivy.org/docs/contact.html">Kivy website</a>.</p>
<p>I’m also happy to hear about problems with this article, it’s only a
quick write-up based on my own experiences and I’d like to improve it
to be more general if possible. You can let me know at
<a class="reference external" href="mailto:alexanderjohntaylor@gmail.com">alexanderjohntaylor@gmail.com</a>.</p>
</div>
<div class="section" id="different-ways-to-build">
<h3>Different ways to build</h3>
<p>For the build process itself, there are effectively two ways to build
your Kivy app to a full standalone Android <span class="caps">APK</span>. Both use Kivy’s
<a class="reference external" href="https://github.com/kivy/python-for-android">python-for-android</a>
project, and actually the first method is to use this directly by
calling its component scripts with the right command line options. You
can find the basic instructions for this on the linked github page, or
on the Kivy website <a class="reference external" href="http://kivy.org/docs/guide/packaging-android.html"><span class="caps">APK</span> building page</a>.</p>
<p>This method is fine, and some users make sole use of it, but I much
prefer to use the <a class="reference external" href="https://github.com/kivy/buildozer">buildozer</a>
tool. This is another project from the Kivy developers, and actually
it calls python-for-android behind the scenes, but it adds a useful
configuration file layer for setting up all the parameters of your
app. It also can automatically download and link many of the Android
build dependencies (at least the <span class="caps">SDK</span>, <span class="caps">NDK</span>, plus python-for-android),
which you would have to do manually if using python-for-android
directly. This article will cover the basic use of buildozer.</p>
<p>There are also potentially a couple of other ways to run Kivy apps or
scripts on Android, most obviously the <a class="reference external" href="https://play.google.com/store/apps/details?id=org.kivy.pygame">Kivy Launcher</a> app
that can run Kivy scripts uploaded to your sd card or user data
partition. This can be useful, but I find buildozer so streamlined
that it’s actually easier to build and install a full <span class="caps">APK</span> that way -
plus that makes your app fully standalone and you can install/use your
own modules and advanced features that may not be built into the launcher.</p>
</div>
</div>
<div class="section" id="using-buildozer">
<h2>Using buildozer</h2>
<p>So, let’s make an <span class="caps">APK</span> with buildozer! The first thing to take care of
is installing the tool along with any dependencies it requires.</p>
<p>Before anything else, I need to note that buildozer is only confirmed
to run on Linux or <span class="caps">OS</span> X. It will not run on Windows, and probably
won’t in the near future. If you’re using Windows that means you have
a couple of options for using it; first, you could install a simple
Linux virtual machine, which I encourage anyway on general principle
but may not be convenient for you. Second, Kivy runs an <a class="reference external" href="http://android.kivy.org/">online cloud
builder</a> that uses python-for-android to
compile uploaded code with a few user parameters. This might be useful
and even sufficient for basic <span class="caps">APK</span> building, though it is probably less
flexible and robust than using the tools locally.</p>
<p>The Kivy project does provide a <a class="reference external" href="http://kivy.org/docs/guide/packaging-android.html#testdrive">virtual machine image</a> with
python-for-android and the associated android build tools
preinstalled. You may find this useful, especially if newer to Linux,
but if you’re using buildozer it doesn’t offer much advantage over a
vanilla Linux distro since buildozer would already handle much of this
for you.</p>
<p>You’ll also need to install a few dependencies for
buildozer/python-for-android to work. I think the main ones are:</p>
<ul class="simple">
<li>git, the version control software.</li>
<li>A java jdk, openjdk7 should be good.</li>
<li>pip, the python package management tool.</li>
<li>virtualenv, used internally by python-for-android</li>
</ul>
<p>All are standard and popular, and should be available in the
repositories of most Linux distros or easy to install on <span class="caps">OS</span> X. You can
probably install virtualenv via pip.</p>
<p>At this point we can finally get around to installing buildozer
itself. You should be able to do this via pip (remember to prefix with
<code>sudo</code> if not running as root):</p>
<div class="highlight"><pre><span></span>pip install buildozer
</pre></div>
<p>Buildozer can change quite quickly so you may instead find it useful
to use its git repository directly. This is also very easy, even
knowing nothing about the details, with the following instructions:</p>
<div class="highlight"><pre><span></span>git clone https://github.com/kivy/buildozer.git
<span class="nb">cd</span> buildozer
sudo python2.7 setup.py install
</pre></div>
<p>This will install the current master version of buildozer straight to
your system.</p>
<p>Now you can go to your app directory, wherever you saved your Kivy
application, such as the simple moving text program I made in the
<a class="reference external" href="https://inclem.net/2014/01/09/kivy-crash-course/1_making-a-simple-app/">previous article</a>. The first
vital point is that you <em>must</em> name your main python file
<code>main.py</code>. That’s because Android app will look for and run this file
when you start the app. You can spread the rest of your app across
other python files and folders if you want, but this <code>main.py</code> must
exist and will always be the entry point.</p>
<p>The second step is to create a basic buildozer.spec file, a
configuration file containing all the different parameters to use when
building your app. You can create the file using buildozer itself:</p>
<div class="highlight"><pre><span></span>buildozer init
</pre></div>
<p>This creates a file called buildozer.spec in the current directory,
populated with default values.</p>
<div class="section" id="populating-your-buildozer-spec">
<h3>Populating your buildozer.spec</h3>
<p>Before creating the <span class="caps">APK</span> you’ll need to go through your buildozer.spec
and set some of the values appropriately. In this section I’ll quickly
explain some of the important values. This list is <em>not</em> exhaustive,
you can view more information in the comments of the file itself or in
buildozer and Kivy’s own documentation, but it’ll be plenty to compile
a simple app.</p>
<p>You should at least quickly skim through these settings, you <em>must</em>
change at least the version settings or your compilation will fail.</p>
<p><strong>title</strong>: The name of your app, this will appear in (for instance)
your app drawer. I used ‘Kivy Crash Course 2’.</p>
<p><strong>package.name</strong>: A simple string identifier (no spaces etc.), which
along with <code>package.domain</code> should be a unique identifier. I used ‘kivycrash2’.</p>
<p><strong>package.domain</strong>: Not a real domain name, but along with
<code>package.name</code> should be a unique identifier. Using the default
org.test is fine for now, or more generally you might use a reversed
form of your own domain name.</p>
<p><strong>source.dir</strong>: The directory containing your source code, including
the main.py file. The default ‘.’ should be fine, this means ‘the
current directory’.</p>
<p><strong>source.include_exts</strong>: Buildozer will automatically include source
files with these extensions in your <span class="caps">APK</span>. That means you obviously want
to include py files so your python is loaded. By default, buildozer
includes a few image formats, ‘kv’ which is kv language (covered in a
future article). You can leave this as the default for now.</p>
<p><strong>source.exclude_exts</strong>, <strong>source.exclude_dirs</strong>,
<strong>source.exclude_patterns</strong>: More options for controlling what files
are built into the <span class="caps">APK</span>. These are commented out by default, which is
fine for us.</p>
<p><strong>version.regex</strong>, <strong>version.filename</strong>: These comprise the default
way to find your <span class="caps">APK</span>’s declared version. Buildozer looks in the given
filename (your main.py by default) for a string of the form
<code>__version__ = 'some_version'</code>. I did not add such a string in our
simple app from the first article, so you should <em>delete or comment
out</em> these two settings tokens as they will fail when they try to find
the version string.</p>
<p><strong>version</strong>: This is another way to set your app version, and is
commented out by default. Unless you added a <code>__version__</code> string (see
above), you should <em>uncomment</em> this line. The actual version number or
string isn’t important, I left it at 1.0 for now.</p>
<p><strong>requirements</strong>: This should be a comma separated list of
non-standard python modules to include in your app. You don’t need to
change this to use most modules in the standard library, they are
included by default. Most pure-python modules will be installed via
pip if listed here, though modules with compilation steps need a
special compilation recipe in python-for-android. You can see the list
of existing recipes <a class="reference external" href="https://github.com/kivy/python-for-android/tree/master/recipes">here</a>. None
of this is important to our simple app, and we can leave only the
default entry ‘kivy’, but it’s worth being aware of.</p>
<p><strong>presplash.filename</strong>: The filename pointing at the image that will
be used on kivy’s loading screen appearing when an app is first
run. It is commented out by default (which means it just uses the Kivy
logo), and that’s fine for us now so you don’t need to change it.</p>
<p><strong>icon.filename</strong>: The filename pointing at the image to use as your
app icon in (for instance) your app drawer or launcher. Again, it’s
commented out by default and just uses the Kivy logo, which is fine
for now so you don’t need to change it.</p>
<p><strong>orientation</strong>: The orientation of your app, either ‘landscape’,
‘portrait’, or ‘all’ which means the app is automatically rotated to match
how the device is currently being held. I set this to ‘all’ for our
simple app, but you can make your own choice. You can also dynamically
change the orientation from within your app if you want.</p>
<p><strong>fullscreen</strong>: If set to 1 the app will fill as much of the screen as
possible (everything except a software navigation bar if there is
one), or if set to 0 it leaves the status bar visible. I set it to 0,
but either option is fine. At the time of writing this doesn’t support
the new screen usage parameters introduced in Android 4.4, you only
have a binary choice.</p>
<p>After this there are lots of android options that we don’t need to
worry about, the defaults are all fine. There are also iOS build
options that obviously aren’t important for Android compilation,
though buildozer <em>can</em> perform part of the iOS build process if you’re
interested. Actually, there’s only one other important option:</p>
<p><strong>log_level</strong>: This controls how much information is printed to your
screen as buildozer runs. It defaults to 1, basic information, but I
almost always set it to 2 to see more build information including a
lot more useful logs if something goes wrong.</p>
</div>
<div class="section" id="building-the-apk">
<h3>Building the <span class="caps">APK</span></h3>
<p>That’s it for the configuration file. Assuming you made the minor
changes I suggested, you’re ready to build your <span class="caps">APK</span>!</p>
<p>The advantage of buildozer is that this part is <em>really easy</em>. All we
need to do is type and run in a shell:</p>
<div class="highlight"><pre><span></span>buildozer android debug
</pre></div>
<p>This calls buildozer, and tells it to build an Android <span class="caps">APK</span> in debug
mode. The debug part refers to the way the package is signed, it
doesn’t need properly signing with a developer key (that isn’t hard
but it’s another topic) and you can immediately upload it to a device
and run it.</p>
<p>You’ll find that the first time you run buildozer it has to download a
lot (the Android <span class="caps">SDK</span> and <span class="caps">NDK</span> plus some other tools), which are
hundreds or thousands of megabytes in size. This isn’t really
avoidable if you want to build locally, but it will only happen once,
after which buildozer will always use the same ones. If you already
have the <span class="caps">SDK</span>/<span class="caps">NDK</span> installed, you can check out some of the buildozer
options I didn’t mention that can point buildozer at the local copies
so it doesn’t have to download them again.</p>
<p>If you have a device ready to run your app on, you may instead like
enable developer mode and adb in its settings (the method varies by
device, you can look it up), which lets your computer interact with
the phone to access logs, run commands, install apps etc. The last is
the most immediately important here, as it means we can plug the phone
into the building computer and run</p>
<div class="highlight"><pre><span></span>buildozer android debug deploy
</pre></div>
<p>The last argument, ‘deploy’, tells buildozer to automatically install
the <span class="caps">APK</span> onto your device when the build process is done.</p>
<p>That’s literally everything. Assuming nothing goes wrong, your <span class="caps">APK</span>
will be built and placed in the ‘bin’ directory in the local path, and
you can do whatever you like with it. You can send it to your device
via email, adb, dropbox, or lots of other methods.</p>
</div>
<div class="section" id="debugging">
<h3>Debugging</h3>
<p>Even if the <span class="caps">APK</span> building works, your app may still have
problems. Common ones are stuff like forgetting to include images in
the <span class="caps">APK</span> so the app crashes when Kivy tries to access them. It’s
extremely useful to debug this using the <em>logcat</em> tool that comes with
the Android <span class="caps">SDK</span>. You can run this with</p>
<div class="highlight"><pre><span></span>buildozer android logcat
</pre></div>
<p>to use the version buildozer installed as part of the build
process. More generally, if the <span class="caps">SDK</span> tools are in your <cite>$<span class="caps">PATH</span></cite> you can
just run:</p>
<div class="highlight"><pre><span></span>adb logcat
</pre></div>
<p>Both of these will output the logcat log straight to your
terminal. This includes any standard output of your Python code, such
as print statements, plus any standard Python tracebacks and
errors. This is obviously extremely useful for working out what’s
going wrong!</p>
<p>There are also logcat applications in the play store that can show the
log from on the device. I think they generally require root nowadays,
but they may be useful if you don’t have a computer handy.</p>
<p>That’s everything for this article. It’s a pretty quick guide, but I
hope it covers everything you need to quickly and easily build your
first Android <span class="caps">APK</span> with buildozer. Once it’s all working, you can
rebuild your app whenever you like with <code>buildozer android debug</code>, and
it only takes a few seconds!</p>
<p>In the next article I’ll go back to covering the features of kivy
itself, starting with some more interesting widget interactions.</p>
</div>
</div>
Kivy Crash Course 1 - Making a simple App2014-01-09T21:34:00+01:002014-01-09T21:34:00+01:00Alexander Taylortag:inclem.net,2014-01-09:/2014/01/09/kivy-crash-course/1_making-a-simple-app/<div class="section" id="introduction">
<h2>Introduction</h2>
<p>(<a class="reference external" href="https://www.youtube.com/watch?v=F7UKmK9eQLY">Original video</a>)</p>
<p>This is the first entry in my Kivy Crash Course, originally a series
of short (~10 minutes or less) youtube videos introducing how to
create an app using the kivy graphical framework, how to use different
Kivy features, and other topics like how to build for android …</p></div><div class="section" id="introduction">
<h2>Introduction</h2>
<p>(<a class="reference external" href="https://www.youtube.com/watch?v=F7UKmK9eQLY">Original video</a>)</p>
<p>This is the first entry in my Kivy Crash Course, originally a series
of short (~10 minutes or less) youtube videos introducing how to
create an app using the kivy graphical framework, how to use different
Kivy features, and other topics like how to build for android. In this
series I’ve set out to write up each video as a short written
tutorial. These tutorials will be standalone, you don’t need to refer
to the original video, but they will cover roughly the same topics. I
may also supplement with posts about other Kivy features.</p>
<p>The idea of these tutorials is that you can just dive in - you’ll need
Kivy installed if you want to follow along, but I’m starting totally
from scratch and quickly progressing through all the basic kivy
features. I’ll explain what’s going on along the way, there are also
plenty of opportunities for experimentation at each stage.</p>
<p>For anyone that doesn’t know, <a class="reference external" href="http://kivy.org/">Kivy</a> is a graphical framework for
<a class="reference external" href="http://www.python.org/">Python</a>. You can use it to make graphical interfaces for your
applications, and it comes with a nice set of highly extensible
widgets covering standard functionality and which can easily be
combined to make powerful and complex interfaces. One of the the great
advantages of Kivy (and one of my favourite features!) is that it’s
very highly cross platform - a kivy app will run not just on standard
desktop platforms like Linux, Windows and <span class="caps">OS</span> X, but can also easily be
compiled into an android or iOS app, or run on more unusual devices
like the raspberry pi. I won’t introduce more details of kivy here,
but you can check out its <a class="reference external" href="http://kivy.org/">website</a> for more details.</p>
</div>
<div class="section" id="a-first-kivy-app">
<h2>A first Kivy app</h2>
<p>So…lets actually do something! We’ll start completely from scratch,
and our kivy entry point will be an <code>App</code> class which we’ll import
from kivy.</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
</pre></div>
<p>The <code>App</code> is the base of any Kivy application, and we’ll start making
our own Kivy program by defining our own <code>App</code> subclass.</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">TutorialApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">pass</span>
</pre></div>
<p>At this point we don’t even need to add any behaviour, the <code>App</code> can
already run, and if we do so it will perform all the basic application
setup like creating a window, sizing and positioning it, and
potentially even stuff like retreiving application settings and
constructing a settings panel.</p>
<p>To complete our first totally trivial kivy app, we just add a standard
python declaration that instantiates our <code>TutorialApp</code>
class and calls its <code>run</code> method - the function that tells the <code>App</code>
to start and to do its thing. With this addition, the full file should
look like the following:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="k">class</span> <span class="nc">TutorialApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">TutorialApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>You can already run this app if you like - it should start up fine,
and you’ll get a totally black window because we didn’t tell it to
display anything. Congratulations on your first kivy program!</p>
<p>To make this more interesting, we need to tell our <code>App</code> how to
display something. In practice with Kivy, this means telling it to
display some <code>Widget</code>. Kivy comes with quite a lot of widget
classes, generally small with some specific functionality of their own
(checkbox, label, textinput, scrollable container etc.), but
designed to be easy to combine to make much more complex interfaces
with whatever behaviour you really want.</p>
<p>For now we’ll import a simple <code>Button</code> widget:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
</pre></div>
<p>Next we need to tell Kivy how to display this button. We do this using
the <code>build</code> method of <code>App</code>. When the <code>App</code> is run it calls this <code>build</code>
method, and whatever is returned is used as Kivy’s ‘root widget’. This
widget is automatically sized and positioned to fill the window, and
the rest of your application is built by adding child widgets (more on
this later in the series).</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">TutorialApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">Button</span><span class="p">()</span>
</pre></div>
<p>Actually we can go a little further than this and set some properties
of the button. As far as we’re concerned right now, these are just
attributes of the button controlling its appearance. You can see the
full list of appearance properties in <a class="reference external" href="http://kivy.org/docs/api-kivy.uix.button.html">Kivy’s documentation</a>, but for now lets
just set a couple of simple ones:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">TutorialApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'Hello!'</span><span class="p">,</span>
<span class="n">background_color</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="c1"># List of</span>
<span class="c1"># rgba components</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>
</pre></div>
<p>If you run the application now you should get something much more
interesting - a nice blue button sized to fill the window, which you
can click (that’ll make the button lighter), and which you can run and
dance and play with or whatever.</p>
<img alt="Image of Kivy button displayed by our app." class="align-center" src="https://inclem.net/images/kivycrashcourse/1_simple_button_app.png" style="width: 65ex;" />
<p>At this point you already have an app worth playing with. You can
change the values of the properties we defined (text,
background_color, font_size) and see how it affects the button, as
well as looking up other options in the documentation, or even
replacing it with other simple widgets - the Kivy website has a <a class="reference external" href="http://kivy.org/docs/api-kivy.uix.html">full
list</a>.</p>
</div>
<div class="section" id="some-more-complex-behaviour">
<h2>Some more complex behaviour</h2>
<p>I’m going to finish this tutorial entry by just
quickly introducing a couple more widgets in a way that adds some
significantly more interesting behaviour and interaction to the app.</p>
<p>To do this I’ll use a <code>Scatter</code> widget. This can be moved, resized and
rotated by interactions (either mouse or touch) or manually in the
code, and in the process takes care of propagating these changes to
any child widgets so that you can easily apply these transformations
to any Kivy widget.</p>
<p>We don’t need the button any more, so you can replace that import with</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.scatter</span> <span class="kn">import</span> <span class="n">Scatter</span>
</pre></div>
<p>We’ll also need a couple of other widgets. First, a <code>Label</code> which
displays some text and will be placed in the scatter (so it will be
possible to move, rotate, scale it etc.), and second a <cite>FloatLayout</cite>
that will be our new root widget. A layout is a special kind of widget
that manages the size and/or position of its child widgets. The
FloatLayout in particular lets widgets behave basically like windows
on your desktop probably do - they can have arbitrary positions and
sizes. That’s what we want, since we’re using a scatter whose position
and size may be changed by our interaction, and we don’t want some
other layout interfering with that (see future tutorial entries for
more). The <code>FloatLayout</code> also has other features like letting us
easily size widgets proportionally, but that’s not important here.</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">kivy.uix.label</span> <span class="kn">import</span> <span class="n">Label</span>
<span class="kn">from</span> <span class="nn">kivy.uix.floatlayout</span> <span class="kn">import</span> <span class="n">FloatLayout</span>
</pre></div>
<p>Now, instead of making a button we’ll need to instantiate our new widgets.</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">TutorialApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">FloatLayout</span><span class="p">()</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">Scatter</span><span class="p">()</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'Hello!'</span><span class="p">,</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>
</pre></div>
<p>Note here that the floatlayout and scatter don’t have any special
properties set, but we could do stuff like disable the scatter’s
touch interaction at this point if we wanted to.</p>
<p>At this point we have three widgets - different to before where we
only had a single button! We can only return one of these widgets to
be the application’s root widget, so
the others will have to be child widgets added to one of the other
ones. We do this by adding each widget to a different widget above it.</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">TutorialApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">FloatLayout</span><span class="p">()</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">Scatter</span><span class="p">()</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'Hello!'</span><span class="p">,</span>
<span class="n">font_size</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
<span class="k">return</span> <span class="n">f</span>
</pre></div>
<p>Now everything is added below the floatlayout, which is returned to
become the application’s root widget - it will fill the screen (though
it has no visual representation so we won’t be able to see it), but
we <em>will</em> be able to see the label that we should be able to move
around by interacting with the scatter that contains it.</p>
<p>Lets try it! If you run the application you should see something like
the following:</p>
<img alt="Finished app with label in bottom left." class="align-center" src="https://inclem.net/images/kivycrashcourse/1_final_app_init.png" style="width: 65ex;" />
<p>The text doesn’t all fit in the screen because the initial position of
the label is in the bottom left corner, and the text doesn’t entirely
fit within the <code>Label</code> widget (a full explanation will come in a later
post). It doesn’t matter though, because you should be able to drag
the text to the centre of the screen! This is all automatically
handled by the scatter widget containing the label.</p>
<img alt="Finished app with label moved and rotated." class="align-center" src="https://inclem.net/images/kivycrashcourse/1_final_app_modified.png" style="width: 65ex;" />
<p>On the desktop you can also right click to interact with multitouch
emulation. Each right click creates a red dot representing an emulated
touch. By creating and dragging multiple touches on the label you can
recreate the inbuilt gestures of rotation and pinching to zoom. If you
use a scatter widget on a touch device, you can of course do the same
thing with normal touch interaction.</p>
<p>That’s it for this first entry in the Kivy crash course, going
straight from an empty file to a fully working kivy app that already
supports dynamic touch interaction. The next entry will cover how to
compile this app (no changes whatsoever!) straight into an android apk
that you can run on most android devices.</p>
<p>You can download the finished code from the crash course
<a class="reference external" href="https://github.com/inclement/kivycrashcourse/blob/master/video1-making_a_simple_app/after.py">github repository</a>.</p>
</div>