TiLDA MK3/ugfx

From EMF Badge
Revision as of 16:13, 9 August 2016 by Joel (talk | contribs) (Renaming section from 'Squares' to 'Rectangles')
Jump to navigation Jump to search

The badge makes use of uGFX for providing drawing functions on the LCD. Most of this functionality is available through the micropython interface, and you may wish to browse the uGFX documentation for more details

Basic usage

uGFX is comprised of 'widgets,' such as buttons and labels, and 'containers' which are used to group widgets.

To create a button on the screen, use ugfx.Button(x,y,width,height,text), and a button will be drawn on the screen.

As well as widgets, there are 'primitives' such as drawing circles and lines, which can be drawn anywhere on the screen or in a container. For example ugfx.circle(50,50,20,ugfx.RED) will draw a circle.


Detailed documentation

Note. all co-ordinates are from the top left (battery symbol) corner.

Colour format

Internally, ugfx uses 565 format (5 bits for red and blue, 6 for green). Preset colours are available, for example ugfx.RED, ugfx.ORANGE, etc. To convert from 24 bit RGB format, use ugfx.html_color(0xRRGGBB) to return the 16 bit 565 format.

Styles and Fonts

Use ugfx.set_default_font(...) to change the font. The font options are:

  • ugfx.FONT_SMALL
  • ugfx.FONT_MEDIUM
  • ugfx.FONT_MEDIUM_BOLD
  • ugfx.FONT_TITLE
  • ugfx.FONT_NAME.

Note that widgets use the font which was default when they were created, while the container.text() primitive uses the font that was default when the container was created

Containers

   .Container(x, y, width, height {,style})  # the style is optional

Containers can be used to group widgets together. They can also perform primitive drawing functions. When drawing widgets or primitives, the coordinates are relative to the top left corner of the container.

Containers can be shown or hidden, and all the widgets will be redrawn. Containers can also be placed on top of other widgets. When the top container is hidden, the widgets below will be redrawn.

Upon creation a style can be passed to a container, which will then be used by default by widgets created as part of that container. If no style is specified at creation, the current default style will be used.

The following example shows how to create a container, add an object and show it.

c = ugfx.Container(100,100,200,100)
b = ugfx.Button(10, 10, 40, 30, "OK", c)
c.show()            # the container will not be shown until this point

Primitives

All primitives can be drawn anywhere on the screen with, for example ugfx.circle(..), or anywhere within a container, with c=ugfx.Container(30,30,100,100); c.circle(..)

Clear

.clear(colour = ugfx.WHITE)

clears the screen to the specified colour. Warning, this is slow!

Lines

.line(<x1>, <y1>, <x2>, <y2>, <colour>)

.thickline(<x1>, <y1>, <x2>, <y2>, <colour>, <width>, <round>)

Draws a line from x1,y1 to x2,y2 using colour. Thickline will draw a line or arbitrary width, with the option of rounded corners

eg. ugfx.thickline(0,0,100,170,ugfx.YELLOW,7,0)

Circle

.circle(x, y, diameter, colour)

.fill_circle(x, y, diameter, colour)

Draws a circle at x,y of <diameter> using colour, either with a 1 pixel border or filling the area.

eg. ugfx.circle(180,150,40,ugfx.RED)

Arc

.arc(x, y, r, angle1, angle2, colour)

.fill_arc(x, y, r, angle1, angle2, colour)

Similar to the circle functions, however two angle parameters specify between which two angles drawing occurs

Ellipse

.ellipse(x, y, a, b, colour)

.fill_ellipse(x, y, a, b, colour)

Draws an ellipse at x,y of a width and b height using colour, either with a 1 pixel border or filling the area.

Rectangles

.box(1, y, a, b, colour)

.area(x, y, a, b, colour)

Draws a rectangle or filled rectangle at x,y of a width and b height using colour.

Polygon

.polygon(x, y, array, colour)

.fill_polygon(x, y, array, colour)

Draws or fills a polygon starting at x,y using colour. Array is an array of coordinates that specifies the corners.

eg. ugfx.circle(0,0, [ [0,20],[20,20],[20,0]], ugfx.RED)

Text

.text(x, y, text, colour)

Draws a text string text at x,y in colour. Note that a ugfx.text(..) call will take the default font, while container.text(..) will take the containers font.

eg. ugfx.text(40,40,"My name is...",ugfx.BLUE)

Other

.width()

.height()

Gets the height or width of the screen or container

Widgets

Widgets can be drawn anywhere on the screen, or within a container. The widgets take the optional parameter parent= to set the parent container. Widgets can have their style set on creation, otherwise will inherit

Widgets also accept input from the buttons. For example, the 'A' button can be 'attached' to an on-screen button, such that pressing the button on the badge causes the on-screen button to be redrawn in a depressed state.

Common

.text([text])

Gets or sets the text displayed by the widget.

.visible([show])

Gets or sets the visibility of the badge. b.visible(0) will hide, and b.visible(1) will show.

.attach_input(button, function)

Attaches a physical button to a widget, so that the user can cause the widget to redraw, for example to scroll or become depressed.

The input button specifies which button, with the options ugfx.BTN_A, ugfx.BTN_B, ugfx.BTN_MENU, ugfx.JOY_UP, ugfx.JOY_DOWN, ugfx.JOY_LEFT, ugfx.JOY_RIGHT.

The input function specifies what the button actually does. For example, the list has three different functions: scroll up, scroll down, and select. Note that some widgets by default attach the joystick to the relevant functions.

.detach_input(function)

Detaches an input. See above for more details.

.destroy()

Frees up all the resources assoicated with the object. While the micropython garbage collector will clear any old objects, the graphics library also has its own memory area, which can become full if objects are not destroyed after they are needed.

.set_focus()

Gives focus to the widget instance. Normally this will draw a box around the widget, the colour is specified by the style.

Button

b=ugfx.Button(x, y, a, b, text, *, parent=None, trigger=None, shape=ugfx.Button.RECT, style=None) (note: parameters after '*' are optional)

Draws a button at x,y having width a and height b. The option 'trigger' specifics which physical switch (if any) causes the display to be redrawn. The shape options are ugfx.Button.RECT, ugfx.Button.ROUNDED, ugfx.Button.ELLIPSE, ugfx.Button.ARROW_UP, ugfx.Button.ARROW_DOWN, ugfx.Button.ARROW_LEFT', ugfx.Button.ARROW_RIGHT.

Textbox

ugfx.Textbox(x, y, a, b, *, text=None, parent=None, maxlen=255})

Draws a text edit-box which can take input from the on-screen keyboard. Will automatically accept key-presses from the keyboard, which will edit the text. The textbox needs to have focus using .set_focus() for it to receive the key-presses.

Label

ugfx.Label(x, y, a, b, text, *, parent=None, style=None, justification=None)

A label displays text. Unlike the primitive text, this Label supports different justifications, wordwrap and changing text. The different justification options are ugfx.Label.LEFT, ugfx.Label.RIGHT, ugfx.Label.CENTER, ugfx.Label.LEFTTOP, ugfx.Label.RIGHTTOP and ugfx.Label.CENTERTOP.

List

.List(x, y, a, b, *, parent=None, up=ugfx.JOY_UP, down=ugfx.JOY_DOWN, style=None)

.enable_draw()

.disable_draw()

.add_item(text)

.assign_image(index, image)

.remove_item(index)

.selected_text()

.selected_index()

.count()

Keyboard

Styles

s=ugfx.Style()  # create a style based on the current default style

s.set_enabled([text_colour, edge_colour, fill_colour, progress_colour]) # sets the style for when something is enabled
s.set_pressed([text_colour, edge_colour, fill_colour, progress_colour]) # sets the style for when something is pressed
s.set_disabled([text_colour, edge_colour, fill_colour, progress_colour]) # sets the style for when something is disabled

s.set_focus(colour)  # sets the colour used for focus
s.set_background(colour) # sets the background colour for the style

ugfx.set_default_style(s) # use this style from now on

b=ugfx.Button(0,0,40,30,"OK", style=s) # use the style for this button

Tips and tricks

Tearing

When writing large areas of the screen, a 'tearing' [1] effect may be observed.

The screen module is comprised of a large memory, with one memory location to store the RGB data for each pixel. The LCD driver continuously updates the LCD pixels, by reading the memory in a sequential, line-by-line manner, and updating the LCD with the data from the memory. This 'read line-pointer' moves from the top to the bottom of the screen (when viewed in portrait), at about 70Hz (the refresh rate of the screen)

This large memory as part of the screen means it can be driven by a microcontroller which may have a considerably smaller memory. The microcontroller therefore only needs to update the memory when it whats the content to change.

Consider the scenario where the microcontroller wants to set the screen from one colour to another. The microcontroller needs to update the entire memory (320x240x2 = 153kB) with the new colour. At the same time the 'read line-pointer' is reading the same memory to update the LCD. In this case, tearing occurs if the 'read line-pointer' reads the top half of the memory containing the new colour, but then catches up with microcontroller writing to the memory, then the 'read line-pointer' starts reading the old colour in the bottom half of the memory.

To avoid tearing the 'read line-pointer' should not cross the region the microcontroller is updating. Since the microcontroller writes to the screen slightly slower than the LCD reads it, providing the microntroller starts writes to the top of the memory just after the LCD starts reading from the top, the read and write pointers will not overlap, and tearing will not occur. To sync the microcontroller with the LCD 'read line-pointer,' there is a vsync/tear output (connected to pin named 'TEAR') which is pulled high when the 'read line-pointer' reaches a given line (default is line 0). This can be turned on and off with ugfx.enable_tear() and ugfx.disable_tear(). To change the line at which the tear output is generated, use ugfx.set_tear_line(0..319).

Example code:

   ugfx.enable_tear()
   tear = pyb.Pin("TEAR", pyb.Pin.IN)
   
   def vsync():
       while tear.value() == 0:
           pass
       while tear.value:
           pass

Reducing power consumption

Use the following to dim the backlight, which uses about 80mA at full brightness

ugfx.backlight(b)     # sets the backlight. Range is 0-100
b = ugfx.backlight()   # reads the current backlight