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
No related wiki articles found