You Are Here:

Community: Wiki

This page was last modified on 6 September 2009, at 19:15.

How to Not to use Timers

From Forum Nokia Wiki

Reviewer Approved   

I've tried to use timers for animation with limited, but not complete, success. Small sample code was easy, but 16 Puzzle v3.0 is now 800+ lines of code. Somehow moving from simple Timer cases to more complex ones wasn't easy at all, something went wrong all the time!

Here I have documented all found errors you can make with Timer, how to identify each and fix them. There's a few correct ways, too, I hope.

My experiments continue, but it's beginning to look better. In general I very strongly propose not to both use asynchronous and synchronous timers in same application. More exactly I very strongly propose using only async timers (the ones with callback function). That might require a bit more complex application logic, but life with timers is much easier. Not to mention you have a possibility to save battery.

Usage: press keyboard numbers 1, 2, 3, 4, 5 and 6.

#
# Timer Sample
#
# Usage: This is for developers, so read the source code.
# This is NOT a tutorial, but an error reference.
#
# I went through most possible errors anyone can do with Timers.
# After all the trouble I want to demonstrate how timers can be
# used and how to identify some of the mistakes. Hope it helps!
#
# Copyright (c) 2008-2009 Jouni Miettunen
# http://jouni.miettunen.googlepages.com/
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
'''
1.20 2009-05-05 Added time problems with class (timer_problem6)
1.10 2008_09-03 Added RunTimeError: negative number not allowed
1.00 2008-06-12 Initial release
'
''
 
import e32
import appuifw
import key_codes
import random
 
g_count = 0
g_limit = 3
 
# You can use just one timer for whole program
# Just ALWAYS remember to cancel it before use
my_timer = e32.Ao_timer()
 
# Not a timer, but close (Active Object) relative
app_lock = e32.Ao_lock()
 
def get_color():
''' Generate random RGB color, used to proof that something happens '''
return (random.choice(range(0,255)), random.choice(range(0,255)), random.choice(range(0,255)))
 
def timer_color1():
''' Timer callback without parameters '''
global g_count
if g_count < g_limit:
g_count += 1
else:
g_count = 0
return
 
# Change screen color, show that something happens
canvas.clear(get_color())
# You might call this routine before timer has expired
# Solution: always cancel timer beforehands
my_timer.cancel()
# Note: Timer interval is in SECONDS
my_timer.after(1, timer_color1)
# Watch closely and see pattern: color change + dialog
# Program execution continued immediately after timer set
# Timer trickers callback function after given interval
appuifw.note(unicode('Count: ' + str(g_count)))
# Screen goes white, because we're missing redraw callback
# Dialog removal requests screen update --> blank by default
# Not a defect as itself, redraw is missing on purpose
 
# Error:
# Python: System error (-50)
# TypeError: bad argument type for built-in operation
# Solution:
# Add time interval (SECONDS)
# This is invalid sample code, do not use:
# my_timer.after(timer_color1)
 
# Error:
# RuntimeError: negative number not allowed
# Reason:
# Your time interval was a negative number
# Solution:
# Add error checking to verify time interval is non-negative
# This is invalid sample code, do not use:
# my_interval = -1
# my_timer.after(my_interval, timer_color1)
 
def timer_color2(a_color=(0, 0, 0)):
''' Timer callback with parameters '''
global g_count
if g_count < g_limit:
g_count += 1
else:
g_count = 0
return
 
# If no color was given (EKeyRightArrow) then it's default (now BLACK)
# Otherwise given color is used, see end of this function
canvas.clear(a_color)
# You might call this before timer has expired
# Solution: always cancel timer beforehands
my_timer.cancel()
# If you want to give parameters to callback
# you MUST use lambda == standard python expression
# Note: Timer interval is in SECONDS
my_timer.after(0.5, lambda:timer_color2(get_color()))
 
# Error:
# Python: System error (-50)
# TypeError: callable expected for 2nd argument
# Solution:
# Use lambda when callback has parameters
# This is invalid sample code, do not use:
# my_timer.after(0.5, timer_color2(get_color()))
 
# Error:
# Python: System error (-50)
# RuntimeError: maximum recursion depth exceeded
# Reason:
# Your timer callback called itself in loop too many times
# Most likely because of invalid callback setup
# Solution:
# Use something to limit and break recursion
# Check your code
# This is invalid sample code, do not use:
# my_timer.after(0.01, timer_color2(get_color()))
 
def timer_crash3():
''' Sample how to crash without timer.cancel '''
canvas.clear(get_color())
# Error:
# Python: System error (-50)
# RunTimerError: Timer pending - cancel first
# Reason:
# Crash if you call this before timer has expired
# Missing cancel on active timer
# Solution:
# add my_timer.cancel() just before my_timer.after()
# Note:
# Everything is ok, IF you call this AFTER timer has expired
# Always cancel timer beforehands, just in case
my_timer.after(2, timer_crash3)
 
def timer_color4():
''' Blocking timer sample '''
# Change screen color, show that something happens
canvas.clear(get_color())
# You might call this before timer has expired
# Solution: always cancel timer beforehands
my_timer.cancel()
# Note: Timer interval is in SECONDS
#my_timer.after(2, timer_color1)
my_timer.after(2)
# Watch closely and see pattern: color change, wait, dialog
# Program execution was blocked and continued only after timer expired
appuifw.note(u'After timer expired')
# Screen goes white, because we're missing redraw callback
# Dialog removal requests screen update --> blank by default
# Not a defect as itself, redraw is missing on purpose
 
def timer_crash5():
''' Sample how to crash without timer.cancel '''
canvas.clear(get_color())
my_timer.cancel()
# Error:
# Application closed:
# Python
# E32USER-CBase 91
# Reason:
# Timer is active and blocking function execution
# User started same function and tried to cancel active timer
# In this case function was started via keyboard event
# SDK docs say:
# "This panic is raised by CActiveSchedulerWait::Start() when
# the CActiveSchedulerWait object has already been started."
# Solution:
# Avoid this situation
# Note:
# Everything is ok, IF you call this AFTER timer has expired
my_timer.after(2)
 
def timer_problem6():
''' Timer callback inside class '''
my_timer.cancel()
tc = TimerClass()
 
# WARNING: this is wrong, callback is never called
appuifw.note(u'TimerClass 1 Before')
my_timer.after(0.1, lambda:tc.timer_call1)
appuifw.note(u'TimerClass 1 After')
# Error:
# Nothing happens
# Reason:
# Missing parenthesis in callback with lambda
# Solution:
# add parenthesis
# Note:
# No error note, search trace output for something like
# <bound method TimerClass.timer_call1 of <__main__.TimerClass object at 0x31f08534>>
 
# Wait a moment to make these different cases visible
my_timer.after(1)
 
# This is right, no lambda and no parenthesis
appuifw.note(u'TimerClass 2 Before')
my_timer.after(0.1, tc.timer_call2)
appuifw.note(u'TimerClass 2 After')
 
# Wait a moment to make these different cases visible
my_timer.after(1)
 
# This is right, lambda and parenthesis
appuifw.note(u'TimerClass 3 Before')
my_timer.after(0.1, lambda:tc.timer_call3())
appuifw.note(u'TimerClass 3 After')
 
# Wait a moment to make these different cases visible
# my_timer.after(1)
 
# WARNING: this is wrong, parenthesis without lambda
# Note: commented away, since this stops/crashes function execution
# appuifw.note(u'TimerClass 4 Before')
# my_timer.after(0.1, tc.timer_call4())
# appuifw.note(u'TimerClass 4 After')
 
# Error:
# Python: System error (-50)
# TypeError: callable expected for 2nd argument
# Reason:
# Missing lambda on callback with parenthesis
# Solution:
# add lambda
# Note:
# Traceback gives filename and source code line number for error
 
def cb_quit():
# There is possible crash if exit while timer is active
# Cancel it always before exit, just in case
my_timer.cancel()
app_lock.signal()
 
class TimerClass(object):
''' Minimal sample class '''
def show(self, a_value):
s = "TimerClass %d Inside" % a_value
appuifw.note(unicode(s))
def timer_call1(self):
self.show(1)
def timer_call2(self):
self.show(2)
def timer_call3(self):
self.show(3)
def timer_call4(self):
self.show(4)
 
appuifw.app.orientation = 'portrait'
appuifw.app.screen = 'full'
appuifw.app.exit_key_handler = cb_quit
appuifw.app.menu = [(u"Exit", cb_quit)]
appuifw.app.body = canvas = appuifw.Canvas()
 
canvas.bind(key_codes.EKey1, timer_color1)
canvas.bind(key_codes.EKey2, lambda:timer_color2())
 
# Error:
# Application closed:
# Python
# KERN-EXEC 3
# Reason:
# Missing lambda on callback with parenthesis
# Solution:
# add lambda
# This is invalid sample code, do not use:
# canvas.bind(key_codes.EKey2, timer_color2())
 
# Error:
# Nothing happens
# Reason:
# Missing parenthesis in callback with lambda
# Solution:
# add parenthesis
# This is invalid sample code, do not use:
# canvas.bind(key_codes.EKey2, lambda:timer_color2)
 
canvas.bind(key_codes.EKey3, timer_crash3)
canvas.bind(key_codes.EKey4, timer_color4)
canvas.bind(key_codes.EKey5, timer_crash5)
canvas.bind(key_codes.EKey6, timer_problem6)
 
app_lock.wait()

Here are some screenshots

Image:timer_sample.jpg

Related Wiki Articles

No related wiki articles found

Rate This

 
Bookmark this page: DeliciousDiggFacebookGoogleYahooStumbleUponRedditDiigoTechnocratiTwitter  Share this page Share this page Print this Page Print this page Invite a friend Invite a friend
京ICP备05048969号    Email Newsletters Press Terms & Conditions Privacy Policy Sitemap Contact Us © 2009 Nokia