×
Namespaces

Variants
Actions

Archived:Python on Symbian/04. Basic User Interface

From Nokia Developer Wiki
Jump to: navigation, search

Archived.pngAquivado: Este artigo foi arquivado, pois o conteúdo não é mais considerado relevante para se criar soluções comerciais atuais. Se você achar que este artigo ainda é importante, inclua o template {{ForArchiveReview|escreva a sua justificativa}}.

All PySymbian articles have been archived. PySymbian is no longer maintained by Nokia and is not guaranteed to work on more recent Symbian devices. It is not possible to submit apps to Nokia Store.

Original Author: Bogdan Galiceanu

This chapter explains how to create a Python application with native look and feel, and how you can generate notifications and queries from non-GUI applications.

Comes with Code: File:PythonOnSymbianBookExampleCode ApplicationUI.zip

File:PythonOnSymbianBookExampleCode appuifw notes.zipca
File:PythonOnSymbianBookExampleCode appuifw popup menu.zip
File:PythonOnSymbianBookExampleCode appuifw queries.zip
File:PythonOnSymbianBookExampleCode Double listbox.zip
File:PythonOnSymbianBookExampleCode Form.zip
File:PythonOnSymbianBookExampleCode globalui notes.zip
File:PythonOnSymbianBookExampleCode globalui popup menu.zip
File:PythonOnSymbianBookExampleCode globalui queries.zip
File:PythonOnSymbianBookExampleCode Infopopup.zip
File:PythonOnSymbianBookExampleCode Menu with submenu.zip
File:PythonOnSymbianBookExampleCode Multi selection list.zip
File:PythonOnSymbianBookExampleCode Orientation.zip
File:PythonOnSymbianBookExampleCode Screen size.zip
File:PythonOnSymbianBookExampleCode Simple menu.zip
File:PythonOnSymbianBookExampleCode Single listbox.zip
File:PythonOnSymbianBookExampleCode Tabs.zip
File:PythonOnSymbianBookExampleCode Text.zip
File:PythonOnSymbianBookExampleCode TopWindow.zip

Contents

Introduction

Python provides access to most of the UI elements available on the Symbian platform, allowing rapid development of applications that are indistinguishable from those written in native code. With a few lines of code you can write menu-driven applications that include text editors, listboxes, dialogs and forms, and that display notifications and queries.

The functions and classes that create an application’s native-looking UI are part of the appuifw (application user interface framework) module. This module also provides the functionality for creating the application’s menu and handling the application’s exit process. Another module, globalui, provides visual elements (notifications and queries) for applications that do not have a UI environment or are running the background.

The UI Structure of a Python Application

Python applications have the same visual structure as Symbian applications written in other programming languages. The large number of UI elements available to Python means that the UI can be customized to meet the needs of most developers.

The following figure represents the skeleton of a Python application. It shows which UI element corresponds to each area. Except for dialogs, every element is part of the Application instance (an instance that is always present when the appuifw module is used and which is referred to as app).

nonelLink=
Figure 4.1 The structure of a Python application

A basic Python application does not require you to specify anything about its visual construction - there are default implementations for the menu and title so a script still runs if these are not defined. The application also has default softkey labels which cannot be changed: the left softkey is always called "Options" and the right one is always called "Exit".

Note.pngNote: Though Python does not provide a way to modify the softkey labels, you can modify them using a native extension (see the Extending PySymbian chapter).

Most serious applications define the main elements of the user interface - a UI control for the application's body (the 'body' is the main part of an application - see Figure 4.1 above), a menu and a right softkey. Note that the right softkey should, unless the nature of the application dictates otherwise, call a function that closes the application.

The code fragment below shows a basic application with title, a text control for its body, and a menu. The application displays the current time in the text editor whenever the Show time menu option is selected. The user can exit the application with the Exit menu option or by selecting the right softkey.

import appuifw, e32, time
 
 
#Set the application title
appuifw.app.title = u"MyApplication"
 
#Create an instance of a Text control and set it as the application's body
appuifw.app.body = t = appuifw.Text()
 
def show_time(): t.add(unicode(time.ctime()))
 
def quit():
app_lock.signal()
appuifw.app.set_exit()
 
#Create a menu, defining callback functions show_time and quit respectively
appuifw.app.menu = [(u"Show time", show_time), (u"Exit", quit)]
 
#Set the function to be called when the right softkey is pressed (quit)
appuifw.app.exit_key_handler = quit
 
#Create an instance of Ao_lock - an active object based synchronization service
app_lock = e32.Ao_lock()
 
#Wait for application lock to be signalled
app_lock.wait()

A normal Python script runs from start to end and then exits - the behaviour of the application is a little different.

First the application imports the application framework and e32 modules, and defines the individual UI elements (these are largely self-explanatory, as you would expect in a Python code). It then creates an instance of Ao_lock and waits on it. Ao_lock is an active scheduler based synchronization service. Even though the execution of the script stops at this point the application is still running and responding to menu and softkey events.

The application continues to run until the user selects Exit from the menu or the right softkey (both of which have been set up to call the quit() method). The quit() method signals the application lock and the active scheduler terminates.

A standalone application remains open until the application framework object's set_exit() method is called - this contrasts with a script run in the shell, which terminates once it reaches completion. In order to exit gracefully, an application must call appuifw.app.set_exit() in the exit callback (the quit() function in our examples).

The following sections of this chapter describe the elements that can make up an application's graphical user interface (GUI). We recommend that you consult the PySymbian documentation in addition to these tutorials for further information.

Title, Orientation and Screen Size

The application title can be changed by assigning a Unicode string to the title attribute of the Application instance. For example, the line:

appuifw.app.title = u"MyApplication"

sets the title to "MyApplication".

Title change.jpg

Figure 4.2 Changing the application title

Python applications automatically respond to orientation change events. If an application needs to maintain a certain orientation then you can do this using the orientation attribute of the Application instance, which can be given one of the following values: 'automatic' (the default value), 'portrait' or 'landscape'.

import appuifw, e32
 
 
#Set the orientation to 'landscape'
appuifw.app.orientation = 'landscape'
#Wait 3 seconds
e32.ao_sleep(3)
#Set the orientation to 'portrait'
appuifw.app.orientation = 'portrait'
PythonOnSymbian4.3 Landscape.jpg

Figure 4.3 The application in landscape orientation


The size of the application body can be modified using the screen atribute, which takes one of three possible values:

  • 'normal' - the default. The title and navigation panes and the softkey labels are visible
  • 'large' - the application uses the entire upper area of the screen. The softkey labels are visible
  • 'full' - the application uses the whole screen.

The following snippet and screenshots demonstrate the three screen sizes:

import appuifw, e32
 
 
#Set the screen to 'full'
appuifw.app.screen = 'full'
#Wait 3 seconds
e32.ao_sleep(3)
#Set the screen to 'large'
appuifw.app.screen = 'large'
#Wait 3 seconds
e32.ao_sleep(3)
#Set the screen to 'normal'
appuifw.app.screen = 'normal'


Figure 4.4 The three screen sizes

Notifications

Notes are native-looking popup messages that an application can display to inform the user about various things. Three types of notes are provided: 'conf' (confirmation), 'info' (information) and 'error' (error). Each type has a different icon and plays an appropriate sound.

Example code:

import appuifw
 
appuifw.note(u"Process complete", 'conf')
appuifw.note(u"Low memory", 'info')
appuifw.note(u"Operation aborted!", 'error')

The following screenshots show the result:

Figure 4.5 appuifw notes

The notifications in the globalui module are slightly more varied, as you can be seen in this code snippet and the screenshots that follow:

import globalui
 
 
globalui.global_note(u"Item available", 'info')
globalui.global_note(u"An error has occured!", 'error')
globalui.global_note(u"Message", 'text')
globalui.global_note(u"Warning!", 'warn')
globalui.global_note(u"Operation in progress", 'wait')
globalui.global_note(u"Permanent note", 'perm')
globalui.global_note(u"Operation complete", 'confirm')
globalui.global_note(u"", 'charging')
globalui.global_note(u"", 'not_charging')
globalui.global_note(u"", 'battery_full')
globalui.global_note(u"", 'battery_low')
globalui.global_note(u"", 'recharge_battery')

Figure 4.6 globalui notes

Queries

Queries enable applications to request user input - such as a password or the answer to a question. The input can be in various formats depending on the type of the query ('text', 'time', 'date', 'number', 'float', 'code' or 'query').

Example code:

import appuifw
 
 
word = appuifw.query(u"Enter a word", 'text')
time = appuifw.query(u"Enter desired time", 'time')
date = appuifw.query(u"Enter a date", 'date')
number = appuifw.query(u"Enter a number", 'number')
decimal_number = appuifw.query(u"Enter a decimal number", 'float')
password = appuifw.query(u"Type in a password", 'code')
answer = appuifw.query(u"Are you sure?", 'query')
first_name, last_name = appuifw.multi_query(u"Enter first name", u"Enter last name")

These screenshots illustrate what each query looks like:

Figure 4.7 appuifw queries

Now we take a look at the globalui queries. There are only two kinds available: global_query and global_msg_query. The first one is a simple confirmation style query while the second one allows you to specify a title in addition to the text. Both kinds can have a timeout (given as the last argument to their functions), which means that if the user doesn't answer after a number of seconds, the query disappears.

import globalui
 
 
globalui.global_query(u"Are you sure?")
globalui.global_msg_query(u"File transfer will begin in 5 seconds", u"Confirm operation", 5)

Figure 4.8 Queries of globalui

Menu

The application’s menu consists of a list of tuples containing labels (Unicode strings) and callbacks (functions that are executed when the corresponding menu item is selected). There are two kinds of tuples that can be used:

  • (label, callback) - a simple menu
  • (label, ((label, callback))) - a menu with a submenu

The menu can be set simply by assigning the list of tuples to the menu attribute of the Application instance (app).

A simple menu:

import appuifw, e32, time
 
 
#Create an instance of Ao_lock - an active object based synchronization service
app_lock = e32.Ao_lock()
 
#Define a function that stops the script execution; it will be called when "Exit" is selected from the menu
def quit(): app_lock.signal()
 
def show_time(): print time.ctime()
 
appuifw.app.menu = [(u"Show time", show_time), (u"Exit", quit)]
 
#Do not stop the script execution until "Exit" is selected
app_lock.wait()

A menu with a submenu:

import appuifw, e32
 
 
app_lock = e32.Ao_lock()
 
def quit(): app_lock.signal()
 
def hello(): print "Hello"
 
def goodbye(): print "Goodbye"
 
appuifw.app.menu = [(u"Message", ((u'Say "Hello"', hello), (u'Say "Goodbye"', goodbye))), (u"Exit", quit)]
 
app_lock.wait()


The following screenshots illustrate the two kinds of menus:

Figure 4.9 Menus

Another type of menu is the popup menu. Like the name suggests, it is displayed automatically when invoked. The function used to create one is popup_menu(list[, label]). list is a list of Unicode strings that represent the menu's entries. label is an optional title the menu can be given.

import appuifw
 
 
colours = [u"Red", u"Green", u"Blue"]
 
colour = appuifw.popup_menu(colours, u"Pick a colour")


Figure 4.10 Popup menu

The globalui module provides a global popup menu that can have a header and a timeout. It is almost identical in appearance to the popup menu from appuifw, but unlike that one, it can be shown outside the Python application.

import globalui
 
 
#Make a list of Unicode strings - the menu's entries
options = [u"Omlette", u"Bacon and eggs", u"Cereal", u"Sandwich"]
 
#Show the menu for 5 seconds and display what was selected
choice = globalui.global_popup_menu(options, u"Breakfast options:", 5)
print options[choice] + " was selected."
Figure 4.11 Global popup menu

Selection Lists

A selection list is a dialog that displays a list from which the user can select an entry. It can have a search field that can be used to find entries (the search field only becomes visible when the user starts typing letters to search for). The multi selection list allows several entries to be selected.

To create a selection list, we use either the selection_list or the multi_selection_list function with a list of Unicode strings as its argument.

import appuifw
 
 
entries = [u"Carrots", u"Potatoes", u"Onions", u"Tomatoes"]
 
selected = appuifw.multi_selection_list(entries, style="checkbox", search_field=1)
 
for i in selected:
print entries[i] + " was selected"


Figure 4.12 A multi selection list


Listbox

A Listbox is similar to a selection list. While it doesn’t allow multiple selections and doesn’t have the search field option, it allows you to specify what keys perform actions on the listbox (key binding) and to control which item is highlighted, as well as using icons with the entries. It can even have two rows for every line.

The constructor for the Listbox class takes a list of tuples and a callback function as its arguments. The callback is executed when a selection occurs, and the list can have one of the following structures:

  • [Unicode_string1, Unicode_string2] - one row on every line; without icons
  • [(Unicode_string1, Unicode_description1), (Unicode_string2, Unicode_description2)] - two rows on every line; without icons
  • [(Unicode_string1, icon1), (Unicode_string2, icon2)] - one row on every line; with icons
  • [(Unicode_string1, Unicode_description1, icon1), (Unicode_string2, Unicode_description2, icon2)] - two rows on every line; with icons

The bind(event_code, callback method binds the specified key code to the specified callback function. Basically, it detects when a certain key is pressed and executes the function.

Using the current method, one can retrieve the index of the currently highlighted entry. This can also be set by using the set_list(list[, current] method, where list is a list of tuples as described above, and current is the index of the item that we want to highlight.

As a first example, we will create a simple Listbox and bind the left and right arrow keys. When the right arrow key is pressed, the next item in the list becomes highlighted; the opposite happens for the left arrow key:

import appuifw, e32
from key_codes import *
 
 
app_lock = e32.Ao_lock()
#Define the exit function
def quit():
app_lock.signal()
appuifw.app.exit_key_handler = quit
 
#The list of entries
items = [u"1", u"2", u"3", u"4"]
 
#Define the function for the arrow keys
def move(direction):
if direction == 'right':
if lb.current() == len(items) - 1:
lb.set_list(items, 0)
else:
lb.set_list(items, lb.current() + 1)
elif direction == 'left':
if lb.current() == 0:
lb.set_list(items, len(items) - 1)
else:
lb.set_list(items, lb.current() - 1)
 
#Create an instance of Listbox and set it as the application's body
lb = appuifw.Listbox(items, lambda:None)
appuifw.app.body = lb
 
#Bind the keys
lb.bind(EKeyRightArrow, lambda:move('right'))
lb.bind(EKeyLeftArrow, lambda:move('left'))
 
app_lock.wait()

Now we can see what a more complex Listbox looks like - one with two rows for every entry and icons (details about the Icon class can be found in the PySymbian documentation).

import appuifw, e32
 
 
app_lock = e32.Ao_lock()
#Define the exit function
def quit():
app_lock.signal()
appuifw.app.exit_key_handler = quit
 
#Define the icons to be used
file_icon = appuifw.Icon(u"Z:\\resource\\apps\\avkon2.mif", 17504, 17505)
folder_icon = appuifw.Icon(u"Z:\\resource\\apps\\avkon2.mif", 17506, 17507)
 
#Create a list of items to be displayed
#An item consists of two fields (Unicode strings) and an icon
items = [(u"File", u"Unknown type", file_icon), (u"Folder", u"Empty", folder_icon)]
 
#Define a function that is called when an item is selected
def handle_selection():
appuifw.note(items[lb.current()][0] + u" has been selected.", 'info')
 
#Create an instance of Listbox and set it as the application's body
lb = appuifw.Listbox(items, handle_selection)
appuifw.app.body = lb
 
app_lock.wait()

Figure 4.13 Listbox

Forms

The Form class allows us to create a customizable multi-field dialog. This means that it supports several fields at the same time, which can be of various types, editable or read-only.

The following example demonstrates how to create and use a Form:

import appuifw, time
 
 
#A list of objects, such as books
titles = [u"Quick Recipes on Symbian OS", u"Java ME on Symbian OS", u"S60 Programming - A Tutorial Guide"]
 
#Create the fields to be displayed in the form
fields = [(u"Domain", 'text', u"Progamming"),
(u"Title", 'combo', (titles, 0)),
(u"Amount",'number', 1),
(u"Date", 'date', time.time()),
(u"Time", 'time')]
 
#Initialize a boolean variable to know whether the form is saved
saved = False
 
#Define a function to be called when the form is saved
def save(arg):
global saved
saved = True
return True
 
#Create a Form object
myForm = appuifw.Form(fields, flags=appuifw.FFormEditModeOnly)
 
#Assign the save function
myForm.save_hook = save
 
#Execute the form
myForm.execute()
 
#After the form is saved and closed, display the information
if saved == True:
print myForm[0][2]
print titles[myForm[1][2][1]]
print myForm[2][2]
print time.strftime("%d/%m/%Y", time.localtime(myForm[3][2]))
print time.strftime(time.ctime(myForm[4][2])[11:20])


Figure 4.14 Form

Now let's take a closer look at what each part of the code above does. First, we create the fields that will be used in the form as a list of tuples containing the label, type and, optionally, the default value for each field.

After that we define a function that handles the saving. This function will be called every time when the contents of an executing Form dialog are saved. It takes a list representing the current contents of the form as an argument and returns a Boolean value. If it returns True, the list is set as the new contents of the form. If it returns False, the form's fields are reset to their previous values. We tell the form to use this save function by assigning it to the save_hook attribute.

After creating a form and launching it (using the execute method), we demonstrate how to check the information the user has entered. After it's saved, we simply print the new contents.

One noticeable fact is that the second argument passed to the form's constructor, flags, is set to appuifw.FFormEditModeOnly. This specifies that the form is to remain in edit mode while it is being executed. Other possible types of forms are:

  • FFormViewModeOnly - the form cannot be edited
  • FFormAutoLabelEdit - allows the user to edit the labels of the fields
  • FFormAutoFormEdit - allows the user to add or remove fields from the form
  • FFormDoubleSpaced - puts the label and value on different lines (practically makes each fields take up two lines)

Text Editor

The Text class provided by the appuifw module is a customizable way of displaying text and receiving input. It provides methods for modifying the text it contains (either the content itself or the font and color of the text) and retrieving it.

An instance of Text can be customized by giving different values to its attributes. A complete list of attributes along with their descriptions and possible values is available in the PySymbian documentation. There you can also find all the methods you can use to manipulate the editor's functionality.

import appuifw, e32
 
 
app_lock = e32.Ao_lock()
 
def quit():
app_lock.signal()
appuifw.app.exit_key_handler = quit
 
#Create an instance of Text and set it as the application's body
appuifw.app.body = t = appuifw.Text()
 
#Set the color of the text
t.color = 0xEE00DD
 
#Set the color for the highlight of the text
t.highlight_color = 0xFFFF00
 
#Set the font by name, size and flags
t.font = (u"Nokia Hindi S60", 25, None)
 
#Highlight the text and set the style of the text to bold and strikethrough
t.style = (appuifw.HIGHLIGHT_STANDARD | appuifw.STYLE_BOLD | appuifw.STYLE_STRIKETHROUGH)
 
#Write text to see the effect
t.add(u"Your text here")
 
app_lock.wait()
Figure 4.15 Text editor example


Tabs

A Python application can make use of tabs in order to allow the user to easily switch between interfaces. All you need to do is create the interfaces using the available UI controls and define a function that handles switching from one to another. When closing the application, the tabs have to be removed manually (i.e., giving an empty list of tab titles and setting the function to None). It should be noted that the tab handler function is called automatically by the framework and thus no explicit key mapping is needed.

Example code:

import appuifw, e32
 
 
app_lock = e32.Ao_lock()
 
def quit():
appuifw.app.set_tabs([], None)
app_lock.signal()
appuifw.app.exit_key_handler = quit
 
#Define the tabs
tab1 = appuifw.Text(u"This is tab #1")
tab2 = appuifw.Text(u"This is tab #2")
 
#Create a function that handles switching between tabs
def tab_handler(index):
#Switch to the tab according to index
if(index==0):
appuifw.app.body = tab1
if(index==1):
appuifw.app.body = tab2
 
#Set the tabs
appuifw.app.set_tabs([u"One", u"Two"], tab_handler)
 
#Open the first tab
appuifw.app.body = tab1
 
#Wait for the user to request the exit
app_lock.wait()
Figure 4.16 Tabs interface

What we've done is create two Text instances (tab1 and tab2), one for each tab, and define the function that will be used for switching between them. The index argument is passed to the function by the framework and is used to decide which tab should become active.

InfoPopup

InfoPopup is a class that allows you to display a message in a certain place on the screen that is inaccessible to a standard note, and for a certain number of seconds. It is especially useful when you want to display tips or instructions.

The show method takes as its arguments the text as a Unicode string. Optionally, other arguments can be specified, such as the coordinates of the top left corner of the message box, the amount of time the message is shown, the amount of time to wait before showing the message (in milliseconds), and the alignment. A complete list of alignment constants and their descriptions is available in the PySymbian documentation.

Example code:

import appuifw
 
 
#Create an instance of InfoPopup
popup = appuifw.InfoPopup()
 
#Show the popup for 7 seconds at coordinates (150, 160)
popup.show(u"Message", (150, 160), 7000)


Figure 4.17 InfoPopup

TopWindow

The TopWindow class (defined in the topwindow module) allows you to create customizable popups containing an image or text.

A TopWindow is simply a rectangular area that obscures other screen elements and that displays an image. Its aspect can be customized to an extent, allowing you to specify whether or not it has a shadow, if the corners are round, and other things. It can be used dynamically, meaning it can be shown and hidden at any time and its position can be changed easily.

Example code:

import appuifw, e32, graphics, topwindow
 
 
app_lock = e32.Ao_lock()
#Define the exit function
def quit():
app_lock.signal()
appuifw.app.exit_key_handler = quit
 
#Instantiate a TopWindow
window = topwindow.TopWindow()
 
#Define its size and the coordinates of its top left corner
window.size = (210, 160)
window.position = (10, 40)
 
#Create a new, blank image and colour it, then write a message on it
img = graphics.Image.new((195, 110))
img.clear(0xFF0000)
img.text((25, 25), u"This is a topwindow example", font = 'title')
 
#Place the image on the TopWindow object and define some of the objects visual characteristics
window.add_image(img, (10, 10))
window.background_color = 0x00FF00
window.shadow = 4
window.corner_type = 'corner5'
 
#Display the TopWindow
window.show()
 
app_lock.wait()
Figure 4.18 TopWindow

Summary

This chapter introduced to you the structure of standard Python applications and the UI elements that are available. With very little coding involved, these elements allow you to customize an application's look and feel to meet your requirements. In many examples, throughout the book, you will find these UI elements used again and understand how these can be a part of full fledged applications.


Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. Portions copyright Bogdan Galiceanu, Hamish Willee, Marcelo Barros de Almeida, Mike Jipping, Pankaj Nathani and others in wiki document history list. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 8 May 2013, at 15:03.
97 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×