Skip to content

Commit

Permalink
Adding a How To document (moving https://wiki.zerophone.org/index.php…
Browse files Browse the repository at this point in the history
…/ZPUI_code_snippets to this documentation)
  • Loading branch information
CRImier committed Jan 4, 2018
1 parent 03649de commit a1576c5
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 0 deletions.
185 changes: 185 additions & 0 deletions docs/howto.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
.. _howto:

How to...
#########

Do you want to improve your ZPUI app or solve your problem by copy-pasting
a snippet in your app code? This page is for you =)

.. contents::
:local:
:depth: 2

Basics
======

What's the minimal ZPUI app?
----------------------------

In ``app/main.py``:

.. code-block:: python
menu_name = "Skeleton app"
i = None #Input device
o = None #Output device
def init_app(input, output):
#Gets called when app is loaded
global i, o
i = input; o = output
def callback():
#Gets called when app is selected from menu
pass
``app/__init__.py`` has to be an empty file:

.. code-block:: python
------------

Config (and other) files
========================

How to get path to a file in the app directory?
-----------------------------------------------

Say, you have a ``my_song.mp3`` file shipped with your app. However, in order to use
that file from your code, you have to refer to that file using a path relative to the
ZPUI root directory, such as ``apps/personal/my_app/my_song.mp3``.

Here's how to get that path automatically, without hardcoding which folder your app is put in:

.. code-block:: python
from helpers import local_path_gen
local_path = local_path_gen(__name__)
mp3_file_path = local_path("my_song.mp3")
In case of your app having nested folders, you can also give multiple arguments to
``local_path()``:

.. code-block:: python
song_folder = "songs/"
mp3_file_path = local_path(song_folder, "my_song.mp3")
------------

How to read JSON from a ``config.json`` file located in the app directory?
--------------------------------------------------------------------------

.. code-block:: python
from helpers import read_config, local_path_gen
config_filename = "config.json"
local_path = local_path_gen(__name__)
config = read_config(local_path(config_filename))
------------

How to read a ``config.json`` file, and restore it to defaults if it can't be read?
-----------------------------------------------------------------------------------

.. code-block:: python
from helpers import read_or_create_config, local_path_gen
default_config = '{"your":"default", "config":"to_use"}' #has to be a string
config_filename = "config.json"
local_path = local_path_gen(__name__)
config = read_or_create_config(local_path(config_filename), default_config, menu_name+" app")
.. note:: The faulty ``config.json`` file will be copied into a ``config.json.faulty``
file before being overwritten

Run tasks on app startup
=====================================

Run a short task only once when your app is called
--------------------------------------------------

This is suitable for short tasks that you only call once, and that won't conflict
with other apps.

.. code-block:: python
def init_app(i, o):
...
init_hardware() #Your task - short enough to run while app is being loaded
------------

Run a task only once, first time when the app is called
-------------------------------------------------------

This is suitable for tasks that you can only call once, and you'd only need to
call once the user activates the app (maybe grabbing some resource that could
conflict with other apps, such as setting up GPIO or other interfaces).

.. code-block:: python
from helpers import Oneshot
...
def init_hardware():
#can only be run once
#since oneshot is only defined once, init_hardware function will only be run once,
#unless oneshot is reset.
oneshot = Oneshot(init_hardware)
def callback():
oneshot.run() #something that you can't or don't want to init in init_app
... #do whatever you want to do
Run a task in background after the app was loaded
-------------------------------------------------

This is suitable for tasks that take a long time. You wouldn't want to execute that task
directly in ``init_app()``, since it'd stall loading of all ZPUI apps, not allowing the user
to use ZPUI until your app has finished loading (pretty egoistic, if you think about it).

.. code-block:: python
from helpers import BackgroundRunner
...
def init_hardware():
#takes a long time
init = BackgroundRunner(init_hardware)
def init_app(i, o):
...
init.run() #something too long that just has to run in the background,
#so that app is loaded quickly, but still can be initialized.
def callback():
if init.running: #still hasn't finished
PrettyPrinter("Still initializing...", i, o)
return
elif init.failed: #finished but threw an exception
PrettyPrinter("Hardware initialization failed!", i, o)
return
... #everything initialized, can proceed safely
Control flow and user-friendliness
==================================

Allow exiting a loop using KEY_LEFT
-----------------------------------

Say, you have a loop that doesn't have an UI element in it - you're just doing something
repeatedly. You'll want to allow the user to exit that loop, and the reasonable way is to
interrupt the loop when the user presses KEY_LEFT. Here's how to allow that:

.. code-block:: python
from helpers import ExitHelper
...
eh = ExitHelper(i).start()
while eh.do_run():
... #do something repeatedly until the user presses KEY_LEFT
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Guides:

* :doc:`Installing and updating ZPUI <setup>`
* :ref:`Installing ZPUI emulator <emulator>`
* :doc:`App development - how to ... ? <howto>`
* :doc:`ZPUI configuration files <config>`
* :doc:`Hacking on UI <hacking_ui>`

Expand All @@ -38,6 +39,7 @@ References:

setup.rst
config.rst
howto.rst
ui.rst
helpers.rst
hacking_ui.rst
Expand Down

0 comments on commit a1576c5

Please sign in to comment.