Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Symbian OS Python / PyS60 1 Andreas Jakl, 2009 v1.2 – 01 March 2009 PyS60 – Examples ● ShakerRacer www.youtube.com/watch?v=EMjAYdF13cU 2 Andreas Jakl, 2009 PyS60 – Examples ● PyWuzzler (Benjamin Gmeiner, Yen-Chia Lin) 3 Andreas Jakl, 2009 PyS60 – Examples ● Controlling Super Mario by movements (jumping, walking, ...) NiiMe Playing drums Controlling flight / racing games with tilting http://www.niime.com/ 4 Andreas Jakl, 2009 PyS60 – Examples Time-lapse Photography http://blog.foozia.com/blog/2007/jan/21/python-s60-time-lapse-photography-using-nokia-n80/ aspyplayer Last.fm-Player for PyS60 http://code.google.com/p/aspyplayer/ pyPoziomica Use your phone as a level tool http://www.symbian-freak.com/news/007/12/pypoziomica_freeware_level_tool.htm PyED Edit Python source code on the phone http://sourceforge.net/projects/pyed/ 5 Andreas Jakl, 2009 What is it all about? Python 6 Andreas Jakl, 2009 Python ● Older than you might think: 1989 – 1991 by Guido van Rossum (National Research Institute for Mathematics and Computer Science, Netherlands) Named after Monty Python’s Flying Circus ● Today: Microcontrollers Mobile Phones Web servers 7 Andreas Jakl, 2009 Scalable ● Modular architecture ● Easy code reuse ● Huge Python standard library ● Extension with own C/C++ modules Shell scripts Huge projects 8 Andreas Jakl, 2009 Mature Object oriented, memory manager 9 Helpful error messages, exception handling Interpreted & bytecompiled Andreas Jakl, 2009 Very high level Fast ● Rapid Application Prototyping ● Easy to learn – can you read this code? import inbox, audio box = inbox.Inbox() msg_id = box.sms_messages()[0] msg_txt = u"Message: " + box.content(msg_id) audio.say(msg_txt) ● What would this be like in C++ or Java ME? ... 10 Andreas Jakl, 2009 Symbian OS + Runtimes Java ME Python .net Basic Perl Apache / PHP / MySQL Widgets (Web Runtime) Flash Lite Silverlight (soon) Ruby S60 (C++) Symbian OS 11 Andreas Jakl, 2009 UI Platforms: S60 ● Unified UI platform based on S60 Official UI platform of Symbian Foundation Former name: Series 60 Nokia N97 12 ● Touchscreen support with S60 5th Edition Andreas Jakl, 2009 UI Platforms: S60 www.s60.com Business High-End Multimedia Mass Market Nokia N96 Nokia E66 Samsung Omnia HD Nokia 6121 Classic Nokia 5800 XPressMusic Nokia E71 Nokia N85 SE Idou Nokia E90 13 Samsung INNOV8 Andreas Jakl, 2009 Nokia 5500 Sport Nokia 6210 Navigator ● Python port to S60 ● Allows easy access to: Accelerometer Camera Text-to-speech Location Ease of development PyS60 Web Managed code Java Native code Symbian C++ Functionality and performance Bluetooth UI 14 Python P.I.P.S. Web services Messaging Flash Andreas Jakl, 2009 Setup – Phone ● ● Install the latest Nokia PC Suite: http://europe.nokia.com/A4144903 PyS60 1.4.x: based on Python 2.3 PyS60 1.9.2+: based on Python 2.5.1, supports new sensor framework of S60 3rd Ed., FP2+ Download and install PyS60: 1.4.x: http://sourceforge.net/projects/pys60/ 1.9.x+: https://garage.maemo.org/projects/pys60/ ● Phone: Phone software: PythonForS60_1_x_x_3rdEd.SIS Phone script shell: PythonScriptShell_1_x_x_3rdEd.SIS 15 Andreas Jakl, 2009 Setup – PC ● Extract SDK plug-in to the S60 SDK: PythonForS60_1_x_x_SDK_3rdEd.zip 16 Andreas Jakl, 2009 IDEs ● IDLE – comes with Python SDK C:\Program Files\Python\Lib\idlelib\idle.bat ● PythonWin + Win32 Extensions http://sourceforge.net/projects/pywi n32/ ● PyDev Eclipse/Carbide.c++ Plug-in http://pydev.sf.net/ ● SPE http://pythonide.stani.be/ 17 Andreas Jakl, 2009 Hello World ● Create a file hello.py: hello.py print “Hello World” ● ● ● Connect your phone to the PC (“PC Suite” connection mode) Transfer the script to E:\Python\ (memory card) using the Nokia PC suite file manager Run the script: 18 Andreas Jakl, 2009 Hello World – Emulator ● ● Copy hello.py to: <epocroot>\winscw\c\python\hello.py Start the emulator from: <epocroot>\release\winscw\udeb\epoc. exe 19 Andreas Jakl, 2009 PyDev + Emulator ● ● Either create the project / workspace directly in the python-dir of the emulator Or link the project files to source files in the dir: 20 Andreas Jakl, 2009 Starting with the UI PyS60 – User Interface 21 Andreas Jakl, 2009 Module ● ● Collection of related functions and data grouped together Has to be imported at the beginning: import appuifw ● Addressing a function of the module: appuifw.query(label, name) ● Import multiple modules in a single statement: import appuifw, e32 22 Andreas Jakl, 2009 import appuifw Query appuifw.query(u"Type a word:", "text", u"Hello") appuifw.query(u"Type a number:", "number", 7) appuifw.query(u"Type a date:", "date") appuifw.query(u"Type a time:", "time") appuifw.query(u"Type a password:", "code") appuifw.query(u"Do you like PyS60?", "query") Syntax: appuifw.query(label, text 23 number / float date type[, initial value]) time Andreas Jakl, 2009 code query import appuifw Note Dialog appuifw.note(u"Hello") appuifw.note(u"File not found", "error") appuifw.note(u"Upload finished", "conf") Syntax: appuifw.note(text[, info / default 24 type[, global] ] ) error Andreas Jakl, 2009 conf Variables ● Variables not declared ahead of time ● Implicit typing ● Automated memory management Reference counting, garbage collection ● Variable names can be “recycled” ● del statement allows explicit de-allocation 25 Andreas Jakl, 2009 Variables – Example age = 5 name = u"Andreas" # u in front of the string: unicode name += age name += str(age) # Doesn't work, the type isn’t converted automatically # name == Andreas5 name = age # name now points to the same object as age; name == 5 foo = "xyz" bar = foo foo “xyz” # bar points to the same object as foo bar foo = 123 26 # a new object is created for foo, bar still points to "xyz“ Andreas Jakl, 2009 foo “xyz” bar 123 Multi-Query Dialog ● Syntax: appuifw.multi_query(label1, import appuifw pwd = u"secret" info = appuifw.multi_query(u"Username:", u"Password:") if info: // returns a tuple with the info login_id, login_pwd = info if login_pwd == pwd: appuifw.note(u"Login successful", "conf") else: appuifw.note(u"Wrong password", "error") else: // returns None – special type appuifw.note(u"Cancelled") 27 Andreas Jakl, 2009 label2) if-statement ● Works like in other languages ● Blocks are defined by indentation – avoids dangling else ● if expression1: expr1_true_suite elif expression2: expr2_true_suite else: none_of_the_above_suite 28 Andreas Jakl, 2009 Lists, Tuples and Dictionaries ● Generic “arrays” for arbitrary number of arbitrary objects ● Ordered and accessed via index offsets ● List – created using [ ] myList = [1, 2, 3, 4] # [1, 2, 3, 4] print myList[0] #1 # Subsets: sequence[starting_index:ending_index] print myList[1:3] # [2, 3] print myList[2:] # [3, 4] print myList[:3] # [1, 2, 3] myList[1] = 5 # [1, 5, 3, 4] myList[2] = ["bla", (-2.3+4j)] # [1, 5, ["bla", (-2.3+4j)], 4] print myList[2][1] # -2.3+4j print 4 in myList # True 29 Andreas Jakl, 2009 Lists, Tuples and Dictionaries ● Tuple – created using ( ) Immutable – can therefore be used as dictionary keys Also useful when you don’t want a function to be able to change your data myTuple1 = ('python', 's60', 27) print myTuple1[0] # python myTuple[1] = 'no' # tuples are immutable – exception! myTuple2 = ('symbian', 'uiq') myTuple3 = myTuple1 + myTuple2 print myTuple3 # ('python', 's60', 27, 'symbian', 'uiq') 30 Andreas Jakl, 2009 Lists, Tuples and Dictionaries ● Dictionary – created using { } Mapping type – like associative arrays or hashes in Perl Key-value pairs – – Keys: almost any Python type, usually numbers or stings Values: arbitrary Python object myDict = {'planet': 'earth'} myDict['port'] = 80 print myDict # {'planet': 'earth', 'port': 80} print myDict.keys() # ['planet', 'port'] print myDict['planet'] # earth for key in myDict: print "key=%s, value=%s" % (key, myDict[key]) # key=planet, value=earth # key=port, value=80 31 Andreas Jakl, 2009 Popup Menu ● Syntax: appuifw.popup_menu(list[, import appuifw items = [u"The Journey", u"RealReplay", u"ShakerRacer"] index = appuifw.popup_menu(items, u"Buy:") if index == 0: appuifw.note(u"Great choice") elif index == 1: appuifw.note(u"Cool") elif index == 2: appuifw.note(u"I like that") elif index == None: appuifw.note(u"Purchase cancelled") 32 Andreas Jakl, 2009 label ]) Selection List ● Search field appears after pressing a letter key Syntax: appuifw.selection_list(choices[, import appuifw names = [u"Michael", u"Devon", u"Bonnie", u"April", u"RC3"] index = appuifw.selection_list(names, 1) if index == 2: print "I love you!" else: print "You're great!" 33 Andreas Jakl, 2009 search_field=0]) Multi-Selection List ● Syntax: appuifw.multi_selection_list( choices[, style=„checkbox‟, search_field=0]) import appuifw names = [u"Michael", u"Devon", u"Bonnie", u"April", u"RC3"] selections = appuifw.multi_selection_list(names, 'checkbox', 1) print selections Style: checkmark Select multiple items with the pen key Not really important 34 Andreas Jakl, 2009 for loop ● ● for iter_var in iterable: suite_to_repeat With each loop, iter_var set to current element of iterable Similar to foreach in other languages 35 Andreas Jakl, 2009 for Loop – Examples # Iterating over a string for eachLetter in "Text": print "current letter:", eachLetter # Iterating by sequence item nameList = ["Mike", "Sarah", "Charles"] for eachName in sorted(nameList): print eachName # Iterating by sequence index for nameIndex in range(len(nameList)): print nameList[nameIndex] # Iterate over a range for eachVal in range(3): print "value: ", eachVal Mike Sarah Charles value: 0 value: 1 value: 2 # Extended syntax: # range(start, end, step = 1) for eachVal in range(2, 10, 3): print "value: ", eachVal 36 current letter: T current letter: e current letter: x current letter: t Charles Mike Sarah value: 2 value: 5 value: 8 Andreas Jakl, 2009 while loop ● ● while expression: suite_to_repeat Nice addition: else is executed if loop was not abandoned by break def showMaxFactor(num): count = num / 2 while count > 1: if num % count == 0: print "Largest factor of %d is %d" % (num, count) break count -= 1 else: print num, "is prime" for eachNum in range(10, 21): showMaxFactor(eachNum) 37 Andreas Jakl, 2009 Example – System Info SMS import appuifw, messaging, sysinfo # You could use a dictionary as well, but here this is more straightforward later on infoNames = [u"Profile", u"Battery", u"Signal DBM"] infoCalls = ["sysinfo.active_profile()", "sysinfo.battery()", "sysinfo.signal_dbm()"] # Let the user choose the information he wants to send choices = appuifw.multi_selection_list(infoNames, "checkbox", 0) infoSms = "" for idx in choices: # Execute the statement(s) stored in the infoCalls-list through the eval-statement, # convert the result to a string and append it to the sms text infoSms += infoNames[idx] + ": " + str(eval(infoCalls[idx])) + "; " # Query the telephone number smsNum = appuifw.query(u"Number:", "text", u"+15550135") if smsNum: # Send the SMS if the user didn’t cancel messaging.sms_send(smsNum, infoSms) appuifw.note(u"Info sent", "conf") 38 Andreas Jakl, 2009 Procedural and Object Oriented Development Python – Function and Classes 39 Andreas Jakl, 2009 Functions – Basics ● ● def function_name(arguments): [“function_documentation_string”] function_body_suite Supports: def foo(): "foo() -- does't do anything special." print "in foo" Default arguments Variable length arguments Multiple return values Inner functions # Execute the function foo() # Print the help text of the function print foo.__doc__ # foo() -- does't do anything special. ... 40 Andreas Jakl, 2009 Functions – Default Arguments ● Default arguments: def func(posargs, defarg1=dval1, defarg2=dval2, ...): def calcTax(amount, rate=0.0275): return amount + (amount * rate) print calcTax(100) # 102.75 print calcTax(100, 0.05) # 105.0 41 Andreas Jakl, 2009 Functions – Variable Length Arguments ● Variable length arguments (unknown number): Non-keyword variable arguments (tuple): def func([formal_args,] *vargs_tuple): Argument with * will hold all remaining arguments once all formal parameters have been exhausted Keyword variable arguments (dictionary): def func([formal_args,][*vargst,] **vargsd): def tupleVarArgs(arg1, arg2='defaultB', *theRest): print "formal arg 1: ", arg1 print "formal arg 2: ", arg2 for eachXtraArg in theRest: print 'another arg: ', eachXtraArg 42 Andreas Jakl, 2009 tupleVarArgs('abc') # formal arg 1: abc # formal arg 2: defaultB tupleVarArgs('abc', 123, 'xyz', 123.456) # formal arg 1: abc # formal arg 2: 123 # another arg: xyz # another arg: 123.456 global_str = "foo" ● local scope Variable Scope Global scope Declared outside a function def foo(): local_str = "bar" return global_str + local_str print foo() Lifespan lasts as long as script is running ● Local scope Live temporarily as long as function they are defined in is active ● Searching for identifiers First local, then global Possible to override global variables by creating a local one 43 Andreas Jakl, 2009 Variable Scope Writing to global variables in functions: Creates a new local variable (pushes global variable out of scope) global statement specifically references a named global variable def foo(): bar = 200 print "in foo(), bar is", bar bar = 100 print "in __main__, bar is", bar foo() print "in __main__, bar still is", bar in __main__, bar is 100 in foo(), bar is 200 in __main__, bar still is 100 44 def foo(): global bar bar = 200 print "in foo(), bar is", bar bar = 100 print "in __main__, bar is", bar foo() print "in __main__, bar is now", bar in __main__, bar is 100 in foo(), bar is 200 in __main__, bar is now 200 Andreas Jakl, 2009 local scope local scope ● Classes ● ● ● class MyObject(bases): “Documentation text” class_suite New style classes should be derived from any other class or from object Simplest use: container object for instance attributes Not defined in class definition Only valid for this instance 45 class MyData(object): pass # code is required syntactically, # but no operation is desired mathObj = MyData() mathObj.x = 4 mathObj.y = 5 Andreas Jakl, 2009 print mathObj.x * mathObj.y # 20 Classes – Subclasses, Methods ● __init__ similar to constructor ● def updateId(self, newId): self.id = newId self-parameter Passes reference to current instance Not needed for static or class methods Python wants to be explicitly clear 46 class LibraryEntry(object): def __init__(self, id, title): self.id = id self.title = title class LibraryBook(LibraryEntry): def __init__(self, id, title, author): LibraryEntry.__init__(self, id, title) self.author = author def updateAuthor(self, newAuthor): self.author = newAuthor libBook = LibraryBook(1, "PyS60", "Andreas Jakl") libBook.updateId(2) libBook.updateAuthor("Nokia") Andreas Jakl, 2009 Classes – Attributes ● Instance attributes are set “on-the-fly” by using them Constructor is the first place to set instance attributes Use default arguments for default instance setup ● Class variables / static data have to be defined class Letter(object): def __init__(self, text, author="Andreas Jakl", category=“love"): self.text = text self.author = author self.category = category def printLetter(self): print self.text, self.author, self.category loveLetter = Letter("I love you") print dir(loveLetter) # Print all methods & attributes of this class 47 Andreas Jakl, 2009 class C(object): foo = 100 # Static data print C.foo # 100 C.foo = C.foo + 1 print C.foo # 101 ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'author', 'category', 'printLetter', 'text'] How to organize your application PyS60 – Application Structure 48 Andreas Jakl, 2009 Application Structure Title appuifw.app.title Navigation pane appuifw.app.set_tabs() Body appuifw.app.body Dialog appuifw.<dialog_function> - appuifw.query() - appuifw.note() - ... 49 Menu Exit appuifw.app. appuifw.app.menu exit_key_handler Andreas Jakl, 2009 UI App – Example import appuifw, e32 def quit(): print "Exit key pressed" app_lock.signal() appuifw.app.exit_key_handler = quit appuifw.app.title = u"UI App" print "App is now running" app_lock = e32.Ao_lock() app_lock.wait() print "Application exits" 50 Andreas Jakl, 2009 Callback Function ● No difference to normal functions ● Associating function with event: binding Use function name without () to get the function object Compare to function pointers in C def quit(): pass # function is empty (instead of {} in C++ / Java) appuifw.app.exit_key_handler = quit 51 Andreas Jakl, 2009 Wait for the User ● Previously: app. exited after executing all lines ● Event based UI-app: wait for user input ... app_lock = e32.Ao_lock() # Create an instance of an Ao_lock object app_lock.wait() Waiting for events 52 quit() call-back handler app_lock.signal() Andreas Jakl, 2009 Refer to the Symbian OS course for more information about Active Objects Application Body appuifw.app.screen = “normal" 53 appuifw.app.screen = “large" Andreas Jakl, 2009 appuifw.app.screen = "full" Application Body ● You can assign several objects to the body Canvas: provides drawable screen area + support for handling raw key events Form: complex forms with various input fields Listbox: shows a list of items (single- or double-line-item) Text: free-form text input 54 Andreas Jakl, 2009 Application Menu ● Defined using tuples: Text Call-back function – Specify another tuple instead to define a submenu import appuifw, e32 def play(): print "Play file" def volume_up(): print "Volume up" def volume_down(): print "Volume down" def quit(): print "Exit key pressed" app_lock.signal() appuifw.app.exit_key_handler = quit appuifw.app.title = u"mp3 Player" appuifw.app.menu = [(u"Play", play), (u"Volume", ( (u"Up", volume_up), (u"Down", volume_down) ) )] print "App is now running" app_lock = e32.Ao_lock() app_lock.wait() submenu Example based on [1] 55 Andreas Jakl, 2009 String Manipulation txt = "I like Python" url = " http://www.mopius.com " url = url.strip() if url.startswith("http://"): print url, "is a valid URL" print txt[2:6] print txt.find("like") if txt.find("love") == -1: print "What's wrong with you?" txt.replace("like", "love") webServiceInput = " 1, 2, 3, 4" print webServiceInput.replace(" ", "") print txt.upper() print "Length", len(txt) txt = "one;two;three" print txt.split(";") txt2 = "" if txt2: print "txt2 contains characters" else: print "txt2 doesn't contain characters" http://www.mopius.com is a valid URL 1,2,3,4 ['one', 'two', 'three'] like 2 What's wrong with you? I LIKE PYTHON Length 13 txt2 doesn't contain characters 56 Andreas Jakl, 2009 String Formatting ● String slicing like for lists: [start:end] ● Assemble string based on other variables: print "Host: %s\tPort: %d" % ("Earth", 80) print "DD.MM.YYYY = %02d.%02d.%d" % (12, 3, 82) list = [3, 2, 1, "go"] # A list has to be converted to a tuple first print "Counting: %d, %d, %d, %s" % tuple(list) Symbol Conversion Host: EarthPort: 80 DD.MM.YYYY = 12.03.82 %s String conversion via str() prior Counting: 3, 2, 1, go to formatting %d Signed decimal integer %f Floating point real number ... ... 57 Andreas Jakl, 2009 Example – Inbox Search import inbox, appuifw # Create an instance of the Inbox object box = inbox.Inbox() # Query search phrase query = appuifw.query(u"Search for:", "text").lower() hits = [] ids = [] # sms_messages() returns message IDs for all messages in the SMS inbox for sms_id in box.sms_messages(): # Retrieve the full message text and convert it to lowercase msg_text = box.content(sms_id).lower() if msg_text.find(query) != -1: # If the text was found, store a preview hits.append(msg_text[:25]) ids.append(sms_id) # Display all results in a list index = appuifw.selection_list(hits, 1) if index >= 0: # Show the full text of the selected message appuifw.note(box.content(ids[index])) 58 Andreas Jakl, 2009 Event Loop ● e32.Ao_lock waits for events, but stops execution ● Game: App. has to be active all the time But still needs respond to events (keys, ...) Initialize event call-backs while <end condition>: Update game state Redraw screen Pause / yield 59 Sleep (+ execute waiting active objects): e32.ao_sleep(interval) Yield (just execute ready active objects with higher priority) e32.ao_yield() Andreas Jakl, 2009 The Python way of Exception Handling 60 Andreas Jakl, 2009 Exceptions ● Example: Exception “NameError” raised by the interpreter: >>> print foo Traceback (most recent call last): File “<stdin>", line 1, in <module> print foo NameError: name 'foo' is not defined 61 Andreas Jakl, 2009 Exceptions ● try: try_suite # watch for exceptions here except Exception[, reason]: except_suite # exception-handling code ● Different objects derived from Exception ● Exception arguments / reasons: May be passed along Not just a string, but contains more information 62 Andreas Jakl, 2009 Exceptions – Information try: f = file(u"c:\\python\\test.txt", "w+") print >> f, "Welcome to Python" f.seek(0) print "File contents:", f.read() f.close() except IOError, reason: print reason print reason.filename print reason.errno print reason.strerror 63 [Errno 2] No such file or directory: u'c:\\python\\test.txt' c:\python\test.txt 2 No such file or directory Andreas Jakl, 2009 Multiple Exceptions ● Catching multiple exceptions: Multiple except-statements Multiple exceptions in one except statement Catch the base class Exception (no good coding style – you might be silently dropping errors) 64 Andreas Jakl, 2009 Code Examples ● Code snippets for: Camera Text to Speech Sound recording / playing Bluetooth Networking Graphics, UI 3D (Open GL ES) Camera Sensor ... 65 http://www.mobilenin.com/pys60/menu.htm Additional modules: http://cyke64.googlepages.com/ Andreas Jakl, 2009 Bonus – Acceleration Sensor (3rd Ed (FP1)) import appuifw,e32,sensor def get_sensor_data(status): "Callback function for regular accelerometer status" print "x: %d, y: %d, z: %d" % (status['data_1'], status['data_2'], status['data_3']) def exit_key_handler(): # Disconnect from the sensor and exit acc_sensor.disconnect() app_lock.signal() appuifw.app.exit_key_handler = exit_key_handler # Retrieve the acceleration sensor sensor_type = sensor.sensors()['AccSensor'] # Create an acceleration sensor object acc_sensor = sensor.Sensor(sensor_type['id'],sensor_type['category']) # Connect to the sensor acc_sensor.connect(get_sensor_data) # Wait for sensor data and the exit event app_lock = e32.Ao_lock() app_lock.wait() 66 Andreas Jakl, 2009 Bonus – Acceleration Sensor (3rd Ed FP2+) from sensor import * import e32, time, appuifw class DemoApp(): def __init__(self): self.accelerometer = AccelerometerXYZAxisData(data_filter=LowPassFilter()) self.accelerometer.set_callback(data_callback=self.my_callback) self.counter = 0 def my_callback(self): # For stream sensor data the callback is hit 35 times per sec (On 5800). # The device cannot handle resource hungry operations like print in the callback function # for such high frequencies. A workaround is to sample the data as demonstrated below. if self.counter % 5 == 0: print "X:%s, Y:%s, Z:%s" % (self.accelerometer.x, self.accelerometer.y, self.accelerometer.z) self.counter = self.counter + 1 def run(self): self.accelerometer.start_listening() def exit_key_handler(): # Disconnect from the sensor and exit global d d.accelerometer.stop_listening() print "Exiting Accelorometer" app_lock.signal() if __name__ == '__main__': appuifw.app.exit_key_handler = exit_key_handler d = DemoApp() d.run() app_lock = e32.Ao_lock() 67 app_lock.wait() Andreas Jakl, 2009 Literature – Recommended Mobile Python Jürgen Scheible, Ville Tuulos Complete overview of Python development for PyS60, many small code samples. Status: Symbian OS 9, PyS60 1.4, 2007 Free code samples: http://www.mobilenin.com/pys60/menu.htm Core Python Programming (Second Edition) Wesley J. Chun Python for developers who already know other languages. Comprehensive short overview, in later chapters a detailed overview of the individual components. Status: 2007 68 Andreas Jakl, 2009 That’s it! Thanks for your attention 69 Andreas Jakl, 2009