Wiley.Mobile.Python.Rapid.prototyping.of.applications.on.the.mobile.platform.Dec.2007 (779889), страница 39
Текст из файла (страница 39)
The parts cover thefollowing functionalities:• constants and result parsing202WEB SERVICES•fetching map images from Yahoo! Maps•user interface functions.MopyMaps! uses the standard appuifw UI framework (Figure 9.1).The map location can be changed by way of a menu item (new map)that triggers a request to the map service. The application body contains a Canvas object to which the received map image is plotted(handle redraw). The image can be moved around using the arrowkeys (handle keys). Messages related to map loading and possibleerror conditions are drawn on the canvas as well (show text).(a)(b)Figure 9.1(c)MopyMaps! (a) Welcome screen, (b) map display and (c) address dialogLet’s dive into the code!9.2.1 Constants and Result ParsingExample 87: MopyMaps! (1/3)import urllib, appuifw, e32, graphics, key_codes, os, os.pathAPP_ID = "reBqRjOdK4E3aKfuRioOj3459Kmas_ENg7!!"MAP_URL = "http://local.yahooapis.com/MapsService/V1/mapImage?"MAP_FILE = u"E:\\Images\\mopymap.png"if not os.path.exists("E:\\Images"):os.makedirs("E:\\Images")def naive_xml_parser(key, xml):key = key.lower()for tag in xml.split("<"):tokens = tag.split()if tokens and tokens[0].lower().startswith(key):return tag.split(">")[1].strip()return NoneMOPYMAPS! MOBILE YAHOO! MAPS203Three constants are used by the application: APP ID is the applicationID that you got from the Yahoo! developer site.
Replace the string in theexample with your personal ID. MAP URL is the URL of the service. Youcan check the current address at the API documentation page, in casethe address has changed since the book’s publication. MAP FILE is atemporary file name for the map images.The Yahoo! service does not return a map image directly after a request.Instead, it returns a small XML message that contains a temporary URLwhere the image can be retrieved (or an error message, if a suitable mapcould not be found). The responses look like this:<?xml version="1.0"?><Result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://gws.maps.yahoo.com/mapimage?MAPDATA=LkE5ukmEyGeRhYl...</Result><!-- ws01.search.scd.yahoo.com uncompressed -->Officially, it should be the job of an XML parser to parse and convert astring like this to a convenient data structure.
However, if we know thatthe messages are going to be really simple, as in this case, we may use alighter solution instead of a fully fledged XML parser.Function naive xml parser() is such a solution. Given a simplestring of XML, xml, it returns the contents of the first tag whose namebegins with key. In MopyMaps! this function is used to extract from theResult tag either the map URL or an error message, if the URL could notbe found.The function works by first splitting the string using < characters asdelimiters, which correspond to the beginnings of XML tags. Then, thebeginning of each token is compared with the given key to find thematching tag. Matching is case insensitive. The function returns None ifno matching tag was found.You should be extremely careful when using an ad hoc solutionlike this. Even a minor change in the message format may cause thefunction to return incorrect results.
A real XML parser is probably a moresuitable solution for any widely distributed application. The functionnaive xml parser() is used here for its brevity and simplicity, notbecause of its robustness.9.2.2 Fetching Map Images from Yahoo! MapsExample 88 contains the core of the MopyMaps! application. It fetchesmap images from Yahoo! Maps. The functionality for retrieving the imagesis divided between two functions: new map() asks the user for a newaddress and requests the corresponding map image URL from the service;load image() is then responsible for retrieving the actual image data,given the new URL. It also resets the map position (variables map x and204WEB SERVICESmap y), clears any status messages (status) and loads the new mapimage into a global Image object, mapimg, that is used to hold thedisplayed map.As mentioned above, Yahoo! Maps provides a simple interface basedon plain HTTP.
This means that request parameters are provided to theservice in the URL. There is no need to construct special request messages.This is good news for us.Example 88: MopyMaps! (2/3)def new_map():addr = appuifw.query(u"Address:", "text")if not addr:returnparams = {"location": addr,"appid": APP_ID,"image_type": "png","image_height": "600","image_width": "600","zoom": "6"}show_text(u"Loading map...")try:url = MAP_URL + urllib.urlencode(params)res = urllib.urlopen(url).read()except:show_text(u"Network error")returnimg_url = naive_xml_parser("result", res)if img_url:show_text(u"Loading map......")load_image(img_url)handle_redraw(canvas.size)else:msg = naive_xml_parser("message", res)show_text(u"%s" % msg)def load_image(url):global mapimg, map_x, map_y, statusres = urllib.urlopen(url).read()f = file(MAP_FILE, "w")f.write(res)f.close()mapimg = graphics.Image.open(MAP_FILE)map_x = mapimg.size[0] / 2 - canvas.size[0] / 2map_y = mapimg.size[1] / 2 - canvas.size[1] / 2status = NoneSince we use the standard urllib.urlencode() function to construct the request URL, parameters must be given in a dictionary.
Thedictionary params specifies the new map:•location is the address string given by the user.•appid is the Yahoo! Application ID.MOPYMAPS! MOBILE YAHOO! MAPS205•image type specifies that we want the image in PNG format.•image height and image width give the requested map size inpixels – you may increase these values if you want a larger area forscrolling.• zoom specifies the map scale in a range from 1 (street level) to 12(country level).You could enhance the application by letting the user modify the zoomlevel. You can find the full list of supported parameters in the Yahoo!Map Image API documentation.After the request parameters have been encoded to the URL string url,we send the request to the service with a familiar urllib.urlopen()call.
If any exceptions occur during the call, an error message is shownand the function returns. Otherwise we receive a response XML message,which contains the actual image URL inside a result tag. If our simpleXML parser does not find the result tag, we try to find an error messageinside a message tag instead, which is then shown on the screen.If the image URL is found, that is, img url is not None, the functionload image is called to load the actual image. This function retrievesthe image data from the result URL, writes the image to a local file andloads it to an image object mapimg. Since the service returns the mapscentered at the requested address, we initialize the map position (map xand map y) to the middle of the canvas.The map loading progress is shown by an increasing number of dotsafter the ‘Loading map’ text.
Three dots are shown when the map imageURL is first requested. Six dots are shown when we start to load theactual image.9.2.3 User Interface FunctionsThe last part of MopyMaps! is presented in Example 89. This part puts thepieces together by constructing the application UI. The core functionalityhere is formed by the two canvas callbacks, handle redraw() andhandle keys() whose roles are already familiar from Chapter 5.Example 89: MopyMaps! (3/3)def show_text(txt):global statusstatus = txthandle_redraw(canvas.size)def handle_redraw(rect):if mapimg:canvas.blit(mapimg, target=(0, 0), source=(map_x, map_y))else:206WEB SERVICEScanvas.clear((255, 255, 255))if status:canvas.text((10, 50), status, fill=(0, 0, 255), font="title")def handle_keys(event):global map_x, map_yif event['keycode'] == key_codes.EKeyLeftArrow:map_x -= 10elif event['keycode'] == key_codes.EKeyRightArrow:map_x += 10elif event['keycode'] == key_codes.EKeyUpArrow:map_y -= 10elif event['keycode'] == key_codes.EKeyDownArrow:map_y += 10handle_redraw(canvas.size)def quit():app_lock.signal()map_x = map_y = 0mapimg = status = Noneappuifw.app.exit_key_handler = quitappuifw.app.title = u"MopyMaps!"appuifw.app.menu = [(u"New location", new_map), (u"Quit", quit)]canvas = appuifw.Canvas(redraw_callback = handle_redraw,event_callback = handle_keys)appuifw.app.body = canvasshow_text(u"Welcome to MopyMaps!")app_lock = e32.Ao_lock()app_lock.wait()Arrow keys are used to move the map image on the screen.
This isnot too difficult, since we can specify the source coordinates for thecanvas.blit() function, which correspond to the upper left corner ofthe map image. By modifying the corner coordinates map x and map y,we can alter the part of the map shown on the canvas. Note that nochecks are made that the map image fully fills the canvas. You cansee the results of this easily by scrolling far enough on the map.