Home

Awesome

micropython-gui

Provides a simple touch driven event based GUI interface for the Pyboard when used with a TFT display. The latter should be based on SSD1963 controller with XPT2046 touch controller. Such displays are available in electronics stores e.g. and on eBay. The software is based on drivers for the TFT and touch controller from Robert Hammelrath.

It now uses and requires uasyncio V3.

It is targeted at hardware control and display applications.

Image

For hardware notes see this reference. An extension for plotting simple graphs is described here.

For sample images, go here. A video may be seen here.

Contents

  1. Release notes
  2. Pre requisites
    2.1 Pre installation
    2.2 Library Documentation
    2.3 Python files
    2.4 Running the demos
  3. Icons
  4. Concepts
    4.1 Terminology
    4.2 Coordinates
    4.3 Colors
    4.4 Callbacks
    4.5 Screens
  5. Program Structure
  6. Class Screen
    6.1 Class methods
    6.2 Constructor
    6.3 Callback methods
    6.4 Method
  7. Display Widgets Non touch sensitive displayable objects.
    7.1 Class Label
    7.2 Class Dial
    7.3 Class LED
    7.4 Class Meter
    7.5 Class IconGauge
    7.6 Vector display
  8. Control Widgets Touch sensitive displayable objects
    8.1 Class Slider
    8.2 Class Knob
    8.3 Class Checkbox
    8.4 Class Button
    8.5 Class ButtonList: emulate a button with multiple states
    8.6 Class RadioButtons
    8.7 Class IconButton also checkbox
    8.8 Class IconRadioButtons
    8.9 Class Listbox
    8.10 Class Dropdown
  9. Dialog Boxes
    9.1 Class Aperture
    9.2 Class DialogBox
  10. Developer Notes

1. Release notes for existing users

Release 0.7 16th Jun 2020 Refactored as a Python package (see below). Add vector display widgets. Release 0.6 15th Jun 2020 Uses (and requires) uasyncio V3. Release 0.51 14th Feb 2017 add Screen.after_open method. Release 0.5 7th Jan 2017. Uses uasyncio in place of usched. Release 0.2 17th Nov 2016. Uses fonts created with the font_to_py.py utility.

V0.7 note

This has been refactored as a Python package. This enables a modular design and reduces RAM use to the point where frozen bytecode is not usually required. All test scripts apart from the icon test will run without frozen code.

Applications will require changes to import statements.

Jump to Contents

2. Pre requisites

2.1 Pre installation

Before running the GUI the hardware should be tested. The display may optionally be calibrated according to the instructions on Robert Hammelrath's site. Resistive touch panels work best when activated by a stylus or fingernail. They are also subject to jitter to a degree which varies between display models: the touch library uses digital filtering to reduce the effect of jitter. This uses two values confidence and margin which may be fine tuned to the unit in use prior to running the GUI. The optimum values, together with calibration data, should be stored in the file tft_local.py listed below.

Some familiarity with callbacks and event driven programming will be of help in developing applications. The GUI classes are in two categories, those rendered using icons and those drawn by means of graphics primitives. Either (or both) may be used in a project.

Smaller applications not using icon controls will run without frozen bytecode. Users planning larger applications should familiarise themselves with building Micropython from source, and with the technique for installing Python modules as frozen bytecode. Official instructions on how to do this may be found here. At the time of writing these did not reflect the new "manifest" system, which is described here.

Firmware

To ensure the correct version of uasyncio firmware must be a daily build or a release build later than V1.12.

2.2 Library Documentation

Documentation for the underlying libraries may be found at these sites.
Robert Hammelrath's drivers:
XPT2046 driver
TFT driver
Other references:
Proposed standard font format
TFT driver fork Robert Hammelrath's driver adapted for above font format.
uasyncio libraries and notes

2.3 Python files

The simplest way to install this library is to copy the tft directory and all its contents to the Pyboard's filesystem.

Hardware driver in tft/driver:

  1. TFT_io.py Low level TFT driver. Cannot be frozen.

Core files in tft/driver:

  1. tft.py TFT driver.
  2. touch_bytecode.py Touch panel driver.
  3. ugui.py The micro GUI library.
  4. constants.py Constants such as colors and shapes (import using from tft.driver.constants import *)
  5. tft_local.py Local hardware definition (user defined settings including optional calibration data). This file should be edited to match your hardware.

Synchronisation primitives in tft/primitives:

  1. delay_ms A software retriggerable timer.

Optional files used by test programs:

Fonts in tft/fonts:

  1. font10.py Font file.
  2. font14.py Ditto.

Icons in tft/icons:

  1. radiobutton.py Icon file for icon radio buttons
  2. checkbox.py Icon file for icon checkboxes.
  3. iconswitch.py Icon file for an on/off switch.
  4. traffic.py Icons for traffic light button
  5. gauge.py Icons for linear gauge
  6. flash.py Icons for flashing button
  7. threestate.py Icon for 3-state checkbox

Test/demo programs in tft/demos:

  1. vst.py A test program for vertical linear sliders.
  2. hst.py Tests horizontal slider controls, meters and LED.
  3. buttontest.py Pushbuttons and checkboxes.
  4. knobtest.py Rotary controls, a dropdown list, a listbox. Also shows the two styles of "greying out" of disabled controls.
  5. screentest.py Test of multiple screens.
  6. dialog.py A modal dialog box.
  7. ibt.py Test of icon buttons.
  8. vtest.py Vector display: clock and compass displays.

If you don't intend to use icons, icon files and demo 7 may be ignored.

By the standards of the Pyboard this is a large library. All test scripts will run without frozen bytecode with the exception of ibt.py. Larger applications and any using icons will require freezing some modules to conserve RAM.

When freezing files create the same directory structure in your frozen modules directory. For example, create the directory 'tft/icons' and copy all icons there. This allows the import statements to be unchanged.

Preferred candidates for freezing are icons (if used), fonts and drivers. The hardware driver listed above cannot be frozen as it uses inline assembler and Viper code. It's probably unwise to freeze tft_local.py as it may need to be edited for calibration values etc.

Instructions on creating icon files may be found in the TFT driver README. Fonts should be created using font_to_py.py. The -x argument should be employed.

2.4 Running the demos

Demos run on import, which is done using the following syntax:

from tft.demos import hst

If you experience memory errors after running more than one demo, issue ctrl-d to reset the board.

Jump to Contents

3. Icons

Most classes use graphics primitives to draw objects on the screen. A few employ icons: this is arguably prettier but less "micro". It uses large icon files which must be frozen as bytecode. By contrast objects drawn with graphics primitives are scalable. Further, properties such as colors can efficiently be changed at runtime: to achieve this with an icon-based object would require a set of colored icons to be created at design time. The library is usable without the icon classes.

Instructions and a utility for creating icon files may be found in Robert Hammelrath's TFT driver README.

Jump to Contents

4. Concepts

4.1 Terminology

GUI objects are created on a Screen instance which normally fills the entire physical screen. Displayable GUI objects comprise control and display instances. The former can respond to touch (e.g. Pushbutton instances) while the latter cannot (LED or Dial instances).

4.2 Coordinates

In common with most displays, the top left hand corner of the display is (0, 0) with increasing values of x to the right, and increasing values of y downward. Display objects exist within a rectangular bounding box; in the case of touch sensitive controls this corresponds to the sensitive region. Locations are defined as a 2-tuple (x, y). The location of an object is defined as the location of the top left hand corner of the bounding box.

4.3 Colors

These are defined as a 3-tuple (r, g, b) with values of red, green and blue in range 0 to 255. The interface and this document uses the American spelling (color) throughout for consistency with the TFT library.

4.4 Callbacks

The interface is event driven. Controls may have optional callbacks which will be executed when a given event occurs. A callback function receives positional arguments. The first is a reference to the object raising the callback. Subsequent arguments are user defined, and are specified as a tuple or list of items. Callbacks are optional, as are the argument lists - a default null function and empty list are provided. Callbacks are usually bound methods - see the Screens section for a reason why this is useful.

All controls and displays have a tft property. This enables callbacks to access drawing primitives.

4.5 Screens

GUI controls and displays are rendered on a Screen instance. A user program may instantiate multiple screens, each with its own set of GUI objects. The Screen class has class methods enabling runtime changes of the screen being rendered to the physical display. This enables nested screens. The feature is demonstrated in screentest.py.

Applications should be designed with a Screen subclass for each of the application's screens (even if the app uses only a single screen). This faciitates sharing data between GUI objects on a screen, and also simplifies the handling of control callbacks. These will be methods bound to the user screen. They can access the screen's bound variables via self and the control's bound methods via the callback's first argument (which is a reference to the control). A simple example can be seen in the KnobScreen example in screentest.py.

The Screen class has 3 null methods which may be implemented in subclasses: on_open which runs when a screen is opened but prior to its display, after_open which is called after display, and on_hide which runs when a screen change is about to make the screen disappear. These may be used to instantiate or control tasks and to retrieve results from a modal dialog box.

The Screen class is configured in tft_local.py.

Jump to Contents

5. Program Structure

The following illustrates the structure of a minimal program:

from tft.driver.ugui import Screen
from tft.driver.constants import *
from tft.driver.tft_local import setup

from tft.fonts import font14
from tft.widgets.buttons import Button

class BaseScreen(Screen):
    def __init__(self):
        super().__init__()
        Button((10, 10), font = font14, fontcolor = BLACK, text = 'Hi')
setup()
Screen.change(BaseScreen)

The last line causes the Screen class to instantiate your BaseScreen and to start the scheduler using that screen object. Control then passes to the scheduler: any code following this line will not run until the GUI is shut down and the scheduler is stopped (by calling Screen.shutdown()).

Jump to Contents

6. Class Screen

The Screen class presents a full-screen canvas onto which displayable objects are rendered. Before instantiating GUI objects a Screen instance must be created. This will be the current one until another is instantiated. When a GUI object is instantiated it is associated with the current screen.

The best way to use the GUI, even in single screen programs, is to create a user screen by subclassing the Screen class. GUI objects are instantialited in the constructor after calling the Screen constructor. This arrangement facilitates communication between objects on the screen. The following presents the outline of this approach:

def backbutton(x, y):
    def back(button):
        Screen.back()
    Button((x, y), height = 30, font = font14, fontcolor = BLACK, callback = back,
           fgcolor = CYAN,  text = 'Back', shape = RECTANGLE, width = 80)

class Screen_0(Screen):
    def __init__(self):
        super().__init__()
        Label((0, 0), font = font14, width = 400, value = 'Test screen')
        backbutton(390, 242)
setup()
Screen.change(Screen_0)

Note that the GUI is started by issuing Screen.change with the class as its argument rather than an instance. This assists in multi-screen programs: screens are only instantiated when they are to be displayed. This allows RAM to be reclaimed by the garbage collector when the screen is closed.

6.1 Class methods

In normal use the following methods only are required:

Other method:

See screentest.py and dialog.py for examples of multi-screen design.

6.2 Constructor

This takes no arguments.

6.3 Callback methods

These do nothing, and are intended to be defined in subclasses if required.

6.4 Method

Jump to Contents

7. Display Widgets

These classes provide ways to display data and are not touch sensitive.

7.1 Class Label

Displays text in a fixed length field. The height of a label is determined by the metrics of the specified font.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Keyword only arguments:

Method:

Jump to Contents

7.2 Class Dial

Displays angles in a circular dial. Angles are in radians with zero represented by a vertical pointer. Positive angles appear as clockwise rotation of the pointer. The object can display multiple angles using pointers of differing lengths (e.g. clock face).

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Keyword only arguments (all optional):

Method:

Jump to Contents

7.3 Class LED

Displays a boolean state. Can display other information by varying the color.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Keyword only arguments (all optional):

Methods:

Jump to Contents

7.4 Class Meter

This displays a single value in range 0.0 to 1.0 on a vertical linear meter.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Keyword only arguments:

Methods:

Jump to Contents

7.5 Class IconGauge

This can display any one of a set of icons at a location. The icon to be displayed can be selected by an integer index. Alternatively a float in range 0.0 to 1.0 can be displayed: the control shows the nearest icon.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Mandatory keyword only argument:

Optional keyword only argument:

Methods:

7.6 Vector display

Provides a means of displaying one or more vectors. A vector is a complex with magnitude in the range of 0 to 1.0. In use a VectorDial is instantiated, followed by a Pointer instance for each vector to be displayed on it. The VectorDial can display its vectors as lines (as on a clock face) or as arrows (as on a compass).

By contrast with the Dial class the pointers have lengths and colors which can vary dynamically.

from micropython_ra8875.widgets.vectors import Pointer, VectorDial

Class VectorDial

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Keyword only arguments (all optional):

Class Pointer

Constructor mandatory positional arg:

Method:

Jump to Contents

8. Control Widgets

These classes provide touch-sensitive objects capable of both the display and entry of data. If the user moves the control, its value will change and an optional callback will be executed. If another control's callback or a task alters a control's value, its appearance will change to reflect this.

Buttons and checkboxes are provided in two variants, one drawn using graphics primitives, and the other using icons.

Jump to Contents

8.1 Class Slider

These emulate linear potentiometers. Vertical Slider and horizontal HorizSlider variants are available. These are constructed and used similarly. The short forms (v) or (h) are used below to identify these variants. See the note above on callbacks.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Optional keyword only arguments:

Methods:

Jump to Contents

8.2 Class Knob

This emulates a rotary control capable of being rotated through a predefined arc.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Optional keyword only arguments:

Methods:

Jump to Contents

8.3 Class Checkbox

Drawn using graphics primitives. This provides for boolean data entry and display. In the True state the control can show an 'X' or a filled block of color.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Optional keyword only arguments:

Methods:

Jump to Contents

8.4 Class Button

Drawn using graphics primitives. This emulates a pushbutton, with a callback being executed each time the button is pressed. Buttons may be any one of three shapes: CIRCLE, RECTANGLE or CLIPPED_RECT.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Mandatory keyword only argument:

Optional keyword only arguments:

Method:

Class variables:

Jump to Contents

8.5 Class ButtonList emulate a button with multiple states

Drawn using graphics primitives.

A ButtonList groups a number of buttons together to implement a button which moves between states each time it is pressed. For example it might toggle between a green Start button and a red Stop button. The buttons are defined and added in turn to the ButtonList object. Typically they will be the same size, shape and location but will differ in color and/or text. At any time just one of the buttons will be visible, initially the first to be added to the object.

Buttons in a ButtonList should not have callbacks. The ButtonList has its own user supplied callback which will run each time the object is pressed. However each button can have its own list of args. Callback arguments comprise the currently visible button followed by its arguments.

Constructor argument:

Methods:

Typical usage is as follows:

from tft.driver.constants import *
from tft.widgets.buttons import ButtonList
from tft.fonts import font14

def callback(button, arg):
    print(arg)

table = [
     {'fgcolor' : GREEN, 'shape' : CLIPPED_RECT, 'text' : 'Start', 'args' : ['Live']},
     {'fgcolor' : RED, 'shape' : CLIPPED_RECT, 'text' : 'Stop', 'args' : ['Die']},
]
bl = ButtonList(callback)
for t in table: # Buttons overlay each other at same location
    bl.add_button((10, 10), font = font14, fontcolor = BLACK, **t)
Jump to Contents

8.6 Class RadioButtons

Drawn using graphics primitives.

These comprise a set of buttons at different locations. When a button is pressed, it becomes highlighted and remains so until another button is pressed. A callback runs each time the current button is changed.

Constructor positional arguments:

Methods:

Typical usage:

def callback(button, arg):
    print(arg)

table = [
    {'text' : '1', 'args' : ['1']},
    {'text' : '2', 'args' : ['2']},
    {'text' : '3', 'args' : ['3']},
    {'text' : '4', 'args' : ['4']},
]
x = 0
rb = RadioButtons(callback, BLUE) # color of selected button
for t in table:
    rb.add_button((x, 180), font = font14, fontcolor = WHITE,
                    fgcolor = LIGHTBLUE, height = 40, **t)
    x += 60 # Horizontal row of buttons
Jump to Contents

8.7 Class IconButton also checkbox

Drawn using an icon file which must be imported before instantiating. A checkbox may be implemented by setting the toggle argument True and using an appropriate icon file. An IconButton instance has a state representing the index of the current icon being displayed. User callbacks can interrogate this by means of the value method described below.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Mandatory keyword only argument:

Optional keyword only arguments:

Methods:

Class variables:

Jump to Contents

8.8 Class IconRadioButtons

Drawn using an icon file which must be imported before instantiating. These comprise a set of buttons at different locations. When initially drawn, all but one button will be in state 0 (i.e. showing icon[0]). The selected button will be in state 1. When a button in state 0 is pressed, the set of buttons changes state so that it is the only one in state 1 (showing icon[1]). A callback runs each time the current button changes.

Constructor positional arguments:

Methods:

Jump to Contents

8.9 Class Listbox

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Mandatory keyword only arguments:

Optional keyword only arguments:

Methods:

The callback is triggered whenever a listbox item is pressed, even if that item is already currently selected.

Jump to Contents

8.10 Class Dropdown

A dropdown list. The list, when active, is drawn below the control. The height of the control is determined by the height of the font in use.

Constructor mandatory positional argument:

  1. location 2-tuple defining position.

Mandatory keyword only arguments:

Optional keyword only arguments:

Methods:

The callback is triggered if an item on the dropdown list is touched and that item is not currently selected (i.e. when a change occurs).

Jump to Contents

9. Dialog Boxes

In general Screen objects occupy the entire physical display. The principal exception to this is modal dialog boxes: these are rendered in a window which accepts all touch events until it is closed. Dialog boxes are created by instantiating an Aperture which is a Screen superclass. In effect this is a window, but a 'micro' implementation lacking chrome beyond a simple border and occupying a fixed location on the screen.

In use the user program creates a class subclassed from Aperture. This is populated in the same way as per Screen subclasses. The class name can then be passed to Screen.change to invoke the dialog box. The GUI provides a simple way to build dialog boxes based on a small set of pushbuttons such as 'Yes/No/Cancel' in the form of the DialogBox class.

A convenience method locn is provided to assist in populating dialog boxes. Given coordinates relative to the dialog box, it provides an absolute location 2-tuple suitable as a constructor argument for control or display classes. See dialog.py for example usage.

Jump to Contents

9.1 Class Aperture

Provides a window for objects in a modal dialog box.

Constructor mandatory positional args:

  1. location 2-tuple defining the window position.
  2. height Dimensions in pixels.
  3. width

Optional keyword only args:

Instance variables:

Method:

Class method:

Jump to Contents

9.2 Class DialogBox

Simplifies building simple dialog boxes based on a set of pushbuttons. Any button press will close the dialog. The caller can determine which button was pressed. The size of the buttons and the width of the dialog box are calculated from the strings assigned to the buttons. This ensures that buttons are evenly spaced and identically sized.

Constructor mandatory positional args:

  1. font The font for buttons and label.

Optional keyword only args:

Pressing any button closes the dialog and sets the Aperture value to the text of the button pressed or 'Close' in the case of the close button.

Jump to Contents

10. Developer Notes

For developers wishing to extend the library with new controls or displays, see this reference.

Jump to Contents