You Are Here:

Community: Developer Discussion Boards

#1 Old simple thread problem (I think!) - 2008-12-09, 18:57

Join Date: Mar 2008
Posts: 127
jtullis
Offline
Regular Contributor
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
is no longer responsive.

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
Reply With Quote

#2 Old Re: simple thread problem (I think!) - 2008-12-09, 21:04

Join Date: May 2004
Posts: 524
Location: Tampere, Finland
jethro.fn's Avatar
jethro.fn
Offline
Forum Nokia Champion
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()
You can call gui.addevent(...) from other threads as well. This example uses only timed callbacks, so threads are not needed.
Reply With Quote

#3 Old Re: simple thread problem (I think!) - 2008-12-09, 23:02

Join Date: Mar 2008
Posts: 127
jtullis
Offline
Regular Contributor
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()
Reply With Quote

#4 Old Re: simple thread problem (I think!) - 2008-12-10, 07:54

Join Date: May 2004
Posts: 524
Location: Tampere, Finland
jethro.fn's Avatar
jethro.fn
Offline
Forum Nokia Champion
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 With Quote
Reply « Previous Thread | Next Thread »
Display Modes
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Rate This Thread
Rate This Thread:

Posting Rules

You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Forum Jump
Similar Threads
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

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