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.
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
(incl. VAT)
Buy Linux Magazine
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.
News
-
Linux Servers Targeted by Akira Ransomware
A group of bad actors who have already extorted $42 million have their sights set on the Linux platform.
-
TUXEDO Computers Unveils Linux Laptop Featuring AMD Ryzen CPU
This latest release is the first laptop to include the new CPU from Ryzen and Linux preinstalled.
-
XZ Gets the All-Clear
The back door xz vulnerability has been officially reverted for Fedora 40 and versions 38 and 39 were never affected.
-
Canonical Collaborates with Qualcomm on New Venture
This new joint effort is geared toward bringing Ubuntu and Ubuntu Core to Qualcomm-powered devices.
-
Kodi 21.0 Open-Source Entertainment Hub Released
After a year of development, the award-winning Kodi cross-platform, media center software is now available with many new additions and improvements.
-
Linux Usage Increases in Two Key Areas
If market share is your thing, you'll be happy to know that Linux is on the rise in two areas that, if they keep climbing, could have serious meaning for Linux's future.
-
Vulnerability Discovered in xz Libraries
An urgent alert for Fedora 40 has been posted and users should pay attention.
-
Canonical Bumps LTS Support to 12 years
If you're worried that your Ubuntu LTS release won't be supported long enough to last, Canonical has a surprise for you in the form of 12 years of security coverage.
-
Fedora 40 Beta Released Soon
With the official release of Fedora 40 coming in April, it's almost time to download the beta and see what's new.
-
New Pentesting Distribution to Compete with Kali Linux
SnoopGod is now available for your testing needs