Wiley.Mobile.Python.Rapid.prototyping.of.applications.on.the.mobile.platform.Dec.2007 (779889), страница 23
Текст из файла (страница 23)
Because of these choices, the stringscannot contain any colons or new line characters.A saved configuration file might look like this:Host: www.google.comPort: 80Username: my_loginPassword: this is a visible secretReading the file is a bit trickier than writing it.Example 47: Read a dictionary from a filedef load_dictionary(filename):f = file(filename, "r")dict = {}for line in f:key, value = line.split(":")dict[key.strip()] = value.strip()f.close()return dictWe go through the file line by line.
We split each line into the keypart and the value part, using the colon as a separator. Then, the newkey and value are updated to the dictionary dict. As before, we usefunction strip() to get rid of all leading and trailing white space thatmight occur in keys and values.6.2.3 Reading and Writing Unicode TextBoth Symbian OS and the S60 platform are designed for world-wide use.Consequently, every part of the system that deals with text in any form,is designed to handle text written in any language, using any characterset.LOCAL DATABASE121Unicode is a standard that defines how the world’s writing systemsshould be handled on computers. Unicode is natively supported by theSymbian and S60 platforms, as well as by PyS60. As you have seen,practically all functions in the PyS60 API that handle strings require thatthe input is specified as Unicode strings.
In Python, Unicode strings canbe recognized by the u prefix before the quotes, as in u"Kanji".However, Python functions that are not part of the mobile platformand PyS60 API, but parts of the Python standard library, often do nothandle Unicode automatically. For instance, File objects and networkconnections do not require that the data must be specified in Unicode.As a consequence, you have to handle conversions between plain stringsand Unicode strings explicitly in your code when using these functions,as we describe below.In the following code, the first line converts a Unicode string orig strto the plain string plain str and the second line converts it back toUnicode.orig_str = u"käärmekännykkä"plain_str = orig_str.encode("utf-8")unicode_str = plain_str.decode("utf-8")There are many ways to encode a Unicode string to a plain string, buthere we use an encoding called UTF-8 that is widely used nowadays.Generally speaking, it is enough to remember the following ruleof thumb: whenever you save a string from PyS60 to a file, useencode("utf-8") before writing.
Correspondingly, when you readthe string from a file, use decode("utf-8") before using the string inyour application. The same rule applies also for the local database, aswell as for Bluetooth and network communication.In this book, we have omitted the conversion in some examples forbrevity.
All the larger application examples should perform the conversionwhenever it is needed.6.3 Local DatabaseSymbian OS includes a relational database engine, to which PyS60provides two interfaces. The first interface can be found in modulee32db and the second interface in module e32dbm.The former interface provides low-level, versatile access to the database.
It supports transactions and querying the database with a subset ofSimple Query Language (SQL). This interface can be useful for applications that need to store relationally organized data and perform queriesfrequently. In this case, having a local database, instead of sending datato a ‘real’ database on a server, may be justified.122DATA HANDLINGIn many cases, the interface provided in module e32dbm is preferredbecause of its simplicity. In essence, you can treat e32dbm as a persistentdictionary for string keys and values. It supports most of the standarddictionary functions, such as adding, deleting and iterating throughkey–value pairs.Example 48: Local databaseimport e32dbmDB_FILE = u"c:\\python\\test.db"def write_db():db = e32dbm.open(DB_FILE, "cf")db[u"host"] = u"www.google.com"db[u"port"] = u"80"db[u"username"] = u"musli"db[u"password"] = u"my secret"db.close()def read_db():db = e32dbm.open(DB_FILE, "r")for key, value in db.items():print "KEY", key, "VALUE", valuedb.close()print "Writing db.."write_db()print "Reading db.."read_db()As you can see, the local database module, e32dbm, behaves like amixture of a file and a dictionary: it is opened like a file and accessedlike a dictionary.
Note that the database has native support for Unicodestrings, so no conversions have to be performed.You need to specify the file name where the database is to be stored.The mode parameter works differently from that of the File object:•r – opens the database for reading only.• w – opens the database for reading and writing.•c – opens the database for reading and writing (and creates a newdatabase if the file does not exist).•n – opens the database for reading and writing (it creates an emptydatabase if the file does not exist or clears the previous database).If you add f after the mode character, the database is not updated ondisk until you close it or force it to be written to disk by using a specialfunction. This makes the database access faster.
You should use this modeunless you need the additional safety of synchronized writing.GSM AND GPS POSITIONING123Given the convenience of a local database, why should one everconsider using an ad hoc solution as in Examples 45 and 46? The biggestreason is interoperability. You can read and write text files on anydevice, including your PC and a server running, for instance, FreeBSD.On the other hand, databases created by the e32dbm interface arestrictly Symbian-specific.
However, if the data is only to be accessedprivately by a PyS60 application, the local database is probably a safechoice.For a practical application example of the local database, see Section9.3.6.4 GSM and GPS PositioningApplications based on positioning are often claimed to be the most likelykiller applications of the mobile platform.
PyS60 gives you access topractically all public positioning techniques that are available for thecurrent S60 mobile phones. As of 2007, the most typical positioningtechniques are the following:•external GPS over Bluetooth, as described in Section 7.5•internal GPS, using the position module•GSM cell IDs, using the location module•SMS-based positioning using the messaging module. This service isprovided by some mobile phone operators.Besides these techniques, various positioning techniques based onWiFi or Bluetooth have been experimented with by research groups usingPyS60 and custom extensions.On S60 3rd Edition phones, access to both the internal GPS and GSMcell IDs are restricted by additional capabilities, which are not availablefor self-signed PyS60 – see Appendix A for more information.
This meansthat if you have an S60 3rd Edition phone, you cannot use Example 49with the self-signed PyS60 interpreter that we installed in Chapter 2.Instead, you need to obtain a personal, free, developer certificate fromSymbian and sign the interpreter with it, as described in Section A.4.3.On S60 2nd Edition devices, no platform security is enforced and theGSM cell IDs can be accessed without trouble.6.4.1GSM Cell ID MapperWe present a simple application that can be used to record positioningdata and to retrieve your current location, based on GSM cell IDs. Theexample is based on the GSM cell IDs because of the ubiquity of the124DATA HANDLINGtechnique.
Only a few phones have an internal GPS receiver (as of 2007)but every GSM phone can be used to retrieve GSM cell IDs.This application allows you to collect cell IDs and give them namesaccording to your actual location. The mapping from the detected cellIDs to their locations is saved in a text file. This file can be edited andupdated, for instance, based on publicly available information about thelocations of GSM cells. In addition, the application logs your currentlocation to a file with a timestamp, so you can analyze your movementsafterwards.The core functionality of Example 49 is based on the module location.Example 49: Retrieve the current GSM cell IDimport locationprint location.gsm_location()You should see a tuple of four integers whose values depend onyour current location. The first two integers correspond to the MobileCountry Code (MCC) and Mobile Network Code (MNC).
A comprehensivemapping of MCC and MNC codes to country and operator names can befound in the Wikipedia article ‘List of GSM Network Codes’.The last two integers correspond to the GSM area code and the currentGSM cell ID. Many different cell IDs may map to a single area code,which, in urban environments, may cover a district.Note that you are likely to see many different cell IDs in one physicallocation, at least if you test the system on the same location at differenttimes.
This is because of overlapping cells and other intricacies of theGSM network.Thus, even though you may have named a certain location earlier,there is no guarantee that the same location will be recognized afterwardswhen you arrive at it again. However, the more cells you name the moreprobable it is that some mappings will eventually match.Example 50 contains the source code for the GSM cell ID mapper.This example uses the functions save dictionary() and loaddictionary() that are found in Examples 46 and 47.
To save space,these functions are not repeated here, but you must include them in theactual application.Example 50: GSM location applicationimport appuifw, e32, location, time, os.pathPATH = u"E:\\Data\\gsm_loca\\"if not os.path.exists(PATH):os.makedirs(PATH)GSM AND GPS POSITIONINGINTERVAL = 5.0CELL_FILE = PATH + "known_cells.txt"LOG_FILE = PATH + "visited_cells.txt"log = file(LOG_FILE, "a")timer = e32.Ao_timer()def current_location():gsm_loc = location.gsm_location()return "%d/%d/%d/%d" % gsm_locdef show_location():loc = current_location()if loc in known_cells:here = known_cells[loc]print "You are currently at", hereelse:here = ""print "Unknown location", locprint >> log, time.ctime(), loc, heretimer.after(INTERVAL, show_location)def name_location():loc = current_location()name = appuifw.query(u"Name this location", "text")if name:known_cells[loc] = namedef load_cells():global known_cellstry:known_cells = load_dictionary(CELL_FILE)except:known_cells = {}def quit():print "SAVING LOCATIONS TO", CELL_FILEsave_dictionary(CELL_FILE, known_cells)print "GSM LOCATIONING APP EXITS"timer.cancel()log.close()app_lock.signal()appuifw.app.exit_key_handler = quitappuifw.app.title = u"GSM location App"appuifw.app.menu = [(u"Name this location", name_location)]print "RECORDING VISITED CELLS TO", LOG_FILEprint "LOADING LOCATIONS FROM", CELL_FILEload_cells()print "%d KNOWN CELLS LOADED" % len(known_cells)show_location()print "GSM LOCATIONING APP STARTED"app_lock = e32.Ao_lock()app_lock.wait()125126DATA HANDLINGWhen the application starts, we try to load previously saved locationsfrom the file CELL FILE in the function load cells().
Here we usea try – except block: if the file does not exist, load dictionary()throws an exception which is caught by the except statement. In thiscase, we start with an empty dictionary.The core data structure is the dictionary known cells. It contains amapping from the known GSM cell IDs to their names. It is initializedby the function load cells(), as mentioned above. When the userchooses ‘Name this location’, we update the dictionary in the functionname location(). When the application quits, the dictionary is savedto the file CELL FILE using the function save dictionary().As this application is used to track the user’s movements, we needto check the current location periodically. The module e32 provides atimer object called e32.Ao timer that can be used to call a functionafter a certain number of seconds has elapsed. In this case, the expressiontimer.after(INTERVAL, show location) specifies that the function show location() should be called after INTERVAL seconds. Theafter() function puts the request on hold and returns immediately.