Pys60 App Skeleton

Over the development of a few applications with pys60 I have standardized on an app skeleton that simplifies and speeds-up the initial process of getting something going. The basic idea is to have an application shell up and running on various environments without needing a lot of configuration changes as quickly as possible. By default the app will run fine in the following environments without any changes: emulator, phone (script shell), phone (stand-alone).

The core idea behind the skeleton is that of a simple launcher and a directory holding the application. The launcher is responsible for setting the import paths based on the environment and bootstraping the application with generic exception handling. The actual application code is in a separate module. The application and data views are also based on individual classes. This allows for simple navigation between views / screens even in situations where the hierarchy is quite complex.

The sample skeleton project zip file has the following contents:

app_myapp.py    
AppFolder
    logger.py
    debug.py
    myapp.py

Here is how you can get started with your own app in seconds in a emulator (the steps below are based on N97 emulator installed in default location and the paths will have to change to reflect any other emulator editions) :

  1. Unzip the app_skeleton into a temp folder
  2. Copy app_myapp.py to python examples folder (in my case here it would be c:\s60….)
  3. Copy the app folder to any location on the emulator C: drive. I usually place the app directory in the c:\data\ folder

At this point you can either launch the app from the Python Script Shell

Hopefully the launcher code is self explanatory and will illustrate whats happening behind the scenes when you launch the app:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
print "MyApp Name"
 
import appuifw
import sys
import os
import os.path
import e32
 
try:
    global DATA_PATH
    print "Loading, please wait..."
    e32.ao_sleep(.1)
    e32.ao_yield()
    if e32.in_emulator():
        if e32.pys60_version_info[0] == 1 and e32.pys60_version_info[1] < 5:
            DATA_PATH = 'c:\\python\\myapp_folder\\'
        else:
            DATA_PATH = 'c:\\data\\python\\myapp_folder\\'
    else:
        #create app dir if neccessery
        drive = 'e'
        if os.path.exists('e:'):
            drive = 'e'
            DATA_PATH = 'e:\\data\\myapp_folder'
        elif os.path.exists('c:'):
            drive = 'c'
            DATA_PATH = 'c:\\data\\myapp_folder'
        try:
            os.makedirs('%s:\\data\\myapp_folder' % drive)
        except: pass
        #also add current script path (if we are running from a sis file)
        sys.path.insert(0,os.getcwd()) #not 100% sure this is needed
 
    if os.path.exists(DATA_PATH) == False:
        appuifw.note(u'Unable to set data directory. Exiting Application.')
        e32.ao_sleep(3)
        appuifw.app.set_exit()
        sys.exit()
 
    sys.path = sys.path + [DATA_PATH]
 
    import myapp
 
    old_title = appuifw.app.title
    exit_handler = appuifw.app.exit_key_handler
 
    appuifw.app.screen = 'normal'
 
    menu = appuifw.app.menu
 
    myApp = myapp.App()
    myApp.run()
    myApp.close()
 
 
    if e32.in_emulator():
        appuifw.app.title = old_title
        appuifw.app.exit_key_handler = exit_handler
        appuifw.app.menu = menu
    else:
        appuifw.app.title = old_title
        appuifw.menu = None
except:
    import sys
    import traceback
    import e32
    import appuifw
 
    if e32.in_emulator():
        if e32.pys60_version_info[0] == 1 and e32.pys60_version_info[1] < 5:
            DATA_PATH = 'c:\\python\\myapp_folder'
        else:
            DATA_PATH = 'c:\\data\\python\\myapp_folder'
    else:
        DATA_PATH = 'e:\\data\\myapp_folder'
    sys.path = sys.path + [DATA_PATH]
 
    from logger import Logger
 
    errlog  = Logger("%s\\myapp_error_log.txt" % DATA_PATH)
    errlog.start_trace()
    appuifw.app.screen = "normal"               # Restore screen to normal size.
    appuifw.app.focus = None                    # Disable focus callback.
    body = appuifw.Text()
    appuifw.app.body = body                     # Create and use a text control.
    exitlock = e32.Ao_lock()
    def exithandler(): exitlock.signal()
    appuifw.app.exit_key_handler = exithandler  # Override softkey handler.
    appuifw.app.menu = [(u"Exit", exithandler)] # Override application menu.
    errlog.writeline("\n".join(traceback.format_exception(*sys.exc_info())))
    body.set(unicode("\n".join(traceback.format_exception(*sys.exc_info()))))
    exitlock.wait()                             # Wait for exit key press.
    appuifw.app.set_exit()

The launcher will create a ‘myapp’ application object and call the run method. From then on, the run method is responsible for creating the first view and for creating a signal lock until the view is closed or an app exit is requested by the user.

Leave a Reply