| Reply | « Previous Thread | Next Thread » |
|
I'm struggling to use basic threading to keep my UI responsive. Here's the deal:
I've got a function that displays a random number on the screen every 5 seconds. The function chooses a random integer, blits it to screen, then sleeps for 5 seconds, then calls itself again. This would go on forever and ever, which is what I want. But because the function is calling itself, the exit key handler Code:
appuifw.app.exit_key_handler = quit So how can I implement a thread so that when the user presses the exit key the app will stop and return to the python console? I don't want to have to kill it every time with the red button and reload pys60. I don't really understand anything about implementing threads in application logic. Thanks, John |
|
The UI must be driven from the main thread. This is an appuifw limitation. It is not too bad, you just need to structure your program accordingly. You can create an event queue and insert events into it from callbacks and threads.
You can use this example as a base for your responsive GUI: Code:
#!/usr/bin/env python
# -*- coding: iso8859-1 -*-
# guiexample.py - A simple PyS60 GUI example
# 2008-12-09 by Jussi Ylänen
import appuifw
import e32
import random
class GUI(object):
# Special events have negative numbers.
# Non-negative numbers represent the selected entry when Listbox() pressed.
EVENT_EXIT = -1
EVENT_TIMER = -2
def __init__(self):
# Create a list for events, guarded by a lock.
self.events = []
self.eventlock = e32.Ao_lock()
# Create a periodic timer.
self.timer = e32.Ao_timer()
# GUI style is a double-item listbox.
self.lbox = appuifw.Listbox([(u"", u"")], self.selecthandler)
# Populate application menu.
self.menu = [(u"Update every 2 sec", lambda: self.settimer(2)),
(u"Update every 5 sec", lambda: self.settimer(5)),
(u"Update every 10 sec", lambda: self.settimer(10)),
(u"Exit", self.exithandler)]
# Set title.
appuifw.app.title = u"GUI Example"
# Set initial listbox contents.
self.setcontents([(u"Wait...", u"")])
# Show GUI.
appuifw.app.exit_key_handler = self.exithandler
appuifw.app.menu = self.menu
appuifw.app.body = self.lbox
# Allow GUI to be drawn.
e32.ao_yield()
# Start periodic timer.
self.timerticks = 0 # No EVENT_TIMER generated initally.
self.settimer(5) # Update every five seconds by default.
self.timerhandler() # Arm timer for the first time.
def close(self):
# Stop periodic timer.
self.settimer(0) # Special value to tell timerhandler() to stop
self.timer.cancel()
def run(self):
running = True
while running:
# Wait for events to arrive.
self.eventlock.wait()
# Handle events.
while len(self.events) > 0:
# Get the oldest event.
event = self.events.pop(0)
# Handle event.
if event == self.EVENT_EXIT:
running = False
break
elif event == self.EVENT_TIMER:
self.setcontents([(u"Random number",
unicode(self.randomvalue))])
elif event >= 0:
# Pressing the joystick/joypad does nothing in this example.
pass
def setcontents(self, contents):
pos = self.lbox.current()
newlen = len(contents)
if newlen == 0:
# Empty list, show one dummy line.
self.lbox.set_list([(u"", u"")])
elif pos < newlen:
# Use old position.
self.lbox.set_list(contents, pos)
else:
# New list too short, reset position.
self.lbox.set_list(contents)
# Allow GUI to be redrawn.
e32.ao_yield()
def settimer(self, timerperiod):
self.timerperiod = timerperiod
def addevent(self, event):
self.events.append(event)
self.eventlock.signal()
def selecthandler(self):
# Listbox() selection made.
self.addevent(self.lbox.current())
def exithandler(self):
# Exit menu selection made or Exit softkey pressed.
self.addevent(self.EVENT_EXIT)
def timerhandler(self):
if self.timerperiod > 0:
self.timerticks += 1
if self.timerticks >= self.timerperiod:
# Timer period is up.
self.timerticks = 0
# Choose another random value.
self.randomvalue = random.randrange(100)
# Signal GUI to update.
self.addevent(self.EVENT_TIMER)
# Re-arm timer.
self.timer.after(1, self.timerhandler)
# Main program starts here.
gui = GUI()
try:
gui.run()
finally:
gui.close()
# Application closes when the script ends.
appuifw.app.set_exit()
|
|
Thanks Jethro. That seems great, but rather more complicated than I need. I'm pleased to say I've solved the problem.
I overlooked the ao_timer : "The rationale for the Ao timer type is that you cannot cancel a pending e32.ao sleep" Works for me. Here's my code: Code:
import appuifw, e32, key_codes, graphics, random
def go():
img.clear()
chord = (random.choice(chords))
img.text((40,130),u"%s" % (chord), fill = (0,0,0), font = (None,50,graphics.FONT_BOLD|graphics.FONT_ANTIALIAS))
canvas.blit(img)
e32.reset_inactivity()
global pause
t.after(pause, go)
def increase():
global pause
pause = pause + 1
def decrease():
global pause
pause = pause - 1
def quit():
t.cancel()
app_lock.signal()
t = e32.Ao_timer()
pause = 2.5
chords = []
f = file(u"e:\\Python\\chords.txt","r")
for line in f:
line = line.strip()
chords.append(line)
canvas = appuifw.Canvas()
appuifw.app.body = canvas
w,h = canvas.size
img = graphics.Image.new((w,h))
canvas.bind(key_codes.EKeySelect, go)
canvas.bind(key_codes.EKey3, increase)
canvas.bind(key_codes.EKey1, decrease)
appuifw.app.exit_key_handler = quit
go()
app_lock = e32.Ao_lock()
app_lock.wait()
|
|
Oh, OK then.
Here's a couple of thoughts about your program: A canvas does not keep its contents if it's obscured by, say, a popup note. Normally the canvas redraw callback is implemented to handle such situations. Your program redraws the whole canvas periodically, so it's probably not an issue here. What might be an issue is that some phones allow changing between portrait and landscape modes dynamically, causing the canvas dimensions to change. There's a resize callback for detecting that. |
| Reply | « Previous Thread | Next Thread » |
| Thread Tools | Search this Thread |
|---|---|
| Rate This Thread | |
| Thread | Thread Starter | Forum | Replies | Last Post |
|---|---|---|---|---|
| S60 5th Edition SDK, EMULATOR crashing | zoiks_guy | Symbian Tools & SDKs | 4 | 2009-01-16 06:37 |
| 为什么会启动这么多的线程 | shaojieli | Symbian | 1 | 2008-08-19 06:34 |
| Problem with Audio & Thread | ecio83 | Symbian Media (Graphics & Sounds) | 3 | 2007-07-16 17:45 |
| Thread problem | pillar | Mobile Java General | 0 | 2006-03-01 10:53 |
| Link errors when trying to Display bitmap | Bkc82 | Symbian Media (Graphics & Sounds) | 1 | 2006-01-16 23:46 |