Clean up some code, add __del__ handler, make methods and names more clear for inexperienced users, handle keyboard exits, add server autodiscovery questions if multiple servers are found

This commit is contained in:
Jan Klopper 2017-06-16 15:14:11 +02:00
parent 52adfbbe66
commit a001e41a8b
3 changed files with 58 additions and 167 deletions

View file

@ -11,15 +11,18 @@ __author__ = "Jan Klopper <jan@underdark.nl>"
import random import random
from vloed import PixelVloedClient, Packet, RGBPixel, MAX_PIXELS from vloed import PixelVloedClient, Packet, RGBPixel, MAX_PIXELS
def RandomFill(pixels, width, height): def RandomFill(screen, width, height):
"""Generates a random number of pixels with a random color""" """Generates a random number of pixels with a random color"""
for pixel in xrange(0, random.randint(10, MAX_PIXELS)): for _x in xrange(1, # at least one pixel
pixel = RGBPixel(random.randint(0, width), random.randint(10, MAX_PIXELS) # at most the max number of pixels
random.randint(0, height), ):
random.randint(0, 255), pixel = RGBPixel(random.randint(0, width), # select a random position on the width of the screen
random.randint(0, 255), random.randint(0, height), # select a random position on the height of the screen
random.randint(0, 255)) random.randint(0, 255), # select a random value for red
pixels.append(pixel) random.randint(0, 255), # select a random value for green
random.randint(0, 255) # select a random value for blue
)
screen.show(pixel) # lets push the pixel to the screen!
def RunClient(options): def RunClient(options):
"""Discover the servers and start sending to the first one""" """Discover the servers and start sending to the first one"""
@ -32,19 +35,14 @@ def RunClient(options):
options.height # Screen pixels height, None for autodetect options.height # Screen pixels height, None for autodetect
) )
# Lets create a screen which buffers the pixels we add to it, and sends them to the actual screen.
screen = Packet(client)
# loop the effect until we cancel by pressing ctrl+c / exit the program # loop the effect until we cancel by pressing ctrl+c / exit the program
while True: while screen:
# packet will automatically send its pixels if gets to the maximum pixel length # add some pixels to the screen with our functions
pixels = Packet(client)
# add some pixels to the output with our functions
# the width/height are read from the client's config # the width/height are read from the client's config
RandomFill(pixels, client.width, client.height) RandomFill(screen, client.width, client.height)
# send whatever pixels are left in the packet
pixels.flush()
if __name__ == '__main__': if __name__ == '__main__':
# if this script is called from the command line, and thus not imported # if this script is called from the command line, and thus not imported

View file

@ -183,140 +183,6 @@ class Canvas(object):
"""Clean up any sockets we created""" """Clean up any sockets we created"""
self.broadcastsocket.close() self.broadcastsocket.close()
class PixelVloedClient(object):
"""Sets up a client
Arguments:
firstserver: (bool) False, select the first server immediately
debug: (bool) False
ip: (str) None
port: (int) None
width: (int) 640
height: (int) 480
Listens for servers if no ip is given
"""
def __init__(self, firstserver=False, debug=False,
ip=None, port=None,
width=640, height=480):
self.sleep = 0.01
self.debug = debug
if not ip:
servers = False
while servers == False:
servers = self.DiscoverServers(firstserver)
self.ipaddress = servers[0]['ip']
self.port = servers[0]['port']
self.width = servers[0]['width']
self.height = servers[0]['height']
if self.debug:
print ('displaying on %(ip)s:%(port)d, %(width)d*%(height)dpx' %
servers[0])
else:
self.ipaddress = ip
self.port = port if port else UDP_PORT
self.width = width
self.height = height
if self.debug:
print ('displaying on %(ip)s:%(port)d, %(width)d*%(height)dpx' %
self)
self.sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
def Sleep(self, duration=None):
"""Sleeps the designated amount of time"""
time.sleep(duration if duration else self.sleep)
def SendPacket(self, message, sleep=0.01):
"""Sends the message to the udp server
Arguments:
message: (str, 140)
sleep: (float) 0.01, duration of time the client should sleep
"""
self.sock.sendto(message, (self.ipaddress, self.port))
if sleep:
self.Sleep(duration=sleep)
@staticmethod
def DiscoverServers(returnfirst=False, timeout=5):
"""Discover servers that send out the pixelvloed preample"""
discoverysock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
discoverysock.bind(('', DISCOVER_PORT))
starttime = time.time()
servers = []
foundhash = {}
while (time.time() - timeout) < starttime:
data, _addr = discoverysock.recvfrom(1024)
try:
if data.startswith(PROTOCOL_PREAMBLE):
dataset = data.split(' ')
if float(dataset[0].split(':')[1]) <= MAX_PROTOCOL_VERSION:
ipaddress = dataset[1].split(':')[0]
port = int(dataset[1].split(':')[1])
width = int(dataset[2].split('*')[0])
height = int(dataset[2].split('*')[1])
if data not in foundhash:
newserver = {'ip': ipaddress,
'port': port,
'width': width,
'height': height}
foundhash[data] = True
print 'New pixelvloed screen found: %r' % newserver
servers.append(newserver)
if returnfirst:
return servers
except:
pass
if servers:
return servers
return False
def NewMessage():
"""Creates a new message with the correct max size, rgb mode and version"""
message = MaxSizeList(MAX_PIXELS+2)
message.append(SetRGBAMode(False))
message.append(SetVersionBit())
return message
def RGBPixel(x, y, r, g, b, a=None): # pylint: disable=C0103
"""Generates the packed data for a pixel"""
if a is not None:
return struct.pack("<2H4B", x, y, r, g, b, a)
return struct.pack("<2H3B", x, y, r, g, b)
def SetRGBAMode(mode):
"""Generate the rgb/rgba bit"""
return struct.pack("<?", mode)
def SetVersionBit(protocol=1):
"""Generate the Version bit"""
return struct.pack("<B", protocol)
class MaxSizeList(list):
"""A list that raises an indexError when it reaches the designated max size"""
def __init__(self, maxcount=100):
"""Inits a list with a maxcount
Arguments:
maxcount: (int) 100
"""
self.maxsize = maxcount
super(MaxSizeList, self).__init__()
def append(self, item):
"""Appends an item to the list"""
if self.__len__() == self.maxsize:
raise IndexError('max size reached')
super(MaxSizeList, self).append(item)
def RunServer(options):
"""Runs a pixelvloed server"""
PixelVloedServer('%s:%d' %(options.ip, options.port),
options=options).serve_forever()
if __name__ == '__main__': if __name__ == '__main__':
import sdl2.ext import sdl2.ext

View file

@ -173,29 +173,47 @@ class PixelVloedClient(object):
"""Sets up a client """Sets up a client
Arguments: Arguments:
firstserver: (bool) False, select the first server immediately firstserver: (bool) True, select the first server immediately
debug: (bool) False debug: (bool) False
ip: (str) None ip: (str) None
port: (int) None port: (int) None
width: (int) 640 width: (int) 640
height: (int) 480 height: (int) 480
Listens for servers if no ip is given,
Listens for servers if no ip is given It will bind to the first server it hears of when firstserver is set to True.
Otherwise if will show a list of servers if a choise if available.
""" """
def __init__(self, firstserver=False, debug=False, def __init__(self, firstserver=True, debug=False,
ip=None, port=None, ip=None, port=None,
width=640, height=480): width=640, height=480,
autoconnect=True):
self.sleep = 0.01 self.sleep = 0.01
self.debug = debug self.debug = debug
if not ip: if not ip:
servers = False servers = False
while servers == False: while servers == False:
servers = self.DiscoverServers(firstserver) servers = self.DiscoverServers(firstserver)
self.ipaddress = servers[0]['ip'] if firstserver or len(servers) == 1:
self.port = servers[0]['port'] self.ipaddress = servers[0]['ip']
self.width = servers[0]['width'] self.port = servers[0]['port']
self.height = servers[0]['height'] self.width = servers[0]['width']
self.height = servers[0]['height']
else:
# lets list all found servers and allow the user to make a selection
for i in xrange(0, len(servers)):
print('ID: %d' % i)
print('%(ip)s:%(port)d, %(width)d*%(height)dpx\n' % servers[i])
while not self.ipaddress:
try:
choice = int(raw_input("Which server? Type the ID"))
self.ipaddress = servers[choice]['ip']
self.port = servers[choice]['port']
self.width = servers[choice]['width']
self.height = servers[choice]['height']
except:
print("Invalid input received, try again.")
if self.debug: if self.debug:
print('displaying on %(ip)s:%(port)d, %(width)d*%(height)dpx' % print('displaying on %(ip)s:%(port)d, %(width)d*%(height)dpx' %
@ -207,10 +225,7 @@ class PixelVloedClient(object):
self.height = height self.height = height
if self.debug: if self.debug:
print('displaying on %(ipaddress)s:%(port)d, %(width)d*%(height)dpx' % print('displaying on %(ipaddress)s:%(port)d, %(width)d*%(height)dpx' %
{'ipaddress': self.ipaddress, self.__dict__)
'port': self.port,
'width': self.width,
'height': self.height})
self.sock = socket.socket(socket.AF_INET, # Internet self.sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP socket.SOCK_DGRAM) # UDP
@ -229,8 +244,7 @@ class PixelVloedClient(object):
if sleep: if sleep:
self.Sleep(duration=sleep) self.Sleep(duration=sleep)
@staticmethod def DiscoverServers(self, returnfirst=False, timeout=5):
def DiscoverServers(returnfirst=False, timeout=5):
"""Discover servers that send out the pixelvloed preample""" """Discover servers that send out the pixelvloed preample"""
discoverysock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) discoverysock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
discoverysock.bind(('', DISCOVER_PORT)) discoverysock.bind(('', DISCOVER_PORT))
@ -242,6 +256,7 @@ class PixelVloedClient(object):
try: try:
if data.startswith(PROTOCOL_PREAMBLE): if data.startswith(PROTOCOL_PREAMBLE):
dataset = data.split(' ') dataset = data.split(' ')
if float(dataset[0].split(':')[1]) <= MAX_PROTOCOL_VERSION: if float(dataset[0].split(':')[1]) <= MAX_PROTOCOL_VERSION:
ipaddress = dataset[1].split(':')[0] ipaddress = dataset[1].split(':')[0]
port = int(dataset[1].split(':')[1]) port = int(dataset[1].split(':')[1])
@ -253,10 +268,14 @@ class PixelVloedClient(object):
'width': width, 'width': width,
'height': height} 'height': height}
foundhash[data] = True foundhash[data] = True
print('New pixelvloed screen found: %r' % newserver)
servers.append(newserver) servers.append(newserver)
if self.debug:
print('New pixelvloed screen found: %r' % newserver)
if returnfirst: if returnfirst:
return servers return servers
elif self.debug:
print('''skipping pixelvloed screen that we already knew
about %r''' % newserver)
except: except:
pass pass
if servers: if servers:
@ -333,6 +352,10 @@ class Packet(list):
self._send() self._send()
super(Packet, self).append(item) super(Packet, self).append(item)
def show(self, item):
"""Nicer name for append"""
return self.append(item)
def flush(self): def flush(self):
"""Immediately send all pixels currently in this packet and empty it""" """Immediately send all pixels currently in this packet and empty it"""
self._send() self._send()
@ -341,6 +364,10 @@ class Packet(list):
self.client.SendPacket(''.join(self)) self.client.SendPacket(''.join(self))
del self[MESSAGE_HEADER_SIZE:] # reset packet del self[MESSAGE_HEADER_SIZE:] # reset packet
def __del__(self):
"""Clean up by sending any remaining pixels"""
self._send()
def RunServer(options): def RunServer(options):
"""Runs a pixelvloed server""" """Runs a pixelvloed server"""
PixelVloedServer('%s:%d' %(options.ip, options.port), PixelVloedServer('%s:%d' %(options.ip, options.port),