I know some effort was made to make the Python resources compatible with Python3, but it wasn't complete.
The remaining incompatibility is that in Python2, socket.send()
takes a string argument, whereas in Python3, socket.send()
expects a bytes object. I made some small changes to the OPC libraries to make them work with Python3.
In opc.py
, the problem is in put_pixels()
. The current version uses chr()
to build a list of Unicode string representations of RGB values, then concatenates them with join()
. I changed this by instead building a list of integers, then concatenating them with bytes()
. Here's the new code (the rest of the function is unchanged):
# build OPC message
len_hi_byte = int(len(pixels)*3 / 256)
len_lo_byte = (len(pixels)*3) % 256
header = [channel, 0, len_hi_byte, len_lo_byte]
pixels = [x for rgb in pixels for x in rgb] # flatten into 1D list of rgb values
clamp = lambda x: max(min(255, int(x)), 0)
rgbvals = [clamp(x) for x in pixels] # floats to ints, and constrain vals
message = bytes(header + rgbvals)
In fastopc.py
, the problem is in putPixels()
. Here things are less straightforward because the function accepts various types of pixel sources. I changed it to accept either a list of (R,G,B) 3-tuples, or a flat NumPy array of RGB values. I did this partly to enable the same data structure for pixels used by opc.py
, partly because it didn't seem to make sense to send the values as strings if we need to convert them to bytes objects, and partly just because I'm a little out of my depth. The new code:
rgbvals = []
numvals = 0
for source in sources:
if isinstance(source, list):
source = [x for rgb in source for x in rgb] # flatten the list
elif isinstance(source, numpy.ndarray):
source = source.tolist() # convert (assumed flat) NumPy array to a list
clamp = lambda x: max(min(255, int(x)), 0)
source = [clamp(x) for x in source]
rgbvals.extend(source)
numvals += len(source)
header = struct.pack('>BBH', channel, 0, numvals) # returns a bytes object
message = header + bytes(rgbvals)
self.send(message)
I verified that the new libraries work with examples both from Fadecandy and Open Pixel Control. Some caveats:
- I didn't explicity mention changing the shebangs to
#!/usr/bin/env python3
.
- Although these changes make the libraries compatible with Python3, they render them incompatible with Python2. I'm sure that something snazzy could be done to automagically determine which version of the put pixel function to use, but I didn't write that.
- While the modified
fastopc.py
works just fine, I'm not at all certain it's as fast as it could be, and performance is after all the reason it exists in the first place.
fastopc.py
also contains a second function that sends data over sockets: sysEx()
. It looks okay as is to me, because it generates a message from struct.pack()
, the Python3 version of which returns a bytes object instead of a string. But someone who knows more than me should take a look at it.