Pyro – Networking made simple

Updating the Screens

The @Pyro4.oneway decorator (line 45) tells Pyro not to wait for a response, but to run this method in its own thread so the main program doesn't block. In this way, the controller isn't waiting for each display to update before returning to monitor the GUI.

The title is the team name displayed at the top of the screen. After storing the new team name in the class (line 47), line 48 blits the background image onto the entire display to erase it. Next, I call drawTitle and drawScoreLocal to redraw all of the information for the scoreboard. Finally, pygame.display.flip draws everything to the screen to make the changes visible.

The sole purpose of the utility function timerRunning (line 55) is to allow the controller to check whether the timer is still being displayed by returning self.running. I could have done this with a property, but this method works just as well.

The drawTitle function under the oneway decorator renders the title (line 60), calculates a horizontal center point (line 61), and then draws it onscreen (line 62). Because Python was having locking problems when updating the score, drawing locally instead of in another thread spun off by Pyro solved the problem. Therefore, the drawScore function exposed to Pyro (line 66) calls the internal function drawScoreLocal, which does the work. This local function renders the score twice: once in black (line 71) and once in white (line 72).

The next task is to calculate the horizontal center (line 74), define the bounding box of the score display in a PyGame Rect (line 76), and then blit only that portion of the background image to clear the score (line 77). The shadow color is blitted first (line 79) followed by the score rendered in white (line 80). Finally, I call the update function and pass blitArea as a list, which tells PyGame to update only that region of the screen, rather than the entire thing. Updating the screen in this way aids in performance, because drawScoreLocal will be called quite often.

To update the score (lines 84-101), the updateScore function works a little differently from drawScore, in that it increments the score five points at a time. Line 86 updates self.scoreGoal, which is the score to be displayed, and line 87 adds 1/100th of a second to the current time for the next display update. The while loop (line 89) first checks the current time and, if it is not time to update, simply checks again until it is time. This loop continues until the currently displayed score and the goal score match. Lines 95-100 increment or decrement the score and then call drawScoreLocal to show it onscreen.

Timers

The startTimer function block switches the display into timer mode by recording the current time (line 105), setting the self.running flag (line 106), and calling self.updateTimer (line 107).

The updateTimer function block draws the timer onscreen until the button is pressed (GPIO goes LOW). Line 110 calculates the time 1/100th of a second in the future, and the while loop (line 111) runs for as long as the self.running flag is True. If it is not, it continues checking until it is (line 112). Line 113 updates the time for the next pass through the loop, and line 115 calculates the elapsed time.

The next three lines render the current elapsed time in both a shadow and foreground color and calculate the horizontal center. Lines 121 and 122 blit a small portion of the background over the previous timer entry to erase it, and then lines 124 and 125 blit the shadow and foreground surfaces before line 126 draws the update to the screen. Finally, line 128 checks the GPIO input, and if it is 0 (LOW), the self.running flag is set to False to exit the loop. The update method allows the controller to force a screen refresh.

For the daemon to receive commands across the network, line 138 defines its IP address. Without it, the daemon will only listen on localhost. Line 139 uses Pyro's serveSimple utility function to start a daemon. I provide a dictionary of the class I want to serve (line 140, left of the colon) and what it should be called (scoreboard2, right of the colon). The final argument (line 141) asks Pyro to register this daemon with the name server.

Web Manager

The Web Manager controls everything. At any given time, it was usually loaded on my cell phone, an iPad, and a computer at the media station. The web manager (Figure 5) uses CherryPy [3] to serve a web page with JavaScript, which returns user requests via Ajax. When the Ajax calls are received, the manager script calls remote Pyro functions to control the scoreboard.

Figure 5: The web manager is a bare-bones interface, because it is only accessed by the crew. The "Vince Casey" and "Casey Vince" buttons changed the displays to show the names of the emcees as they came on stage. If they switched places, the names would follow.

For the manager (Listing 4), I needed two libraries: cherrypy to act as a web server and Pyro4 to talk to the Pyro objects already discussed.

Listing 4

webManager.py

 

CherryPy operates by taking a Python class and exposing certain methods as web addresses. For instance, http://webManager.local:8080/updateTitle calls the associated self.updateTitle class method. Any POST or GET variables show up as named arguments. Just as in Pyro, only methods with the @cherrypy.expose decorator are accessible via HTTP, so the __init__ class that sets up all of the Pyro objects can't be accessed from a web server.

Unlike Pyro, CherryPy calls the __init__ method regardless of whether anything has connected or not, so line 6 creates a list of screens, and lines 7-10 ask Pyro to go find the addresses of the named objects from the name server and append them to the screen list; the initial graphics then are displayed on all four screens (lines 12-14).

Just like index.html in Apache, the index function here is the default destination if no specific address is specified. In this case, lines 18-166 define the manager web page. Although the HTML and JavaScript are beyond the scope of this article, the following is a brief summary.

Each of the JavaScript functions (e.g., adjustScore, lines 22-31) defines a function that can be called by a button or JavaScript event on the manager web page. The obj created in line 24 contains the POST variables (i.e., screen, the screen to update, and delta, how much to change the score).

jQuery then posts the request to adjustScore (line 28), which is defined in the next line. The inline function is called when the Ajax call completes and receives data, which is anything that the Python function that follows returns.

The updateTitle function (lines 169-170) is exposed by CherryPy and called by jQuery, as described above. The argument screen is used as an index for self.screens, and updateTitle is called again on the remote object via Pyro. All of that happens behind the scenes, though, so the Python code here doesn't look any different. The same thing happens with adjustScore (lines 173-174).

The startTimers function loops over the self.screens list (line 178) and starts the timer on all screens (line 179). This remote Pyro call runs on each Raspberry Pi. The function then sits in a loop (lines 182-186) calling screen.timerRunning on each Pi until one returns False (line 184). That screen's title is updated to show WINNER and a flag is set that first place has been found (line 186).

Setting the socket_host to 0.0.0.0 in line 189 tells CherryPy to respond to all requests regardless to which network adapter or address they arrive. The final line uses CherryPy's quickstart function to start serving the application from web()< at address "/".

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • DIY Scoreboard

    We look at a broadcast video system network that uses Python code to control a video router and check out another program that creates a scoreboard.

  • Nerf Target Game

    A cool Nerf gun game for a neighborhood party provides a lesson in Python coding with multiple processors.

  • Python generators simulate gambling

    Can 10 heads in a row really occur in a coin toss? Or, can the lucky numbers in the lottery be 1, 2, 3, 4, 5, 6? We investigate the law of large numbers.

  • Csound

    The powerful Csound software provides an impressive set of features for audio production and processing. We walk you through the entire system.

  • Panda3D

    Several free game engines are available for Linux users, but programming with them is often less than intuitive. Panda3D is an easy-to-use engine that is accessible enough for newcomers but still powerful enough for the pros at Disney Studios.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News