Use Python to retrieve and display images from space
pyroClient.py
I've already talked about most of the libraries used in pyroClient.py
(Listing 4), the display script for the interactive version of the sun display, but cherrypy
and thread
are new. Here, cherrypy
turns a Python class into a web server, and thread
allows you to start subprocesses that run concurrently with your main program. By combining cherrypy
and thread
, I can create a web interface that allows the display to be controlled without affecting what's currently displayed on screen.
Listing 4
pyroClient.py
01 import Pyro4 02 import pygame 03 import cPickle as pickle 04 import cherrypy 05 import thread 06 07 class web: 08 def __init__ ( self , server , display ): 09 self.server = server 10 self.display = display 11 12 @cherrypy.expose 13 def index ( self ): 14 html = "<h1>Sun Display Channel Selection</h1>" 15 channels = self.server.channelList() 16 for channel in channels: 17 html += "<div><a href='loadChannel?name=" + \ channel + "'>" + channel + "</a></div>" 18 return html 19 20 @cherrypy.expose 21 def loadChannel ( self , name ): 22 self.display.remoteLoad = name 23 return self.index() 24 25 class sunDisplay: 26 def __init__ ( self , screen , server ): 27 self.screen = screen 28 self.server = server 29 self.remoteLoad = None 30 31 def getFiles ( self , channel , count ): 32 self.frames = list() 33 files = server.fileList ( channel ) 34 for i in range ( count ): 35 if len ( self.frames ) > 0: 36 self.screen.blit ( self.frames [ -1 ] , ( 0 , 0 ) ) 37 pygame.display.flip() 38 39 package = server.getFile ( channel , files \ [ "files" ] [ i ] ) 40 sun = pickle.loads ( package.encode ( "ascii" ) ) 41 image = pygame.image.fromstring \ ( sun , ( 768 , 768 ) , "RGB" ) 42 self.frames.append ( image ) 43 print i 44 45 def loop ( self ): 46 for frame in self.frames: 47 self.screen.blit ( frame , ( 0 , 0 ) ) 48 pygame.display.flip() 49 50 uri = raw_input ( "uri" ).strip() 51 sunImages = Pyro4.Proxy ( uri ) 52 channels = sunImages.channelList() 53 54 pygame.display.init() 55 screen = pygame.display.set_mode ( ( 1024 , 768 ) ) 56 57 display = sunDisplay ( screen , sunImages ) 58 59 thread.start_new_thread ( cherrypy.quickstart , \ ( web ( sunImages , display ) , ) ) 60 61 display.getFiles ( channels [ 0 ] , 60 ) 62 63 while 1: 64 looping = True 65 while looping: 66 if display.remoteLoad != None: looping = False 67 display.loop() 68 display.getFiles ( display.remoteLoad , 60 ) 69 display.remoteLoad = None
The web
class (lines 7-23) creates the functions that become web pages on the interface cherrypy
, which defaults to port 8080. If I visit [display ip]:8080 then, I'll get the results of the index
function. In the init class, all I do is copy parameters to class objects so they are accessible in other class methods. server
is the Pyro remote class that connects to pyroServer.py
.
The index
function creates the simple web page that you see to control the display, and @cherrypy.expose
tells CherryPy to make this function accessible via the web interface. Without it, the function won't be accessible as a web page.
On line 15, self.server.channelList
is a remote call to pyroServer.py
, which will get a list of available channels. Lines 16 and 17 create a <div>
and a link for each channel, and line 18 returns the generated HTML.
Next, the loadChannel
function accepts one parameter – name
– the channel name to load. Because this is a cherrypy
function, the parameter must be provided by the web browser as either a GET or POST variable. Line 22 sets remoteLoad
in the display
class and then returns index
again to continue to provide the selection list (line 23).
In the sunDisplay
class (lines 25-48), I start by creating class variables from parameters and initializing self.remoteLoad
to None
.
Again, getFiles
retrieves sun images from the server, and self.frames
is the list where I store them. After getting the list of file names of the selected channel from the server, the loop starting on line 34 gets the images and uses PyGame to display the most recent frame. Lines 39-42 retrieve the image
package from the server, unpack the pickle string, convert the string back to a PyGame surface, and add the unpacked image to the list of current frames.
The short loop
function (lines 45-48) shows each frame in the list on the display. Once it's run, the function returns, allowing the display to check whether it needs to change the channel currently being shown.
On line 50, the program asks for the URL of the Pyro server. Once obtained, the connection is established and sunImages.channelList()
fetches a list of available channels.
Lines 54-57 start PyGame, save a 1024x768 graphics window to the screen
variable, and create the sunDisplay
class, passing screen
and sunImages
to it so that it can access the display and the server.
Line 59 uses threading to start the web server in a separate process. Thus, it runs concurrently with the display and doesn't prevent the screen from updating. Line 61 fetches images from the first channel in the list so that the display will show something immediately; then, the main loop starts.
Lines 63-69 set up an infinite loop and another loop that exits if a new channel has been requested; otherwise, it loops through the images (line 67). If a new channel is requested, lines 68 and 69 fetch the new channel images and set remoteLoad
to None
so the loop continues, beginning the process again!
Conclusion
The sun display project has become an interesting conglomeration of Python modules as I searched for the best way to handle all of the requirements. The end result, however, is a unique set of displays that offer control and flexibility beyond what is immediately obvious. That flexibility allows planetarium presenters to educate guests more freely and have more options in how they present the SDO materials.
Infos
- Fort Worth Museum of Science and History: http://www.fwmsh.org
- Solar Dynamics Observatory: https://www.nasa.gov/mission_pages/sdo/main/index.html
- SDO data repository: http://sdo.gsfc.nasa.gov/
- Listings for this article: ftp://ftp.linux-magazine.com/pub/listings/raspberry-pi-geek.com/13
« Previous 1 2 3 4 5 Next »
Buy this article as PDF
(incl. VAT)