Add pylintrc file
Move client functions to vloed module, simplify client file for newbee's, Make sure the server module only starts operating when actually initialised
This commit is contained in:
parent
562ae26d7f
commit
b9ba710848
3 changed files with 503 additions and 114 deletions
109
client.py
109
client.py
|
|
@ -5,48 +5,15 @@ Inspired by the PixelFlut beamer on eth0:winter 2016 and
|
||||||
code from https://github.com/defnull/pixelflut/
|
code from https://github.com/defnull/pixelflut/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = 0.2
|
__version__ = 0.3
|
||||||
__author__ = "Jan Klopper <jan@underdark.nl>"
|
__author__ = "Jan Klopper <jan@underdark.nl>"
|
||||||
|
|
||||||
MAX_PROTOCOL_VERSION = 1;
|
|
||||||
PROTOCOL_PREAMBLE = 'pixelvloed'
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
import random
|
import random
|
||||||
import time
|
from vloed import PixelVloedClient, NewMessage, RGBPixel
|
||||||
|
|
||||||
class MaxSizeList(list):
|
def RandomFill(message, width, height):
|
||||||
|
|
||||||
def __init__(self, maxcount=100):
|
|
||||||
self.maxsize = maxcount
|
|
||||||
super( MaxSizeList, self ).__init__()
|
|
||||||
|
|
||||||
def append(self, item):
|
|
||||||
super( MaxSizeList, self ).append(item)
|
|
||||||
if self.__len__() == self.maxsize:
|
|
||||||
raise IndexError('max size reached')
|
|
||||||
|
|
||||||
def RGBPixel(x, y, r, g, b, a=None): # pylint: disable=C0103
|
|
||||||
"""Generates the packed data for a pixel"""
|
|
||||||
if a:
|
|
||||||
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)
|
|
||||||
|
|
||||||
def RandomFill(width=640, height=480):
|
|
||||||
"""Generates a random number of pixels with a random color"""
|
"""Generates a random number of pixels with a random color"""
|
||||||
message = MaxSizeList(140)
|
for pixel in xrange(0, random.randint(10, 140)):
|
||||||
message.append(SetRGBAMode(False))
|
|
||||||
message.append(SetVersionBit())
|
|
||||||
for pixel in xrange(0, random.randint(10, 100)):
|
|
||||||
pixel = RGBPixel(random.randint(0, width),
|
pixel = RGBPixel(random.randint(0, width),
|
||||||
random.randint(0, height),
|
random.randint(0, height),
|
||||||
random.randint(0, 255),
|
random.randint(0, 255),
|
||||||
|
|
@ -57,57 +24,29 @@ def RandomFill(width=640, height=480):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
yield ''.join(message)
|
yield ''.join(message)
|
||||||
message[2:] = []
|
message[2:] = []
|
||||||
message.append(pixel)
|
|
||||||
yield ''.join(message)
|
yield ''.join(message)
|
||||||
|
|
||||||
def SendPacket(ipaddress, port, message):
|
def RunClient():
|
||||||
"""Sends the message to the udp server"""
|
|
||||||
sock = socket.socket(socket.AF_INET, # Internet
|
|
||||||
socket.SOCK_DGRAM) # UDP
|
|
||||||
sock.sendto(message, (ipaddress, port))
|
|
||||||
|
|
||||||
def DiscoverServers(discoveryport, timeout=5):
|
|
||||||
"""Discover servers that send out the pixelfvloed preample"""
|
|
||||||
DiscoverySock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
DiscoverySock.bind(('', discoveryport))
|
|
||||||
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:
|
|
||||||
ip = 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': ip,
|
|
||||||
'port': port,
|
|
||||||
'width': width,
|
|
||||||
'height': height}
|
|
||||||
foundhash[data] = True
|
|
||||||
print 'New pixelvloed screen found: %r' % newserver
|
|
||||||
servers.append(newserver)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if servers:
|
|
||||||
return servers
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Discover the servers and start sending to the first one"""
|
"""Discover the servers and start sending to the first one"""
|
||||||
discoveryport = 5006
|
|
||||||
servers = False
|
client = PixelVloedClient(True, # start as soon as we find a server
|
||||||
while servers == False:
|
False, # show debugging output
|
||||||
servers = DiscoverServers(discoveryport)
|
None, # ip of the server, None for autodetect
|
||||||
print 'displaying on %(ip)s:%(port)d, %(width)d*%(height)dpx' % servers[0]
|
None, # port of the server None for autodetect
|
||||||
|
None, # Screen pixels wide, or Autodetect
|
||||||
|
None # Screen pixels height, or Autodetect
|
||||||
|
)
|
||||||
|
message = NewMessage() #create a new message that buffers the output etc
|
||||||
|
|
||||||
|
# loop the effect untill we cancel by pressing ctrl+c / exit the program
|
||||||
while True:
|
while True:
|
||||||
for packet in RandomFill(servers[0]['width'], servers[0]['height']):
|
# create a new message and send if everytime the buffer is full
|
||||||
time.sleep(0.01)
|
# the width/height are read from the client's config
|
||||||
SendPacket(servers[0]['ip'], servers[0]['port'], packet)
|
for packet in RandomFill(message, client.width, client.height):
|
||||||
|
# send the message we just filled with random pixelfs
|
||||||
|
client.SendPacket(packet)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
# if this script is called from the commandline, and thus not imported
|
||||||
|
# Start a client and start sending messages
|
||||||
|
RunClient()
|
||||||
|
|
|
||||||
311
pylintrc
Normal file
311
pylintrc
Normal file
|
|
@ -0,0 +1,311 @@
|
||||||
|
# lint Python modules using external checkers.
|
||||||
|
#
|
||||||
|
# This is the main checker controling the other ones and the reports
|
||||||
|
# generation. It is itself both a raw checker and an astng checker in order
|
||||||
|
# to:
|
||||||
|
# * handle message activation / deactivation at the module level
|
||||||
|
# * handle some basic but necessary stats'data (number of classes, methods...)
|
||||||
|
#
|
||||||
|
[MASTER]
|
||||||
|
|
||||||
|
# Specify a configuration file.
|
||||||
|
#rcfile=
|
||||||
|
|
||||||
|
# Python code to execute, usually for sys.path manipulation such as
|
||||||
|
# pygtk.require().
|
||||||
|
#init-hook=
|
||||||
|
|
||||||
|
# Profiled execution.
|
||||||
|
profile=no
|
||||||
|
|
||||||
|
# Add <file or directory> to the black list. It should be a base name, not a
|
||||||
|
# path. You may set this option multiple times.
|
||||||
|
ignore=CVS
|
||||||
|
ignore=.hg
|
||||||
|
|
||||||
|
# Pickle collected data for later comparisons.
|
||||||
|
persistent=yes
|
||||||
|
|
||||||
|
# Set the cache size for astng objects.
|
||||||
|
cache-size=500
|
||||||
|
|
||||||
|
# List of plugins (as comma separated values of python modules names) to load,
|
||||||
|
# usually to register additional checkers.
|
||||||
|
load-plugins=
|
||||||
|
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
# Enable only checker(s) with the given id(s). This option conflicts with the
|
||||||
|
# disable-checker option
|
||||||
|
#enable-checker=
|
||||||
|
|
||||||
|
# Enable all checker(s) except those with the given id(s). This option
|
||||||
|
# conflicts with the enable-checker option
|
||||||
|
#disable-checker=
|
||||||
|
|
||||||
|
# Enable all messages in the listed categories.
|
||||||
|
#enable-msg-cat=
|
||||||
|
|
||||||
|
# Disable all messages in the listed categories.
|
||||||
|
#disable-msg-cat=
|
||||||
|
|
||||||
|
# Enable the message(s) with the given id(s).
|
||||||
|
#enable-msg=
|
||||||
|
|
||||||
|
# Disable the message(s) with the given id(s).
|
||||||
|
# W0142: Used * or ** magic -- this is in fact a very Pythonic approach.
|
||||||
|
# E1103: %s %r has no %r member (but some types could not be inferred)
|
||||||
|
# This is generally not an actual problem, and causes false positives
|
||||||
|
disable-msg=W0403, W0142, E1103
|
||||||
|
disable=W0403, W0142, E1103
|
||||||
|
|
||||||
|
|
||||||
|
[REPORTS]
|
||||||
|
|
||||||
|
# set the output format. Available formats are text, parseable, colorized, msvs
|
||||||
|
# (visual studio) and html
|
||||||
|
output-format=colorized
|
||||||
|
|
||||||
|
# Include message's id in output
|
||||||
|
include-ids=yes
|
||||||
|
|
||||||
|
# Put messages in a separate file for each module / package specified on the
|
||||||
|
# command line instead of printing them on stdout. Reports (if any) will be
|
||||||
|
# written in a file name "pylint_global.[txt|html]".
|
||||||
|
files-output=no
|
||||||
|
|
||||||
|
# Tells wether to display a full report or only the messages
|
||||||
|
reports=yes
|
||||||
|
|
||||||
|
# Python expression which should return a note less than 10 (10 is the highest
|
||||||
|
# note).You have access to the variables errors warning, statement which
|
||||||
|
# respectivly contain the number of errors / warnings messages and the total
|
||||||
|
# number of statements analyzed. This is used by the global evaluation report
|
||||||
|
# (R0004).
|
||||||
|
evaluation=10.0 - (5.0 * error + warning + refactor + convention) / statement * 10
|
||||||
|
|
||||||
|
# Add a comment according to your evaluation note. This is used by the global
|
||||||
|
# evaluation report (R0004).
|
||||||
|
comment=yes
|
||||||
|
|
||||||
|
# Enable the report(s) with the given id(s).
|
||||||
|
#enable-report=
|
||||||
|
|
||||||
|
# Disable the report(s) with the given id(s).
|
||||||
|
# R0801 is the "similar lines" report, which is not used.
|
||||||
|
disable-report=R0801
|
||||||
|
|
||||||
|
|
||||||
|
# checks for
|
||||||
|
# * unused variables / imports
|
||||||
|
# * undefined variables
|
||||||
|
# * redefinition of variable from builtins or from an outer scope
|
||||||
|
# * use of variable before assigment
|
||||||
|
#
|
||||||
|
[VARIABLES]
|
||||||
|
|
||||||
|
# Tells wether we should check for unused import in __init__ files.
|
||||||
|
init-import=no
|
||||||
|
|
||||||
|
# A regular expression matching names used for dummy variables (i.e. not used).
|
||||||
|
dummy-variables-rgx=_.*$
|
||||||
|
|
||||||
|
# List of additional names supposed to be defined in builtins. Remember that
|
||||||
|
# you should avoid to define new builtins when possible.
|
||||||
|
additional-builtins=
|
||||||
|
|
||||||
|
|
||||||
|
# try to find bugs in the code using type inference
|
||||||
|
#
|
||||||
|
[TYPECHECK]
|
||||||
|
|
||||||
|
# Tells wether missing members accessed in mixin class should be ignored. A
|
||||||
|
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||||
|
ignore-mixin-members=yes
|
||||||
|
|
||||||
|
# When zope mode is activated, consider the acquired-members option to ignore
|
||||||
|
# access to some undefined attributes.
|
||||||
|
zope=no
|
||||||
|
|
||||||
|
# List of members which are usually get through zope's acquisition mecanism and
|
||||||
|
# so shouldn't trigger E0201 when accessed (need zope=yes to be considered).
|
||||||
|
acquired-members=REQUEST,acl_users,aq_parent
|
||||||
|
|
||||||
|
|
||||||
|
# checks for :
|
||||||
|
# * doc strings
|
||||||
|
# * modules / classes / functions / methods / arguments / variables name
|
||||||
|
# * number of arguments, local variables, branchs, returns and statements in
|
||||||
|
# functions, methods
|
||||||
|
# * required module attributes
|
||||||
|
# * dangerous default values as arguments
|
||||||
|
# * redefinition of function / method / class
|
||||||
|
# * uses of the global statement
|
||||||
|
#
|
||||||
|
[BASIC]
|
||||||
|
|
||||||
|
# Required attributes for module, separated by a comma
|
||||||
|
required-attributes=__author__, __version__
|
||||||
|
|
||||||
|
# Regular expression which should only match functions or classes name which do
|
||||||
|
# not require a docstring
|
||||||
|
no-docstring-rgx=_.*$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct module names
|
||||||
|
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct module level names
|
||||||
|
const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct class names
|
||||||
|
class-rgx=[A-Z_][a-zA-Z0-9]+$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct function names
|
||||||
|
function-rgx=([A-Z_][a-zA-Z0-9_]{2,30})|(main)$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct method names
|
||||||
|
method-rgx=([a-zA-Z_][a-zA-Z0-9_]{2,30})|(test.*)$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct instance attribute names
|
||||||
|
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct argument names
|
||||||
|
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct variable names
|
||||||
|
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct list comprehension /
|
||||||
|
# generator expression variable names
|
||||||
|
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
||||||
|
|
||||||
|
# Good variable names which should always be accepted, separated by a comma
|
||||||
|
good-names=i,n
|
||||||
|
|
||||||
|
# Bad variable names which should always be refused, separated by a comma
|
||||||
|
bad-names=foo,bar
|
||||||
|
|
||||||
|
# List of builtins function names that should not be used, separated by a comma
|
||||||
|
bad-functions=filter,apply,input
|
||||||
|
|
||||||
|
|
||||||
|
# checks for
|
||||||
|
# * external modules dependencies
|
||||||
|
# * relative / wildcard imports
|
||||||
|
# * cyclic imports
|
||||||
|
# * uses of deprecated modules
|
||||||
|
#
|
||||||
|
[IMPORTS]
|
||||||
|
|
||||||
|
# Deprecated modules which should not be used, separated by a comma
|
||||||
|
deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
|
||||||
|
|
||||||
|
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||||
|
# given file (report R0402 must not be disabled)
|
||||||
|
import-graph=
|
||||||
|
|
||||||
|
# Create a graph of external dependencies in the given file (report R0402 must
|
||||||
|
# not be disabled)
|
||||||
|
ext-import-graph=
|
||||||
|
|
||||||
|
# Create a graph of internal dependencies in the given file (report R0402 must
|
||||||
|
# not be disabled)
|
||||||
|
int-import-graph=
|
||||||
|
|
||||||
|
|
||||||
|
# checks for :
|
||||||
|
# * methods without self as first argument
|
||||||
|
# * overridden methods signature
|
||||||
|
# * access only to existant members via self
|
||||||
|
# * attributes not defined in the __init__ method
|
||||||
|
# * supported interfaces implementation
|
||||||
|
# * unreachable code
|
||||||
|
#
|
||||||
|
[CLASSES]
|
||||||
|
|
||||||
|
# List of interface methods to ignore, separated by a comma. This is used for
|
||||||
|
# instance to not check methods defines in Zope's Interface base class.
|
||||||
|
ignore-iface-methods=
|
||||||
|
|
||||||
|
# List of method names used to declare (i.e. assign) instance attributes.
|
||||||
|
defining-attr-methods=__init__,__new__,setUp,run
|
||||||
|
|
||||||
|
|
||||||
|
# checks for sign of poor/misdesign:
|
||||||
|
# * number of methods, attributes, local variables...
|
||||||
|
# * size, complexity of functions, methods
|
||||||
|
#
|
||||||
|
[DESIGN]
|
||||||
|
|
||||||
|
# Maximum number of arguments for function / method
|
||||||
|
max-args=8
|
||||||
|
|
||||||
|
# Maximum number of locals for function / method body
|
||||||
|
max-locals=15
|
||||||
|
|
||||||
|
# Maximum number of return / yield for function / method body
|
||||||
|
max-returns=6
|
||||||
|
|
||||||
|
# Maximum number of branch for function / method body
|
||||||
|
max-branchs=20
|
||||||
|
|
||||||
|
# Maximum number of statements in function / method body
|
||||||
|
max-statements=40
|
||||||
|
|
||||||
|
# Maximum number of parents for a class (see R0901).
|
||||||
|
max-parents=12
|
||||||
|
|
||||||
|
# Maximum number of attributes for a class (see R0902).
|
||||||
|
max-attributes=10
|
||||||
|
|
||||||
|
# Minimum number of public methods for a class (see R0903).
|
||||||
|
min-public-methods=0
|
||||||
|
|
||||||
|
# Maximum number of public methods for a class (see R0904).
|
||||||
|
max-public-methods=20
|
||||||
|
|
||||||
|
|
||||||
|
# checks for:
|
||||||
|
# * warning notes in the code like FIXME, XXX
|
||||||
|
# * PEP 263: source code with non ascii character but no encoding declaration
|
||||||
|
#
|
||||||
|
[MISCELLANEOUS]
|
||||||
|
|
||||||
|
# List of note tags to take in consideration, separated by a comma.
|
||||||
|
notes=FIXME,XXX,TODO
|
||||||
|
|
||||||
|
|
||||||
|
# checks for :
|
||||||
|
# * unauthorized constructions
|
||||||
|
# * strict indentation
|
||||||
|
# * line length
|
||||||
|
# * use of <> instead of !=
|
||||||
|
#
|
||||||
|
[FORMAT]
|
||||||
|
|
||||||
|
# Maximum number of characters on a single line.
|
||||||
|
max-line-length=80
|
||||||
|
|
||||||
|
# Maximum number of lines in a module
|
||||||
|
max-module-lines=1000
|
||||||
|
|
||||||
|
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||||
|
# tab).
|
||||||
|
indent-string=' '
|
||||||
|
|
||||||
|
|
||||||
|
# checks for similarities and duplicated code. This computation may be
|
||||||
|
# memory / CPU intensive, so you should disable it if you experiments some
|
||||||
|
# problems.
|
||||||
|
#
|
||||||
|
[SIMILARITIES]
|
||||||
|
|
||||||
|
# Minimum lines number of a similarity.
|
||||||
|
min-similarity-lines=4
|
||||||
|
|
||||||
|
# Ignore comments when computing similarities.
|
||||||
|
ignore-comments=yes
|
||||||
|
|
||||||
|
# Ignore docstrings when computing similarities.
|
||||||
|
ignore-docstrings=yes
|
||||||
197
vloed.py
197
vloed.py
|
|
@ -5,31 +5,32 @@ Inspired by the PixelFlut beamer on eth0:winter 2016 and
|
||||||
code from https://github.com/defnull/pixelflut/
|
code from https://github.com/defnull/pixelflut/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = 0.2
|
__version__ = 0.3
|
||||||
__author__ = "Jan Klopper <jan@underdark.nl>"
|
__author__ = "Jan Klopper <jan@underdark.nl>"
|
||||||
|
|
||||||
import pygame
|
import pygame
|
||||||
from pygame.locals import *
|
from pygame import locals as pygamelocals
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
from gevent import spawn, socket, monkey
|
import socket
|
||||||
|
|
||||||
|
from gevent import spawn, monkey
|
||||||
from gevent.server import DatagramServer
|
from gevent.server import DatagramServer
|
||||||
from gevent.queue import Queue
|
from gevent.queue import Queue
|
||||||
|
|
||||||
monkey.patch_all()
|
monkey.patch_all()
|
||||||
UDP_IP = "127.0.0.1"
|
UDP_IP = "127.0.0.1"
|
||||||
UDP_PORT= 5005
|
UDP_PORT = 5005
|
||||||
|
DISCOVER_PORT = 5006
|
||||||
PROTOCOL_VERSION = 1
|
PROTOCOL_VERSION = 1
|
||||||
|
MAX_PROTOCOL_VERSION = 1
|
||||||
PROTOCOL_PREAMBLE = "pixelvloed"
|
PROTOCOL_PREAMBLE = "pixelvloed"
|
||||||
|
MAX_PIXELS = 140
|
||||||
def main():
|
|
||||||
"""Runs a pixelvloed server"""
|
|
||||||
PixelVloed(':%d' %(UDP_PORT)).serve_forever()
|
|
||||||
|
|
||||||
class Canvas(object):
|
class Canvas(object):
|
||||||
"""PixelVloed server class"""
|
"""PixelVloed display class"""
|
||||||
|
|
||||||
def __init__(self, queue, width=1366, height=768, debug=True):
|
def __init__(self, queue, width=1366, height=768, debug=False):
|
||||||
"""Init the pixelVloed server"""
|
"""Init the pixelVloed server"""
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.pixeloffset = 2
|
self.pixeloffset = 2
|
||||||
|
|
@ -42,10 +43,11 @@ class Canvas(object):
|
||||||
self.canvas()
|
self.canvas()
|
||||||
self.set_title()
|
self.set_title()
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.limit = 140
|
self.limit = MAX_PIXELS
|
||||||
self.broadcastSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
self.pixels = None
|
||||||
self.broadcastSocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
self.broadcastsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
self.broadcastSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
self.broadcastsocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
|
self.broadcastsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_title(text=None):
|
def set_title(text=None):
|
||||||
|
|
@ -59,8 +61,8 @@ class Canvas(object):
|
||||||
"""Init the pygame canvas"""
|
"""Init the pygame canvas"""
|
||||||
pygame.init()
|
pygame.init()
|
||||||
pygame.mixer.quit()
|
pygame.mixer.quit()
|
||||||
flags = DOUBLEBUF
|
self.screen = pygame.display.set_mode((self.width, self.height),
|
||||||
self.screen = pygame.display.set_mode((self.width, self.height), flags)
|
pygamelocals.DOUBLEBUF)
|
||||||
|
|
||||||
def clear(self, r=0, g=0, b=0): # pylint: disable=C0103
|
def clear(self, r=0, g=0, b=0): # pylint: disable=C0103
|
||||||
""" Fill the entire screen with a solid colour (default: black)"""
|
""" Fill the entire screen with a solid colour (default: black)"""
|
||||||
|
|
@ -115,7 +117,7 @@ class Canvas(object):
|
||||||
pixelcount = min(((len(data)-1) / pixellength),
|
pixelcount = min(((len(data)-1) / pixellength),
|
||||||
self.limit)
|
self.limit)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print '%d pixels received, protocol V %d ' % (pixelcount, protocol)
|
print '%d pixels received, protocol V %d' % (pixelcount, protocol)
|
||||||
for i in xrange(0, pixelcount):
|
for i in xrange(0, pixelcount):
|
||||||
pixel = struct.unpack_from(
|
pixel = struct.unpack_from(
|
||||||
packetformat,
|
packetformat,
|
||||||
|
|
@ -124,36 +126,173 @@ class Canvas(object):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print pixel
|
print pixel
|
||||||
self.Pixel(*pixel)
|
self.Pixel(*pixel)
|
||||||
except Exception as e:
|
except Exception as error:
|
||||||
if self.debug:
|
if self.debug:
|
||||||
# All exceptions will be printed, but won't result in a crash.
|
# All exceptions will be printed, but won't result in a crash.
|
||||||
print e
|
print error
|
||||||
# indicate that we have been drawing stuff
|
# indicate that we have been drawing stuff
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def SendDiscoveryPacket(self):
|
def SendDiscoveryPacket(self):
|
||||||
"""Lets send out our ip/port/resolution to any listening clients"""
|
"""Lets send out our ip/port/resolution to any listening clients"""
|
||||||
self.broadcastSocket.sendto(
|
self.broadcastsocket.sendto(
|
||||||
'%s:%f %s:%d %d*%d' % (PROTOCOL_PREAMBLE, PROTOCOL_VERSION,
|
'%s:%f %s:%d %d*%d' % (PROTOCOL_PREAMBLE, PROTOCOL_VERSION,
|
||||||
UDP_IP, UDP_PORT,
|
UDP_IP, UDP_PORT,
|
||||||
self.width, self.height),
|
self.width, self.height),
|
||||||
('<broadcast>', UDP_PORT+1))
|
('<broadcast>', DISCOVER_PORT))
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print 'sending discovery packet'
|
print 'sending discovery packet'
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"""Clean up any sockets we created"""
|
"""Clean up any sockets we created"""
|
||||||
self.broadcastSocket.close()
|
self.broadcastsocket.close()
|
||||||
|
|
||||||
class PixelVloed(DatagramServer):
|
class PixelVloedServer(DatagramServer):
|
||||||
"""PixelVloed server class"""
|
"""PixelVloed server class"""
|
||||||
queue = Queue()
|
|
||||||
pixelcanvas = Canvas(queue)
|
|
||||||
__request_processing_greenlet = spawn(pixelcanvas.CanvasUpdate)
|
|
||||||
|
|
||||||
def handle(self, data, address):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Is called by the DataGramServer whenever a package is received"""
|
"""Set up some vars for this instance"""
|
||||||
|
self.queue = Queue()
|
||||||
|
pixelcanvas = Canvas(self.queue)
|
||||||
|
__request_processing_greenlet = spawn(pixelcanvas.CanvasUpdate)
|
||||||
|
DatagramServer.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def handle(self, data, _address):
|
||||||
|
"""Is called by the DataGramServer whenever an udp package is received"""
|
||||||
self.queue.put(data)
|
self.queue.put(data)
|
||||||
|
|
||||||
|
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
|
||||||
|
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=True):
|
||||||
|
"""Sends the message to the udp server
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
message: (str, 140)
|
||||||
|
sleep: (bool) True, should the client sleep for a while?
|
||||||
|
"""
|
||||||
|
self.sock.sendto(message, (self.ipaddress, self.port))
|
||||||
|
if sleep:
|
||||||
|
self.Sleep()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def DiscoverServers(returnfirst=False, timeout=5):
|
||||||
|
"""Discover servers that send out the pixelfvloed 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:
|
||||||
|
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"""
|
||||||
|
super(MaxSizeList, self).append(item)
|
||||||
|
if self.__len__() == self.maxsize:
|
||||||
|
raise IndexError('max size reached')
|
||||||
|
|
||||||
|
def RunServer():
|
||||||
|
"""Runs a pixelvloed server"""
|
||||||
|
PixelVloedServer('%s:%d' %(UDP_IP, UDP_PORT)).serve_forever()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
RunServer()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue