×
Namespaces

Variants
Actions

Archived:Python on Symbian/08. Touch 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 Authors: Pankaj Nathani

This chapter explains how to write touch-aware applications for Symbian devices in Python, and how to support touch and non-touch devices with the same script.

Contents

Introduction

Touch-enabled user interfaces are more natural than interfaces that rely solely on hardware buttons or a keypad. While some applications work well with both types of interfaces, some applications are improved or are only usable if they have a touch interface.

The Symbian platform supports devices with and without touch interfaces. At the time of writing, popular Symbian "touch" devices include:

  • Nokia N8, C5, C7, 5800 XpressMusic., N97
  • Samsung i8910 HD
  • Sony Ericsson Satio
  • Sony Ericsson Vivaz

This chapter explains how to detect whether touch is supported on the device, how to handle touch events from different regions of the screen in your canvas, and how Python will handle the case where overlapping touch screen event handlers have been declared. It is accompanied by a number of worked example applications and games that demonstrate how to create your own custom touch-enabled UI elements.

Supporting both touch and non-touch devices

Python applications that use only the basic user interface elements described in Chapter 4 (e.g. notifications, list boxes, dialogs and so on) should not require any changes in order to run on touch-enabled devices. This is because events generated when the user touches a UI element are mapped to the same events that the application receives from physical key presses on a non-touch device.

Python on Symbian v2.0 provides explicit support for touch pointer events in the appuifw module's Canvas object. Applications that use a canvas for drawing can easily be made to work sensibly for both touch and non-touch devices by using an on-screen virtual directional keypad to simulate the action of the physical keypad, as discussed in Chapter 7.

The following example shows how the virtual keypad can be enabled on touch devices only.

import appuifw
...
if appuifw.touch_enabled():
appuifw.app.directional_pad=True;
else:
appuifw.app.directional_pad=False;
...

The preceding code uses the touch_enabled() function to determine if the device is touch enabled, and returns True if touch UI is supported and False if not.

Applications that support custom touch-enabled Canvas elements can use this method conditionally to enable the code for touch devices.

Detecting touch events in full-screen mode

Python can detect four different touch events, which are referenced using the following key codes (from key_codes.py):

  • EButton1Down - Touch down event, sent when finger/stylus touches the screen
  • EButton1Up - Touch up event, sent when finger/stylus is lifted from the screen
  • EDrag – Drag event, sent when finger/stylus is dragged on the screen
  • ESwitchOn - Tap screen event, sent when finger/stylus taps or double clicks the screen

In order to detect touch events, the Canvas binds an event handler to the required key codes, which is called whenever the specified event occurs. The approach is much the same as that used to bind key presses to the Canvas as we discussed in Chapter 7.

The following code snippet shows an application that will display a notification whenever the screen is touched. The Canvas binds to the EButton1Down event, but you can bind to other touch events by replacing EButton1Down with EButton1Up , EDrag or ESwitchOn.

import appuifw
import graphics
import e32
import key_codes #import key_codes - required for touch event detection
 
# define white colour constant
RGB_WHITE =(255, 255, 255)
 
# change application screen size to 'full'
appuifw.app.screen = 'full'
 
def quit():
'''Define quit function'''
app_lock.signal()
 
def down_event(event):
'''Pen DOWN event handler'''
appuifw.note(u"Down Event")
 
#prepare canvas for drawing
canvas = appuifw.Canvas()
appuifw.app.body = canvas
 
#define right soft key as exit key
appuifw.app.exit_key_handler = quit
 
#clear canvas with white colour
canvas.clear(RGB_WHITE)
 
'''bind canvas for touch event'''
canvas.bind(key_codes.EButton1Down, down_event)
#EButton1Up, EDrag and ESwitchOn could be used similarly
 
#Wait for user to exit
app_lock = e32.Ao_lock()
app_lock.wait()

In the preceding snippet, the EButton1Down event is bound to the down_event() function. The callback function can be replaced by calling the bind() function again with a different handler, or it can be cleared by specifying None as shown.

canvas.bind(key_codes.EButton1Down, None)

Detecting touch on a specific area

In the previous section, we showed how touch events can be detected on the full canvas. Detecting events on a specific area of the canvas is achieved by passing the top left and bottom right co-ordinates of the required region as additional parameters to the bind() call.

PySymbian doesn't offer any dedicated UI controls for buttons, but they can be provided as shapes drawn onto the canvas, and can be rounded, or even circular. The bind() approach only allows us to specify rectangular areas, so at the end of this section, we'll explain how to detect touch events in arbitrarily shaped regions.

Detecting a touch event within a rectangle

In order to detect a touch down event in a rectangle with top left corner co-ordinates (x1, y1) and bottom right corner co-ordinates (x2, y2), we would use:

canvas.bind(key_codes.EButton1Down, down_event, ((x1,y1),(x2,y2)))

The following code snippet illustrates how we detect each of the different touch events in a different area of the canvas/screen:

'''Detecting touch over a specific area of screen'''
 
import appuifw
import graphics
import e32
import key_codes #import key_codes - required for touch event detection
 
# define colour constants
RGB_RED = (255, 0, 0)
RGB_GREEN = (0, 255, 0)
RGB_BLUE = (0, 0, 255)
RGB_PURPLE = (100,0,255)
RGB_BLACK = (0,0,0)
 
# disable directional pad
appuifw.app.directional_pad = False
 
#prepare canvas for drawing
canvas = appuifw.Canvas()
appuifw.app.body = canvas
 
#obtaining canvas size (Total_x and Total_y)
Total_x, Total_y = canvas.size
y1 = Total_y/4
 
def blue_down(event):
''' Blue DOWN event handler '''
appuifw.note(u"Blue Down")
 
def green_up(event):
''' Green UP event handler '''
appuifw.note(u"Green Up")
 
def red_drag(event):
''' Red DRAG event handler '''
appuifw.note(u"Red Drag")
 
def purple_tap(event):
''' Purple TAP event handler '''
appuifw.note(u"Purple Tap")
 
# Blue rectangle - DOWN Event
'''Draw Blue rectangle and text'''
canvas.rectangle(((0,0), (Total_x,y1)), fill=RGB_BLUE, width=5)
canvas.text((Total_x/2,y1/2), u"DOWN", fill = RGB_BLACK,font=(u'Nokia Hindi S60',40,appuifw.STYLE_BOLD))
'''Bind DOWN to Blue rectangle'''
canvas.bind(key_codes.EButton1Down, blue_down, ((0,0), (Total_x,y1)))
 
# Green rectangle - UP Event
'''Draw Green rectangle and text'''
canvas.rectangle(((0,y1), (Total_x,2*y1)), fill=RGB_GREEN, width=5)
canvas.text((Total_x/2,3*y1/2), u"UP", fill = RGB_BLACK,font=(u'Nokia Hindi S60',40,appuifw.STYLE_BOLD))
'''Bind UP to Blue rectangle'''
canvas.bind(key_codes.EButton1Up, green_up, ((0,y1), (Total_x,2*y1)))
 
# Red rectangle - DRAG Event
'''Draw Red rectangle and text'''
canvas.rectangle(((0,2*y1), (Total_x,3*y1)), fill=RGB_RED, width=5)
canvas.text((Total_x/2,5*y1/2), u"DRAG", fill = RGB_BLACK,font=(u'Nokia Hindi S60',40,appuifw.STYLE_BOLD))
'''Bind DRAG to Red rectangle'''
canvas.bind(key_codes.EDrag, red_drag, ((0,2*y1), (Total_x,3*y1)))
 
# Purple rectangle - TAP Event
'''Draw Purple rectangle and text'''
canvas.rectangle(((0,3*y1), (Total_x,4*y1)), fill=RGB_PURPLE, width=5)
canvas.text((Total_x/2,7*y1/2), u"TAP", fill = RGB_BLACK,font=(u'Nokia Hindi S60',40,appuifw.STYLE_BOLD))
'''Bind TAP to Red rectangle'''
canvas.bind(key_codes.ESwitchOn, purple_tap, ((0,3*y1), (Total_x,4*y1)))
 
#wait for user to exit
app_lock = e32.Ao_lock()
app_lock.wait()

When run, the example application looks like the screenshot shown in Figure 8.1. Each area of the screen responds to its specified type of event by displaying an information note. Note: If a drag event goes outside of a bound area, no drag events will be received.

Figure 8.1 Event detection in four different canvas areas

Piano example (overlapping events)

Screen areas that bind to any oneparticular event keycode may overlap. Within the overlap region, only the callback function that was registered last will be called in response to an event. However, areas that bind to different event keycodes may overlap without issue, and both callbacks are invoked if both events occur in an overlap region.

Consider the following examples:

Figure 8.2 Overlapping areas

For Overlapping 1:

  • Touching on point 1 triggers the callback registered to the red rectangle.
  • Touching on point 3 triggers the callback registered to the blue rectangle.
  • Touching on point 2 (a region where overlap occurs) triggers the callback registered to the blue rectangle only.

For Overlapping 2:

  • Touching on point 1 triggers the callback registered to the red rectangle.
  • Touching on point 2 (a region where overlap occurs) triggers callback registered to the blue rectangle.

This is further illustrated with the example of a touch piano:

Figure 8.3 Touch Piano

The black keys overlap the white keys. To ensure that touching the overlapping area (black keys) triggers the callback functions registered to the respective black keys, we bind their event handlers last.

import appuifw
import graphics
import e32
import key_codes #import key_codes - required for touch event detection
import audio
 
# define white colour constants
RGB_WHITE =(255, 255, 255)
RGB_BLACK =(0, 0, 0)
 
# define sound files
white1_sound= u"C:\\Data\\Sounds\\Digital\\white1.mp3"
white2_sound= u"C:\\Data\\Sounds\\Digital\\white2.mp3"
white3_sound= u"C:\\Data\\Sounds\\Digital\\white3.mp3"
white4_sound= u"C:\\Data\\Sounds\\Digital\\white4.mp3"
white5_sound= u"C:\\Data\\Sounds\\Digital\\white5.mp3"
white6_sound= u"C:\\Data\\Sounds\\Digital\\white6.mp3"
black1_sound= u"C:\\Data\\Sounds\\Digital\\black1.mp3"
black2_sound= u"C:\\Data\\Sounds\\Digital\\black2.mp3"
black3_sound= u"C:\\Data\\Sounds\\Digital\\black3.mp3"
black4_sound= u"C:\\Data\\Sounds\\Digital\\black4.mp3"
 
# change application screen size to 'full'
appuifw.app.screen = 'full'
 
# disable directional pad
appuifw.app.directional_pad = False
 
def quit():
'''Define quit function'''
app_lock.signal()
appuifw.app.exit_key_handler = quit
 
def white1(event):
'''White1 callback function'''
#The path of the file that is to be played when white1 is touched
s1 = audio.Sound.open(white2_sound)
try: # Try to stop() - the file may be previously playing
s1.stop()
s1.play()
except:
s1.play()
 
def white2(event):
'''White2 callback function'''
#The path of the file that is to be played when white1 is touched
s2 = audio.Sound.open(white2_sound)
try: # Try to stop() - the file may be previously playing
s2.stop()
s2.play()
except:
s2.play()
 
def white3(event):
'''White3 callback function'''
#The path of the file that is to be played when white1 is touched
s3 = audio.Sound.open(white3_sound)
try: # Try to stop() - the file may be previously playing
s3.stop()
s3.play()
except:
s3.play()
 
def white4(event):
'''White4 callback function'''
#The path of the file that is to be played when white1 is touched
s4 = audio.Sound.open(white4_sound)
try: # Try to stop() - the file may be previously playing
s4.stop()
s4.play()
except:
s4.play()
 
def white5(event):
'''White5 callback function'''
#The path of the file that is to be played when white1 is touched
s5 = audio.Sound.open(white5_sound)
try: # Try to stop() - the file may be previously playing
s5.stop()
s5.play()
except:
s5.play()
 
def white6(event):
'''White6 callback function'''
#The path of the file that is to be played when white1 is touched
s6 = audio.Sound.open(white6_sound)
try: # Try to stop() - the file may be previously playing
s6.stop()
s6.play()
except:
s6.play()
 
def black1(event):
'''Black1 callback function'''
#The path of the file that is to be played when white1 is touched
s7 = audio.Sound.open(black1_sound)
try: # Try to stop() - the file may be previously playing
s7.stop()
s7.play()
except:
s7.play()
 
def black2(event):
'''Black2 callback function'''
#The path of the file that is to be played when white1 is touched
s8 = audio.Sound.open(black2_sound)
try: # Try to stop() - the file may be previously playing
s8.stop()
s8.play()
except:
s8.play()
 
def black3(event):
'''Black3 callback function'''
#The path of the file that is to be played when white1 is touched
s9 = audio.Sound.open(black3_sound)
try: # Try to stop() - the file may be previously playing
s9.stop()
s9.play()
except:
s9.play()
 
def black4(event):
'''Black4 callback function'''
#The path of the file that is to be played when white1 is touched
s10 = audio.Sound.open(black4_sound)
try: # Try to stop() - the file may be previously playing
s10.stop()
s10.play()
except:
s10.play()
 
#prepare canvas for drawing
canvas = appuifw.Canvas()
appuifw.app.body = canvas
 
#define right soft key as exit key
appuifw.app.exit_key_handler = quit
 
Total_x, Total_y = canvas.size
y1 = Total_y/6
 
#clear canvas with white colour
canvas.clear(RGB_WHITE)
 
#White Key #1 - Draw and bind touch Pen down event
canvas.rectangle(((0,0), (Total_x,y1)), outline=RGB_BLACK, fill=RGB_WHITE, width=3)
canvas.bind(key_codes.EButton1Down, white1, ((0,0), (Total_x,y1)))
 
#White Key #2 - Draw and bind touch Pen down event
canvas.rectangle(((0,y1), (Total_x,2*y1)),outline=RGB_BLACK, fill=RGB_WHITE, width=3)
canvas.bind(key_codes.EButton1Down, white2, ((0,y1), (Total_x,2*y1)))
 
#White Key #3 - Draw and bind touch Pen down event
canvas.rectangle(((0,2*y1), (Total_x,3*y1)), outline=RGB_BLACK, fill=RGB_WHITE, width=3)
canvas.bind(key_codes.EButton1Down, white3, ((0,y1), (Total_x,2*y1)))
 
#White Key #4 - Draw and bind touch Pen down event
canvas.rectangle(((0,3*y1), (Total_x,4*y1)), outline=RGB_BLACK,fill=RGB_WHITE, width=3)
canvas.bind(key_codes.EButton1Down, white4, ((0,3*y1), (Total_x,4*y1)))
 
#White Key #5 - Draw and bind touch Pen down event
canvas.rectangle(((0,4*y1), (Total_x,5*y1)), outline=RGB_BLACK,fill=RGB_WHITE, width=3)
canvas.bind(key_codes.EButton1Down, white5, ((0,4*y1), (Total_x,5*y1)))
 
#White Key #6 - Draw and bind touch Pen down event
canvas.rectangle(((0,5*y1), (Total_x,6*y1)), outline=RGB_BLACK,fill=RGB_WHITE, width=3)
canvas.bind(key_codes.EButton1Down, white6, ((0,5*y1), (Total_x,6*y1)))
 
#Black Key #1 - Draw and bind touch Pen down event
canvas.rectangle(((0,y1-30), (Total_x/2,y1+30)), fill=RGB_BLACK, width=3)
canvas.bind(key_codes.EButton1Down, black1, ((0,y1-30), (Total_x/2,y1+30)))
 
#Black Key #2 - Draw and bind touch Pen down event
canvas.rectangle(((0,2*y1-30), (Total_x/2,2*y1+30)), fill=RGB_BLACK, width=3)
canvas.bind(key_codes.EButton1Down, black2, ((0,2*y1-30), (Total_x/2,2*y1+30)))
 
#Black Key #3 - Draw and bind touch Pen down event
canvas.rectangle(((0,4*y1-30), (Total_x/2,4*y1+30)), fill=RGB_BLACK, width=3)
canvas.bind(key_codes.EButton1Down, black3, ((0,4*y1-30), (Total_x/2,4*y1+30)))
 
#Black Key #4 - Draw and bind touch Pen down event
canvas.rectangle(((0,5*y1-30), (Total_x/2,5*y1+30)), fill=RGB_BLACK, width=3)
canvas.bind(key_codes.EButton1Down, black4, ((0,5*y1-30), (Total_x/2,5*y1+30)))
 
#Wait for user to exit
app_lock = e32.Ao_lock()
app_lock.wait()

Detecting touch events in arbitrary regions

The bind() function lets us receive touch events that occur in a rectangular area. But what do we do when we want to use other shapes, for example, buttons that have rounded edges, or some other arbitrary shape?

There are three basic approaches that we can use:

  • We can approximate the area using a single rectangle, which not very nice, but may be acceptable for a slightly rounded rectangular button
  • We can build up the required polygon using a number of overlapping or adjacent rectangles
  • We can detect events in the bounding rectangle of our shape, and then, within the callback function, determine whether the touch event was inside the customized shape.

The following code snippet demonstrates the third approach, using the example of a circular area in which we want to detect touch events.

import appuifw
import graphics
import e32
import key_codes #import key_codes - required for touch event detection
import math
 
# define colour constants
RGB_BLUE = (0, 0, 255)
 
# disable directional pad
appuifw.app.directional_pad = False
 
#prepare canvas for drawing
canvas = appuifw.Canvas()
appuifw.app.body = canvas
 
#obtaining canvas size (Total_x and Total_y)
Total_x, Total_y = canvas.size
 
# define circle centre co-ordinates and radius
circle_x=Total_x/2
circle_y=Total_y/2
radius=100
 
def calculate(centre, radius):
'''Function to calculate co-ordinates of the circle'''
return ((centre[0]-radius, centre[1]-radius), (centre[0]+radius, centre[1]+radius))
 
def blue_down(pos):
''' Blue DOWN event handler '''
global circle_x,circle_y, radius
# Use the 'distance formula' to check if the touched position is within the circle
distance=math.hypot(pos[0]-circle_x, pos[1]-circle_y)
if distance<=radius:
appuifw.note(u"Circle touched")
 
# Blue rectangle - DOWN Event
'''Draw Blue rectangle and text'''
canvas.ellipse((calculate((circle_x, circle_y), radius)), fill=RGB_BLUE)
canvas.bind(key_codes.EButton1Down, blue_down)
 
#wait for user to exit
app_lock = e32.Ao_lock()
app_lock.wait()

The blue_down(pos) function is called if a touch event is called anywhere within the bounding rectangle of the circle, and it uses the position of the touch and the radius of the circle to determine whether the touch occured inside the circle. If so, it displays an information note.

Figure 8.4 shows the preceding code in action.

Painting with touch

Using what we learned in this chapter and in the earlier chapters, let’s make a simple application similar to 'Paint' on Windows.

First we prepare a blank canvas (as in previous examples). Then we draw a point where we detect any touch event on the canvas. Since we need to detect more than one event type on the same area (full canvas) – we use the default event_callback() function of the canvas instead of calling bind() to bind to a single event.

canvas = appuifw.Canvas(event_callback=handle_event, redraw_callback=handle_redraw) 
appuifw.app.body = canvas
appuifw.app.exit_key_handler = quit

Whenever the canvas is touched, handle_event() is called, and receives the event information passed as an argument. The event information is a dictionary containing the details related to the pointer event. It has the following format (which remains unchanged for non-touch devices):

  • 'type': one of the several pointer events - EButton1Down, EButton1Up, EDrag, ESwitchOn
  • 'modifiers': the modifiers that apply to this pointer event
  • ‘pos’: a tuple containing the x-y pointer co-ordinates

In the handle_event() function we detect the touch event and draw a point:

def handle_event(event):
if event['type'] not in (key_codes.EButton1Up, key_codes.EButton1Down,key_codes.EDrag):
return
canvas.point((event['pos'][0], event['pos'][1]),outline=RGB_RED,width=10)

This first implementation of the paint application did not quite work as expected. Since we didn't get an event at every point, we didn't draw a continuous line, as shown in Figure 8.5.

Figure 8.5 Touchy Paint

In order to fix this, we modify the handle_event() function, shown as follows. This simply ensures that for a EDrag event we draw a line from the previous touched co-ordinate to the received dragged co-ordinate. We also draw two points when we detect touch to improve the resolution of drawing on the screen.

def handle_event(event):
if event['type'] not in (key_codes.EButton1Up, key_codes.EButton1Down,key_codes.EDrag):
return
if event['type'] in (key_codes.EButton1Down, key_codes.EButton1Up):
canvas.point((event['pos'][0], event['pos'][1]),outline=RGB_RED,width=10)
canvas.point((event['pos'][0], event['pos'][1]),outline=RGB_RED,width=10)
prev_pos[0]=event['pos'][0]
prev_pos[1]=event['pos'][1]
 
#appuifw.note(u"Green Down")
elif event['type'] ==key_codes.EDrag:
rect=(prev_pos[0], prev_pos[1],event['pos'][0], event['pos'][1])
canvas.line(rect, outline=RGB_RED, width=10,fill=RGB_RED)
prev_pos[0]=event['pos'][0]
prev_pos[1]=event['pos'][1]

The complete code of the paint application is as follows, with a screenshot in Figure 8.6.

import appuifw
import graphics
import e32
import key_codes #import key_codes - required for touch event detection
 
# disable directional pad
appuifw.app.directional_pad=False;
 
# define colour constants
RGB_RED = (255, 0, 0)
RGB_GREEN = (0, 255, 0)
RGB_BLUE = (0, 0, 255)
RGB_WHITE =(255, 255, 255)
 
canvas=None
global prev_pos
prev_pos=[0,0]
 
# change application screen size to 'full'
appuifw.app.screen = 'large'
 
def handle_redraw(event):
'''Define redraw function'''
pass
 
def handle_event(event):
'''Define event callback function'''
if event['type'] not in (key_codes.EButton1Up, key_codes.EButton1Down,key_codes.EDrag):
return
if event['type'] in (key_codes.EButton1Down, key_codes.EButton1Up):
canvas.point((event['pos'][0], event['pos'][1]),outline=RGB_RED,width=10)
canvas.point((event['pos'][0], event['pos'][1]),outline=RGB_RED,width=10)
prev_pos[0]=event['pos'][0]
prev_pos[1]=event['pos'][1]
if event['type'] ==key_codes.EDrag:
rect=(prev_pos[0], prev_pos[1],event['pos'][0], event['pos'][1])
canvas.line(rect, outline=RGB_RED, width=10,fill=RGB_RED)
prev_pos[0]=event['pos'][0]
prev_pos[1]=event['pos'][1]
 
canvas = appuifw.Canvas(event_callback=handle_event, redraw_callback=handle_redraw)
appuifw.app.body = canvas
appuifw.app.exit_key_handler = quit
 
# Clear the canvas
canvas.clear(RGB_WHITE)
 
app_lock = e32.Ao_lock()
app_lock.wait()
Figure 8.6 Touchy Paint

Touchy Tic-Tac-Toc

We finish up this chapter with a quick and easy game –Tic-Tac-Toe (the rules of which can be found on Wikipedia).

import appuifw
import graphics
import e32
import key_codes #import key_codes - required for touch event detection
 
# define colour constants
RGB_RED = (255, 0, 0)
RGB_GREEN = (0, 255, 0)
RGB_BLUE = (0, 0, 255)
RGB_BLACK=(0,0,0)
 
# define board colour
board_colour=RGB_BLUE
 
# define flag variables
turn_flag="Cross"
flag_rect1=None
flag_rect2=None
flag_rect3=None
flag_rect4=None
flag_rect5=None
flag_rect6=None
flag_rect7=None
flag_rect8=None
flag_rect9=None
 
def handle_redraw(event):
pass
 
# disable directional pad
appuifw.app.directional_pad = False
 
canvas = appuifw.Canvas(redraw_callback=handle_redraw)
appuifw.app.body = canvas
 
Total_x, Total_y = canvas.size
 
RectWidth=Total_x/3
RectHeight=Total_y/3
print RectWidth,RectHeight
 
def draw_cross(pos, cross_colour):
'''Function to draw a cross - on the co-ordinates passed as arguments'''
cross_length=30
cross_width=15
a=pos[0]-cross_length
b=pos[1]-cross_length
while a<=pos[0]+cross_length:
canvas.point((a,b), width=cross_width, outline=cross_colour)
a+=1
b+=1
a=pos[0]+cross_length
b=pos[1]-cross_length
while a>=pos[0]-cross_length and b<=pos[1]+cross_length:
canvas.point((a,b), width=cross_width, outline=cross_colour)
a-=1
b+=1
 
def check_winner():
'''Function to check the winner after each turn'''
global flag_rect1,flag_rect2,flag_rect3,flag_rect4,flag_rect5,flag_rect6,flag_rect7,flag_rect8,flag_rect9
if flag_rect1=="Cross" and flag_rect2=="Cross" and flag_rect3=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect1=="Zero" and flag_rect2=="Zero" and flag_rect3=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect1=="Cross" and flag_rect4=="Cross" and flag_rect7=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect1=="Zero" and flag_rect4=="Zero" and flag_rect7=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect1=="Cross" and flag_rect5=="Cross" and flag_rect9=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect1=="Zero" and flag_rect5=="Zero" and flag_rect9=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect2=="Cross" and flag_rect5=="Cross" and flag_rect8=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect2=="Zero" and flag_rect5=="Zero" and flag_rect8=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect3=="Cross" and flag_rect6=="Cross" and flag_rect9=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect3=="Zero" and flag_rect6=="Zero" and flag_rect9=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect3=="Cross" and flag_rect5=="Cross" and flag_rect7=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect3=="Zero" and flag_rect5=="Zero" and flag_rect7=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect1=="Cross" and flag_rect2=="Cross" and flag_rect3=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect1=="Zero" and flag_rect2=="Zero" and flag_rect3=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect4=="Cross" and flag_rect5=="Cross" and flag_rect6=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect4=="Zero" and flag_rect5=="Zero" and flag_rect6=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect7=="Cross" and flag_rect8=="Cross" and flag_rect9=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect7=="Zero" and flag_rect8=="Zero" and flag_rect9=="Zero":
appuifw.note(u"Zero wins!")
 
def down(pos):
''' Event handler '''
global turn_flag, flag_rect1,flag_rect2,flag_rect3,flag_rect4,flag_rect5,flag_rect6,flag_rect7,flag_rect8,flag_rect9
if 0<pos[0]<RectWidth and 0<pos[1]<RectHeight:
 
if flag_rect1 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect1="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect1="Zero"
turn_flag="Cross"
check_winner()
elif RectWidth<pos[0]<2*RectWidth and 0<pos[1]<RectHeight:
 
if flag_rect2 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect2="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect2="Zero"
turn_flag="Cross"
check_winner()
elif 2*RectWidth<pos[0]<3*RectWidth and 0<pos[1]<RectHeight:
 
if flag_rect3 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect3="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect3="Zero"
turn_flag="Cross"
check_winner()
elif 0<pos[0]<RectWidth and RectHeight<pos[1]<2*RectHeight:
 
if flag_rect4 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect4="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect4="Zero"
turn_flag="Cross"
check_winner()
print turn_flag, flag_rect4
elif RectWidth<pos[0]<(2*RectWidth) and RectHeight<pos[1]<2*RectHeight:
 
if flag_rect5 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect5="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect5="Zero"
turn_flag="Cross"
check_winner()
elif 2*RectWidth<pos[0]<3*RectWidth and RectWidth<pos[1]<2*RectHeight:
 
if flag_rect6 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect6="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect6="Zero"
turn_flag="Cross"
check_winner()
elif 0<pos[0]<RectWidth and 2*RectHeight<pos[1]<3*RectHeight:
 
if flag_rect7 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect7="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect7="Zero"
turn_flag="Cross"
check_winner()
 
elif RectWidth<pos[0]<2*RectWidth and 2*RectHeight<pos[1]<3*RectHeight:
 
if flag_rect8 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect8="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect8="Zero"
turn_flag="Cross"
check_winner()
 
elif 2*RectWidth<pos[0]<3*RectWidth and 2*RectHeight<pos[1]<3*RectHeight:
 
if flag_rect9 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect9="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect9="Zero"
turn_flag="Cross"
check_winner()
 
# Draw rectangles #1 #2 and #3
canvas.bind(key_codes.EButton1Down, down, ((0,0), (RectWidth,RectHeight)))
canvas.bind(key_codes.EButton1Down, down, ((RectWidth,0), (2*RectWidth,RectHeight)))
canvas.bind(key_codes.EButton1Down, down, ((2*RectWidth,0), (3*RectWidth,RectHeight)))
canvas.rectangle(((0,0), (RectWidth,RectHeight)), fill=board_colour, width=5,outline=RGB_BLACK)
canvas.rectangle(((RectWidth,0), (2*RectWidth,RectHeight)), fill=board_colour, width=5, outline=RGB_BLACK)
canvas.rectangle(((2*RectWidth,0), (3*RectWidth,RectHeight)), fill=board_colour, width=5,outline=RGB_BLACK)
 
# Draw rectangles #4 #5 and #6
canvas.bind(key_codes.EButton1Down, down, ((0,RectHeight), (RectWidth,2*RectHeight)))
canvas.bind(key_codes.EButton1Down, down, ((RectWidth,RectHeight), (2*RectWidth,2*RectHeight)))
canvas.bind(key_codes.EButton1Down, down, ((2*RectWidth,RectHeight), (3*RectWidth,2*RectHeight)))
canvas.rectangle(((0,RectHeight), (RectWidth,2*RectHeight)), fill=board_colour, width=5,outline=RGB_BLACK)
canvas.rectangle(((RectWidth,RectHeight), (2*RectWidth,2*RectHeight)), fill=board_colour, width=5,outline=RGB_BLACK)
canvas.rectangle(((2*RectWidth,RectHeight), (3*RectWidth,2*RectHeight)), fill=board_colour, width=5,outline=RGB_BLACK)
 
# Draw rectangles #7 #8 and #9
canvas.bind(key_codes.EButton1Down, down, ((0,2*RectHeight), (RectWidth,3*RectHeight)))
canvas.bind(key_codes.EButton1Down, down, ((RectWidth,2*RectHeight), (2*RectWidth,3*RectHeight)))
canvas.bind(key_codes.EButton1Down, down, ((2*RectWidth,2*RectHeight), (3*RectWidth,3*RectHeight)))
canvas.rectangle(((0,2*RectHeight), (RectWidth,3*RectHeight)), fill=board_colour, width=5,outline=RGB_BLACK)
canvas.rectangle(((RectWidth,2*RectHeight), (2*RectWidth,3*RectHeight)), fill=board_colour, width=5,outline=RGB_BLACK)
canvas.rectangle(((2*RectWidth,2*RectHeight), (3*RectWidth,3*RectHeight)), fill=board_colour, width=5,outline=RGB_BLACK)
 
app_lock = e32.Ao_lock()
app_lock.wait()

Let's go through this code in pieces. After importing the required modules, the color constants and flags used in the game are initialized.

# define colour constants
RGB_RED = (255, 0, 0)
RGB_GREEN = (0, 255, 0)
RGB_BLUE = (0, 0, 255)
RGB_BLACK=(0,0,0)
 
# define board colour
board_colour=RGB_BLUE
 
# define flag variables
turn_flag="Cross"
flag_rect1=None
flag_rect2=None
flag_rect3=None
flag_rect4=None
flag_rect5=None
flag_rect6=None
flag_rect7=None
flag_rect8=None
flag_rect9=None

In this example, we need to draw 2 shapes: a circle and cross. The circle is quite simple and can be achieved by a single line. Drawing a cross is a little more complex so we define a function to do this whenever needed, called draw_cross(pos, cross_colour).

def draw_cross(pos, cross_colour):
'''Function to draw a cross - on the co-ordinates passed as arguments'''
cross_length=30
cross_width=15
a=pos[0]-cross_length
b=pos[1]-cross_length
while a<=pos[0]+cross_length:
canvas.point((a,b), width=cross_width, outline=cross_colour)
a+=1
b+=1
a=pos[0]+cross_length
b=pos[1]-cross_length
while a>=pos[0]-cross_length and b<=pos[1]+cross_length:
canvas.point((a,b), width=cross_width, outline=cross_colour)
a-=1
b+=1

down(pos) is the callback function that we bind to the down touch events. Here we check if the last shape drawn was a circle or a cross and accordingly draw the other one (since the players take turns to choose a square). A circle is drawn the first time the code is run because was initialized as "Cross".

def down(pos):
''' Event handler '''
global turn_flag, flag_rect1,flag_rect2,flag_rect3,flag_rect4,flag_rect5,flag_rect6,flag_rect7,flag_rect8,flag_rect9
if 0<pos[0]<RectWidth and 0<pos[1]<RectHeight:
 
if flag_rect1 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect1="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect1="Zero"
turn_flag="Cross"
check_winner()
elif RectWidth<pos[0]<2*RectWidth and 0<pos[1]<RectHeight:
 
if flag_rect2 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect2="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect2="Zero"
turn_flag="Cross"
check_winner()
elif 2*RectWidth<pos[0]<3*RectWidth and 0<pos[1]<RectHeight:
 
if flag_rect3 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect3="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect3="Zero"
turn_flag="Cross"
check_winner()
elif 0<pos[0]<RectWidth and RectHeight<pos[1]<2*RectHeight:
 
if flag_rect4 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect4="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect4="Zero"
turn_flag="Cross"
check_winner()
print turn_flag, flag_rect4
elif RectWidth<pos[0]<2*RectWidth and RectHeight<pos[1]<2*RectHeight:
 
if flag_rect5 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect5="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect5="Zero"
turn_flag="Cross"
check_winner()
elif 2*RectWidth<pos[0]<3*RectWidth and RectWidth<pos[1]<2*RectHeight:
 
if flag_rect6 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect6="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect6="Zero"
turn_flag="Cross"
check_winner()
elif 0<pos[0]<RectWidth and 2*RectHeight<pos[1]<3*RectHeight:
 
if flag_rect7 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect7="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect7="Zero"
turn_flag="Cross"
check_winner()
 
elif RectWidth<pos[0]<2*RectWidth and 2*RectHeight<pos[1]<3*RectHeight:
 
if flag_rect8 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect8="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect8="Zero"
turn_flag="Cross"
check_winner()
 
elif 2*RectWidth<pos[0]<3*RectWidth and 2*RectHeight<pos[1]<3*RectHeight:
 
if flag_rect9 not in ("Cross", "Zero"):
if turn_flag=="Cross":
draw_cross(pos, RGB_GREEN)
flag_rect9="Cross"
turn_flag="Zero"
else:
canvas.point(pos,0x00ff00,width=50)
flag_rect9="Zero"
turn_flag="Cross"
check_winner()

After drawing each shape (Cross or Zero), we call the function. This checks if there are 3 correctly aligned zeros or crosses, and if so, declares the winner using a note.

def check_winner():
'''Function to check the winner after each turn'''
global flag_rect1,flag_rect2,flag_rect3,flag_rect4,flag_rect5,flag_rect6,flag_rect7,flag_rect8,flag_rect9
if flag_rect1=="Cross" and flag_rect2=="Cross" and flag_rect3=="Cross"):
appuifw.note(u"Cross wins!")
elif flag_rect1=="Zero" and flag_rect2=="Zero" and flag_rect3=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect1=="Cross" and flag_rect4=="Cross" and flag_rect7=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect1=="Zero" and flag_rect4=="Zero" and flag_rect7=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect1=="Cross" and flag_rect5=="Cross" and flag_rect9=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect1=="Zero" and flag_rect5=="Zero" and flag_rect9=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect2=="Cross" and flag_rect5=="Cross" and flag_rect8=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect2=="Zero" and flag_rect5=="Zero" and flag_rect8=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect3=="Cross" and flag_rect6=="Cross" and flag_rect9=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect3=="Zero" and flag_rect6=="Zero" and flag_rect9=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect3=="Cross" and flag_rect5=="Cross" and flag_rect7=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect3=="Zero" and flag_rect5=="Zero" and flag_rect7=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect1=="Cross" and flag_rect2=="Cross" and flag_rect3=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect1=="Zero" and flag_rect2=="Zero" and flag_rect3=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect4=="Cross" and flag_rect5=="Cross" and flag_rect6=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect4=="Zero" and flag_rect5=="Zero" and flag_rect6=="Zero":
appuifw.note(u"Zero wins!")
elif flag_rect7=="Cross" and flag_rect8=="Cross" and flag_rect9=="Cross":
appuifw.note(u"Cross wins!")
elif flag_rect7=="Zero" and flag_rect8=="Zero" and flag_rect9=="Zero":
appuifw.note(u"Zero wins!")

Summary

This chapter has shown how to add touch support to your applications, and how to make conditional on whether touch is supported by the device.

It's also shown how you can register the whole Canvas or just specific areas for touch events, and how you can create custom touch-enabled UI elements, and used a number of examples, including a touch piano, paint application and a tic-tac-toe game, to illustrate the key points.

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 09:16.
90 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.

×