def run(self): global errFlag, frames errFlag = False while not globals.exitFlag: # mind you, this is the implementation coming straight from the firmware ;) # CHECKING FOR CONTROL MESSAGES if not globals.evt.is_set(): globals.cxn1.write(globals.evtDefinition['message']) try: # if a delay has been set, sleep for that length of time sleep(globals.evtDefinition['delay']) globals.evt.set() except: # otherwise, do not resume until an external event happens which resets the event flag globals.evt.wait() # STOP RUNNING SERIAL COMMANDS ON AN ERROR (TODO: make this more elegant) if errFlag: continue # WRITING THE MESSAGE ON THE BOARD for frame in frames: # iterate through each frame of the "text animation" try: # write the frame to the marquee # TODO: This had to change recently # Join the chars in frame ahead of time to optimize this globals.cxn1.write(''.join( [chr(ord(char)) for char in frame])) except Exception as e: # something unexpected happened console.cwrite("Serial communication disconnected") errFlag = True break # Now just sleep for a wee bit before we go to the next character column sleep(globals.delay) # MESSAGE EXPIRATION console.cwrite("Updating messages...\n") updateMessages()
def analyzeImage(path): im = Image.open(path) results = { 'size': im.size, 'mode': 'full', 'framecount': 0, } try: #while True: print "Tile?", im.tile if im.tile: results['framecount'] += 1 tile = im.tile[0] update_region = tile[1] update_region_dimensions = update_region[2:] if update_region_dimensions != im.size: results['mode'] = 'partial' return results #im.seek(im.tell() + 1) return results except EOFError: console.cwrite("Unexpected end of file in your GIF. Your image does not conform to GIF 87a or 89a, or could be corrupt.") return except: console.cwrite("Unhandled exception while parsing GIF animation.") return
def analyzeImage(path): im = Image.open(path) results = { 'size': im.size, 'mode': 'full', 'framecount': 0, } try: #while True: print "Tile?", im.tile if im.tile: results['framecount'] += 1 tile = im.tile[0] update_region = tile[1] update_region_dimensions = update_region[2:] if update_region_dimensions != im.size: results['mode'] = 'partial' return results #im.seek(im.tell() + 1) return results except EOFError: console.cwrite( "Unexpected end of file in your GIF. Your image does not conform to GIF 87a or 89a, or could be corrupt." ) return except: console.cwrite("Unhandled exception while parsing GIF animation.") return
def run(self): global errFlag, frames errFlag = False while not globals.exitFlag: # mind you, this is the implementation coming straight from the firmware ;) # CHECKING FOR CONTROL MESSAGES if not globals.evt.is_set(): globals.cxn1.write(globals.evtDefinition['message']) try: # if a delay has been set, sleep for that length of time sleep(globals.evtDefinition['delay']) globals.evt.set() except: # otherwise, do not resume until an external event happens which resets the event flag globals.evt.wait() # STOP RUNNING SERIAL COMMANDS ON AN ERROR (TODO: make this more elegant) if errFlag: continue # WRITING THE MESSAGE ON THE BOARD for frame in frames: # iterate through each frame of the "text animation" try: # write the frame to the marquee # TODO: This had to change recently # Join the chars in frame ahead of time to optimize this globals.cxn1.write(''.join([chr(ord(char)) for char in frame])) except Exception as e: # something unexpected happened console.cwrite("Serial communication disconnected") errFlag = True break # Now just sleep for a wee bit before we go to the next character column sleep(globals.delay) # MESSAGE EXPIRATION console.cwrite("Updating messages...\n") updateMessages()
def makeMessageString(message): # TODO FIXME: Rows needs to be dynamic based on number of open COMs rows = 1 # Initialize subMessage & serialMsg as empty arrays subMessage = len(globals.panelColors) * [''] serialMsg = len(globals.panelColors) * [None] # Find out which color we need to make the message in tree = ET.ElementTree(ET.fromstring(message)) body = tree.find("body") # Iterate through each element in the html <body> tag for element in body.iter(): # Parse out the <span style=' color=#{this is what we want};'> segmentColor = re.search(r'color\:\#([0-9a-fA-F]{6})', element.attrib['style'], flags=re.S) if segmentColor is None: continue # the element was the <body> tag itself segmentColor = int(segmentColor.group(1), 16) # find out if the font color in the rich text box matches the deque whose color we're making now # Default red is 0xFF0000, default yellow is 0x808000, and default green is 0x008000. Default blue would probably be 0x0000FF. # The point is the high bit is always set when the text is a default color. # The lower bits are only set if a special arrangement is desired -- e.g. 0x1F7C00 would be bottom 2 rows red, top 2 rows green, and middle rows yellow for i in range(0, len(globals.panelColors)): panelColor = globals.panelColors[i] if panelColor & segmentColor == panelColor: # "rich text" color is set to contain this panel's color completely (i.e. no special pattern) subMessage[i] += element.text else: #if (panelColor & segmentColor == 0): # "rich text" color is not set to contain this panel's color at all subMessage[i] += " " * (element.text.__len__()) #else: # this color is set for specific rows, not the whole matrix # pass # Find out how many rows exist in the selected font & size rows = 1 # TODO FIXME: globals.font['LEDgoes Default Font']['size'][7]['weight'][4]['spacing'] It's not spacing, there should be a separate "height" # Make the message string from the letters, iterating over all colors for colorIdx in range(0, len(subMessage)): serialMsg[colorIdx] = [deque() for i in range(0, rows)] # Make that many rows of serialMsg for d in serialMsg[colorIdx]: # TODO: Allow an optional amount of padding before the message is shown d.extend(["\x00", "\x00", "\x00", "\x00", "\x00"]) # add padding to the left of the message for character in subMessage[colorIdx]: # Map the current letter to something that appears in the font for r in range(0, rows): try: # Add the prescribed row of the prescribed character serialMsg[colorIdx][r].extend( globals.font['LEDgoes Default Font']['size'][7]['weight'][4][character][r] ) except KeyError: serialMsg[colorIdx][r].extend(["\x00", "\x00", "\x00", "\x00", "\x00"]) console.cwrite("Character " + character + " unknown; substituting with whitespace") # Insert spacing between characters for r in range(0, rows): try: # Add the prescribed spacing between characters for this font serialMsg[colorIdx][r].extend(['\x00'] * globals.font['LEDgoes Default Font']['size'][7]['weight'][4]['spacing']) except: # Tell the user there was a problem console.cwrite("Character spacing is unknown for this font; assuming 0") return serialMsg
def __init__(self): # Carry out the rest of the "serial welcome" here by posting initial messages: default is "Awaiting Messages" updateMessages() # Start the thread super(serialThread, self).__init__() console.cwrite( "IO Thread is now running; when it stops may not necessarily be advertised..." )
def run(self): global desiredIndex while self.active: # The user can toggle if this RSS feed is actively being read sleep(1) if not self.evt.is_set(): # Get the feed, and massage it so we can parse it # TODO: use the user's desired quotes console.cwrite("Stock quote thread: Fetching quotes...") d = "dummy" try: d = feedparser.parse(self.feedURL) except: console.cwrite( "There was an error in fetching the requested RSS document." ) self.active = False continue info = [] feed = "<body>%s</body>" % d.entries[ 0].summary_detail.value.replace(" ", "") tree = ET.ElementTree(ET.fromstring(feed)) root = tree.getroot() # Find the last updated time last = root.find(".//table[@width='180']/tr/td") info.append("%s%s %s" % ((setColor % yellow), last.text.strip(), endColor)) # Find all the quotes counter = 0 for elem in root.findall(".//table[@width='200']"): for elem2 in elem.findall(".//td"): for text in elem2.itertext(): idx = counter % 13 if idx == 0: # Ticker symbol info.append( "%s%s " % ((setColor % yellow), text.strip())) if idx == 3: # Last trade info.append("%s %s" % (text.strip(), endColor)) if idx == 5: # Change sign sign = text.strip() info.append( "%s%s" % ((setColor % (green if sign == "+" else red)), sign)) if idx == 6: # Change amount info.append("%s %s" % (text.strip(), endColor)) counter += 1 # We're done parsing, so join everything together newMessage = globals.html % ''.join(info) desiredIndex = globals.pushMsg("RSS-stocks", "This is the stock feed", newMessage, True, desiredIndex) self.evt.set() # After the while loop terminates, remove references to the event globals.asyncEvts.remove(self.evt)
def twitterStream(properties): global twitterThread try: if twitterThread.isAlive(): console.cwrite("Shutting down Twitter stream...") twitterEvt.clear() return False _twitterStart(properties) return True except: _twitterStart(properties) return True
def run(self): global desiredIndex while self.active: # The user can toggle if this RSS feed is actively being read sleep(1) if not self.evt.is_set(): # Get the feed, and massage it so we can parse it # TODO: use the user's desired quotes console.cwrite("Stock quote thread: Fetching quotes...") d = "dummy" try: d = feedparser.parse(self.feedURL) except: console.cwrite("There was an error in fetching the requested RSS document.") self.active = False continue info = [] feed = "<body>%s</body>" % d.entries[0].summary_detail.value.replace(" ", "") tree = ET.ElementTree(ET.fromstring(feed)) root = tree.getroot() # Find the last updated time last = root.find(".//table[@width='180']/tr/td") info.append("%s%s %s" % ((setColor % yellow), last.text.strip(), endColor)) # Find all the quotes counter = 0 for elem in root.findall(".//table[@width='200']"): for elem2 in elem.findall(".//td"): for text in elem2.itertext(): idx = counter % 13 if idx == 0: # Ticker symbol info.append("%s%s " % ((setColor % yellow), text.strip())) if idx == 3: # Last trade info.append("%s %s" % (text.strip(), endColor)) if idx == 5: # Change sign sign = text.strip() info.append("%s%s" % ((setColor % (green if sign == "+" else red)), sign)) if idx == 6: # Change amount info.append("%s %s" % (text.strip(), endColor)) counter += 1 # We're done parsing, so join everything together newMessage = globals.html % ''.join(info) desiredIndex = globals.pushMsg("RSS-stocks", "This is the stock feed", newMessage, True, desiredIndex) self.evt.set() # After the while loop terminates, remove references to the event globals.asyncEvts.remove(self.evt)
def __init__(self, tickerString): global desiredIndex # Find out if there is enough space in the message list to start this thread # Start the thread; call it active, and register the event super(stockThread, self).__init__() self.active = True self.evt = threading.Event() self.evt.set() globals.asyncEvts.append(self.evt) # TODO: Add an item to the main UI list to show that this is taking place # FIXME: For now, this will be Message #1 stocks = re.split(" |,", tickerString) stocks = filter(None, stocks) if len(stocks) < 1: console.cwrite("You did not enter any valid ticker symbols.") return; self.feedURL = 'http://www.nasdaq.com/aspxcontent/NasdaqRSS.aspx?data=quotes&symbol=%s' % ("&symbol=".join(stocks)) desiredIndex = globals.pushMsg("RSS-stocks", "This is the stock feed", globals.html % "Awaiting stock data...", True, desiredIndex) console.cwrite("Stock quote thread is now running...")
def toggleQuotes(windowInstance): global stockInstance tickerString = windowInstance.ui.txtQuotes.toPlainText() if len(tickerString) == 0: console.cwrite("You must enter at least one ticker symbol in order to use this feature.") return # For now, treat this as a static class (one instance) # TODO: Make a mechanism to handle toggling multiple class instances # Initialize a new instance of the Nasdaq RSS feed if stockInstance is not None: stockInstance.active = False stockInstance.join() stockInstance = None console.cwrite("Stock quote thread terminated.") windowInstance.ui.btnGetQuotes.setText("Get Quotes") else: stockInstance = stockThread(tickerString) stockInstance.setDaemon(True) stockInstance.start() windowInstance.ui.btnGetQuotes.setText("Stop Quotes")
def updateMessages(): global frames # NOTE: This is where we used to print messages to the console. If we want to do this again, put it here serialMsgRed = [ ] # array holds deques containing red pixel data for all messages serialMsgGreen = [ ] # array holds deques containing green pixel data for all messages # Ask any asynchronous calls to update their messages for evt in globals.asyncEvts: evt.clear() for evt in globals.asyncEvts: if not evt.wait(15): console.cwrite( "Timed out after waiting 15 seconds for a data provider before refreshing scrolling." ) for msg in globals.richMsgs: # Fill the new data structures with pixel data [msgRed, msgGreen] = makeMessageString(msg) zipMessageString(msgRed, serialMsgRed) zipMessageString(msgGreen, serialMsgGreen) #zipMessageString(makeMessageStringFromFont(msg, "red"), serialMsgRed) #zipMessageString(makeMessageStringFromFont(msg, "green"), serialMsgGreen) # Make the frames frames.clear() for f in range(0, len(serialMsgRed[0])): frame = deque() # each board has an x & y assigned by the "marquee arrangement" UI; sort the boards by y first then x # this will help ensure a nice, smooth scrolling effect (assuming we aren't scrolling top-down) sortedBoards = sorted(globals.boards, key=itemgetter(0, 1)) for b in sortedBoards: # b[0] := x with respect to the marquee; b[1] = y; b[2] = board index # b[0] + f := x with respect to the whole message # add the board address of the red chip and its data to the serial message x = b[0] * 5 frame.extend(chr(int(b[2] + 128))) frame.extend(list(serialMsgRed[b[1]])[x + f:x + f + 5]) # add the board address of the green chip and its data to the serial message frame.extend(chr(int(b[2] + 192))) frame.extend(list(serialMsgGreen[b[1]])[x + f:x + f + 5]) # don't forget to add the completed work to the deque of frames :-P frames.append(frame)
def __init__(self, tickerString): global desiredIndex # Find out if there is enough space in the message list to start this thread # Start the thread; call it active, and register the event super(stockThread, self).__init__() self.active = True self.evt = threading.Event() self.evt.set() globals.asyncEvts.append(self.evt) # TODO: Add an item to the main UI list to show that this is taking place # FIXME: For now, this will be Message #1 stocks = re.split(" |,", tickerString) stocks = filter(None, stocks) if len(stocks) < 1: console.cwrite("You did not enter any valid ticker symbols.") return self.feedURL = 'http://www.nasdaq.com/aspxcontent/NasdaqRSS.aspx?data=quotes&symbol=%s' % ( "&symbol=".join(stocks)) desiredIndex = globals.pushMsg("RSS-stocks", "This is the stock feed", globals.html % "Awaiting stock data...", True, desiredIndex) console.cwrite("Stock quote thread is now running...")
def toggleQuotes(windowInstance): global stockInstance tickerString = windowInstance.ui.txtQuotes.toPlainText() if len(tickerString) == 0: console.cwrite( "You must enter at least one ticker symbol in order to use this feature." ) return # For now, treat this as a static class (one instance) # TODO: Make a mechanism to handle toggling multiple class instances # Initialize a new instance of the Nasdaq RSS feed if stockInstance is not None: stockInstance.active = False stockInstance.join() stockInstance = None console.cwrite("Stock quote thread terminated.") windowInstance.ui.btnGetQuotes.setText("Get Quotes") else: stockInstance = stockThread(tickerString) stockInstance.setDaemon(True) stockInstance.start() windowInstance.ui.btnGetQuotes.setText("Stop Quotes")
def zipMessageString(new, messages): # handle the special case if messages is empty if len(messages) == 0: for row in range(0, len(new)): messages.append(new[row]) return # we did that because the first message would always look weird if higher rows always get shifted # here's the usual case: for row in range(0, len(new)): # for each row in the "new" messages, try: # extend the existing messages' row with the data from the new message messages[row].extend(new[row]) except IndexError: # the row doesn't exist yet in the existing messages, so make it. try: # Pad it with \x00s so the letters will remain coherent if we have taller fonts than last time # Chances are that row 0 (the bottom) has been made already messages.append(deque(['\x00'] * len(messages[0]))) messages.append(new[row]) except IndexError: console.cwrite("There was a problem extending the message contents.")
def pushMsg(source, rawMessage, richMessage, isSticky, desiredIndex=None): global richMsgs, msgLimit, msgToOverwrite, uiMsgList console.cwrite("Attempting to add message: " + rawMessage) console.cwrite(richMessage) # Count how many items exist in the list now msgList = uiMsgList.findItems('.*', Qt.MatchRegExp) if desiredIndex is None: if len(msgList) >= msgLimit: # We are at or above our message limit; find one to overwrite written = False # Keep track of if we've successfully overwritten a message # Start with the message at index "msgToOverwrite", go to the end of the list, then restart at the beginning msgIndexes = range(msgToOverwrite, msgLimit) + range(0, msgToOverwrite) for index in msgIndexes: if not msgList[index].sticky: # Do not overwrite sticky messages formattedSource = "[%s]" % source formattedSource = formattedSource + " sticky" if isSticky else formattedSource msgList[index].setText("%s %s" % (formattedSource, rawMessage)) msgList[index].formattedText = richMessage msgList[index].sticky = isSticky msgToOverwrite = index + 1 written = True break if not written: raise Exception("Could not overwrite any messages; they are all sticky") else: # We have not exceeded the maximum message count; add the message to the UI list RawTextItem(richMessage, source, isSticky, uiMsgList) else: # This is the same code as before formattedSource = "[%s]" % source formattedSource = formattedSource + " sticky" if isSticky else formattedSource msgList[desiredIndex].setText("%s %s" % (formattedSource, rawMessage)) msgList[desiredIndex].formattedText = richMessage msgList[desiredIndex].sticky = isSticky # Reconstruct the global list by pulling out "formattedText" from the UI list objects richMsgs = [x.formattedText for x in uiMsgList.findItems('.*', Qt.MatchRegExp)] return len(uiMsgList.findItems('.*', Qt.MatchRegExp)) - 1
def zipMessageString(new, messages): # handle the special case if messages is empty if len(messages) == 0: for row in range(0, len(new)): messages.append(new[row]) return # we did that because the first message would always look weird if higher rows always get shifted # here's the usual case: for row in range(0, len(new)): # for each row in the "new" messages, try: # extend the existing messages' row with the data from the new message messages[row].extend(new[row]) except IndexError: # the row doesn't exist yet in the existing messages, so make it. try: # Pad it with \x00s so the letters will remain coherent if we have taller fonts than last time # Chances are that row 0 (the bottom) has been made already messages.append(deque(['\x00'] * len(messages[0]))) messages.append(new[row]) except IndexError: console.cwrite( "There was a problem extending the message contents.")
def analyzeImage(path): im = Image.open(path) results = {"size": im.size, "mode": "full", "framecount": 0} try: while True: if im.tile: results["framecount"] += 1 tile = im.tile[0] update_region = tile[1] update_region_dimensions = update_region[2:] if update_region_dimensions != im.size: results["mode"] = "partial" return results im.seek(im.tell() + 1) return results except EOFError: console.cwrite( "Unexpected end of file in your GIF. Your image does not conform to GIF 87a or 89a, or could be corrupt." ) return except: console.cwrite("Unhandled exception while parsing GIF animation.") return
def updateMessages(): global frames # NOTE: This is where we used to print messages to the console. If we want to do this again, put it here serialMsgRed = [] # array holds deques containing red pixel data for all messages serialMsgGreen = [] # array holds deques containing green pixel data for all messages # Ask any asynchronous calls to update their messages for evt in globals.asyncEvts: evt.clear() for evt in globals.asyncEvts: if not evt.wait(15): console.cwrite("Timed out after waiting 15 seconds for a data provider before refreshing scrolling.") for msg in globals.richMsgs: # Fill the new data structures with pixel data [msgRed, msgGreen] = makeMessageString(msg) zipMessageString(msgRed, serialMsgRed) zipMessageString(msgGreen, serialMsgGreen) #zipMessageString(makeMessageStringFromFont(msg, "red"), serialMsgRed) #zipMessageString(makeMessageStringFromFont(msg, "green"), serialMsgGreen) # Make the frames frames.clear() for f in range(0, len(serialMsgRed[0])): frame = deque() # each board has an x & y assigned by the "marquee arrangement" UI; sort the boards by y first then x # this will help ensure a nice, smooth scrolling effect (assuming we aren't scrolling top-down) sortedBoards = sorted(globals.boards, key=itemgetter(0, 1)) for b in sortedBoards: # b[0] := x with respect to the marquee; b[1] = y; b[2] = board index # b[0] + f := x with respect to the whole message # add the board address of the red chip and its data to the serial message x = b[0] * 5 frame.extend( chr(int(b[2] + 128)) ) frame.extend(list( serialMsgRed[b[1]] )[ x + f : x + f + 5 ] ) # add the board address of the green chip and its data to the serial message frame.extend( chr(int(b[2] + 192)) ) frame.extend(list( serialMsgGreen[b[1]] )[ x + f : x + f + 5 ]) # don't forget to add the completed work to the deque of frames :-P frames.append(frame)
def run(self): currentCel = 0 redChannel = globals.animTuple[0] try: greenChannel = globals.animTuple[1] except: greenChannel = redChannel celWidth = globals.animTuple[2] maxCel = len(redChannel) / celWidth # Find out a little bit more about the frames & bands present console.cwrite("Num bands: %d" % len(globals.animTuple)) totCols = len( redChannel ) # The deque stores each frame continuously along the x axis console.cwrite("Total # of columns: %d" % totCols) boardRows = len( redChannel[0]) # Number of rows of boards in the animation console.cwrite("Height in boards: %d" % boardRows) while not globals.exitFlag: # mind you, this is the implementation coming straight from the firmware ;) # CHECKING FOR CONTROL MESSAGES if not globals.evt.is_set(): globals.cxn1.write(globals.evtDefinition['message']) try: # if a delay has been set, sleep for that length of time sleep(globals.evtDefinition['delay']) globals.evt.set() except: # otherwise, do not resume until an external event happens which resets the event flag globals.evt.wait() # WRITING THE MESSAGE ON THE BOARD #print "Frame", currentCel, " Boards:", celOffset = currentCel * celWidth for board in globals.boards: #print board[2], rPart = chr(board[2] | 0x80) gPart = chr(board[2] | 0xC0) boardLeft = board[0] * 5 myRange = range(boardLeft, boardLeft + 5) row = board[1] for col in myRange: try: rPart += chr(redChannel[celOffset + col][row]) except IndexError: rPart += '\x00' try: gPart += chr(greenChannel[celOffset + col][row]) except IndexError: gPart += '\x00' #print "rPart", repr(rPart), #print "gPart", repr(gPart) globals.cxn1.write(rPart) globals.cxn1.write(gPart) #print " " # GOING TO THE NEXT FRAME currentCel = (currentCel + 1) % maxCel sleep(globals.delay)
def run(self): currentCel = 0; redChannel = globals.animTuple[0] try: greenChannel = globals.animTuple[1] except: greenChannel = redChannel celWidth = globals.animTuple[2] maxCel = len(redChannel) / celWidth # Find out a little bit more about the frames & bands present console.cwrite("Num bands: %d" % len(globals.animTuple)) totCols = len(redChannel) # The deque stores each frame continuously along the x axis console.cwrite("Total # of columns: %d" % totCols) boardRows = len(redChannel[0]) # Number of rows of boards in the animation console.cwrite("Height in boards: %d" % boardRows) while not globals.exitFlag: # mind you, this is the implementation coming straight from the firmware ;) # CHECKING FOR CONTROL MESSAGES if not globals.evt.is_set(): globals.cxn1.write(globals.evtDefinition['message']) try: # if a delay has been set, sleep for that length of time sleep(globals.evtDefinition['delay']) globals.evt.set() except: # otherwise, do not resume until an external event happens which resets the event flag globals.evt.wait() # WRITING THE MESSAGE ON THE BOARD #print "Frame", currentCel, " Boards:", celOffset = currentCel * celWidth for board in globals.boards: #print board[2], rPart = chr(board[2] | 0x80) gPart = chr(board[2] | 0xC0) boardLeft = board[0] * 5 myRange = range(boardLeft, boardLeft + 5) row = board[1] for col in myRange: try: rPart += chr(redChannel[celOffset + col][row]) except IndexError: rPart += '\x00' try: gPart += chr(greenChannel[celOffset + col][row]) except IndexError: gPart += '\x00' #print "rPart", repr(rPart), #print "gPart", repr(gPart) globals.cxn1.write(rPart) globals.cxn1.write(gPart) #print " " # GOING TO THE NEXT FRAME currentCel = (currentCel + 1) % maxCel sleep(globals.delay)
def run(self): global twitterApi, twitterProperties, twitterEvt maxTweets = 10 curIndex = 0 for item in twitterApi.GetStreamFilter(track=[twitterProperties]): if item.has_key('text'): try: # Output all Tweets including language console.cwrite("===== %s " % item['lang']) console.cwrite(item['text']) # Display only English tweets (change as you see fit) if item['lang'] == "en": rawTweetText = '@%s %s' % (item['user']['screen_name'], item['text']) formattedTweetText = '<span style=" color:#ff0000;">@%s </span><span style=" color:#808000;">%s</span>' % (item['user']['screen_name'], item['text']) globals.pushMsg("twitter", rawTweetText, globals.html % formattedTweetText, False) except Exception as ex: console.cwrite(ex) if not twitterEvt.is_set(): console.cwrite("Twitter stream shut down!") twitterEvt.set() return
def makeMessageString(message): # TODO FIXME: Rows needs to be dynamic based on number of open COMs rows = 1 # Initialize subMessage & serialMsg as empty arrays subMessage = len(globals.panelColors) * [''] serialMsg = len(globals.panelColors) * [None] # Find out which color we need to make the message in tree = ET.ElementTree(ET.fromstring(message)) body = tree.find("body") # Iterate through each element in the html <body> tag for element in body.iter(): # Parse out the <span style=' color=#{this is what we want};'> segmentColor = re.search(r'color\:\#([0-9a-fA-F]{6})', element.attrib['style'], flags=re.S) if segmentColor is None: continue # the element was the <body> tag itself segmentColor = int(segmentColor.group(1), 16) # find out if the font color in the rich text box matches the deque whose color we're making now # Default red is 0xFF0000, default yellow is 0x808000, and default green is 0x008000. Default blue would probably be 0x0000FF. # The point is the high bit is always set when the text is a default color. # The lower bits are only set if a special arrangement is desired -- e.g. 0x1F7C00 would be bottom 2 rows red, top 2 rows green, and middle rows yellow for i in range(0, len(globals.panelColors)): panelColor = globals.panelColors[i] if panelColor & segmentColor == panelColor: # "rich text" color is set to contain this panel's color completely (i.e. no special pattern) subMessage[i] += element.text else: #if (panelColor & segmentColor == 0): # "rich text" color is not set to contain this panel's color at all subMessage[i] += " " * (element.text.__len__()) #else: # this color is set for specific rows, not the whole matrix # pass # Find out how many rows exist in the selected font & size rows = 1 # TODO FIXME: globals.font['LEDgoes Default Font']['size'][7]['weight'][4]['spacing'] It's not spacing, there should be a separate "height" # Make the message string from the letters, iterating over all colors for colorIdx in range(0, len(subMessage)): serialMsg[colorIdx] = [deque() for i in range(0, rows)] # Make that many rows of serialMsg for d in serialMsg[colorIdx]: # TODO: Allow an optional amount of padding before the message is shown d.extend(["\x00", "\x00", "\x00", "\x00", "\x00"]) # add padding to the left of the message for character in subMessage[colorIdx]: # Map the current letter to something that appears in the font for r in range(0, rows): try: # Add the prescribed row of the prescribed character serialMsg[colorIdx][r].extend( globals.font['LEDgoes Default Font']['size'][7] ['weight'][4][character][r]) except KeyError: serialMsg[colorIdx][r].extend( ["\x00", "\x00", "\x00", "\x00", "\x00"]) console.cwrite("Character " + character + " unknown; substituting with whitespace") # Insert spacing between characters for r in range(0, rows): try: # Add the prescribed spacing between characters for this font serialMsg[colorIdx][r].extend( ['\x00'] * globals.font['LEDgoes Default Font']['size'] [7]['weight'][4]['spacing']) except: # Tell the user there was a problem console.cwrite( "Character spacing is unknown for this font; assuming 0" ) return serialMsg
def processImage(path, results): console.cwrite("Incoming results: %s" % results) size = results['size'] mode = results['mode'] # Open the image im = Image.open(path) # Convert to RGBA so we can do mathematical processing easier last_frame = im.convert('RGBA') # Iterate through the entire gif redFrames = deque() # collective red data greenFrames = deque() # collective green data # Each "frame" will have a series of rows, and each row contains the data of 0-127 or 0-255 try: while 1: console.cwrite("Analyzing frame %d" % im.tell()) # make a couple new frames # size[0] = width, size[1] = height rowsOfBoards = int( math.ceil(1.0 * results['size'][1] / globals.rowPixels)) redFrame = [[0 for i in range(rowsOfBoards)] for j in range(results['size'][0])] greenFrame = [[0 for i in range(rowsOfBoards)] for j in range(results['size'][0])] # Apply the global palette to the frame, if necessary if not im.getpalette(): im.putpalette(p) # Store this frame in memory new_frame = Image.new('RGBA', im.size) # Apply the current "partial" frame to the last frame if this is a partial-mode GIF if mode == 'partial': new_frame.paste(last_frame) # Paste the current frame of the GIF onto the blank canvas new_frame.paste(im, (0, 0), im.convert('RGBA')) # Run analysis on each band of each pixel to decide if it should be illuminated bands = Image.eval(new_frame, doThreshold) # This makes a list of tuples containing the results: 4 channels, 1 if illuminated, 0 if not colorPresent = list(bands.getdata()) board = 0 # which row of boards we're concerned with yOnBoard = 0 # which row we're accessing w.r.t. the current board # along the x axis of the image... for x in range(0, new_frame.size[0]): # Reset variables yOnBoard = 0 board = 0 # Add the image data to the redRow & greenRow deques from along the y axis #print redFrame, greenFrame for y in range(new_frame.size[1] - 1, -1, -1): pix = (y * new_frame.size[0]) + x # Red band if colorPresent[pix][0] == 1: redFrame[x][board] |= (1 << yOnBoard) # Green band if colorPresent[pix][1] == 1: greenFrame[x][board] |= (1 << yOnBoard) # If we cared about the blue band, we would call on colorPresent[pix][2] yOnBoard += 1 if yOnBoard >= globals.rowPixels: yOnBoard = 0 board += 1 # Add the converted frames to the respective deques redFrames.extend(redFrame) greenFrames.extend(greenFrame) #print redFrame, greenFrame # Go on to the next frame last_frame = new_frame im.seek(im.tell() + 1) except EOFError: # return a tuple containing the deques of redFrames & greenFrames, plus the GIF size console.cwrite("Final length: %d * %d" % (len(redFrames), len(greenFrames))) return (redFrames, greenFrames, size[0], size[1])
def _twitterStart(properties): global twitterThread, twitterProperties console.cwrite("Starting Twitter stream with properties %s" % properties) twitterProperties = properties twitterThread = twitterStreamThread() twitterThread.start()
def __init__(self): super(animationThread, self).__init__() console.cwrite( "Animation thread is now running; when it stops may not necessarily be advertised..." )
def processImage(path, results): console.cwrite("Incoming results: %s" % results) size = results['size'] mode = results['mode'] # Open the image im = Image.open(path) # Convert to RGBA so we can do mathematical processing easier last_frame = im.convert('RGBA') # Iterate through the entire gif redFrames = deque() # collective red data greenFrames = deque() # collective green data # Each "frame" will have a series of rows, and each row contains the data of 0-127 or 0-255 try: while 1: console.cwrite("Analyzing frame %d" % im.tell()) # make a couple new frames # size[0] = width, size[1] = height rowsOfBoards = int(math.ceil(1.0 * results['size'][1] / globals.rowPixels)) redFrame = [ [ 0 for i in range(rowsOfBoards) ] for j in range(results['size'][0]) ] greenFrame = [ [ 0 for i in range(rowsOfBoards) ] for j in range(results['size'][0]) ] # Apply the global palette to the frame, if necessary if not im.getpalette(): im.putpalette(p) # Store this frame in memory new_frame = Image.new('RGBA', im.size) # Apply the current "partial" frame to the last frame if this is a partial-mode GIF if mode == 'partial': new_frame.paste(last_frame) # Paste the current frame of the GIF onto the blank canvas new_frame.paste(im, (0,0), im.convert('RGBA')) # Run analysis on each band of each pixel to decide if it should be illuminated bands = Image.eval(new_frame, doThreshold) # This makes a list of tuples containing the results: 4 channels, 1 if illuminated, 0 if not colorPresent = list(bands.getdata()) board = 0 # which row of boards we're concerned with yOnBoard = 0 # which row we're accessing w.r.t. the current board # along the x axis of the image... for x in range(0, new_frame.size[0]): # Reset variables yOnBoard = 0 board = 0 # Add the image data to the redRow & greenRow deques from along the y axis #print redFrame, greenFrame for y in range(new_frame.size[1] - 1, -1, -1): pix = (y * new_frame.size[0]) + x # Red band if colorPresent[pix][0] == 1: redFrame[x][board] |= (1 << yOnBoard) # Green band if colorPresent[pix][1] == 1: greenFrame[x][board] |= (1 << yOnBoard) # If we cared about the blue band, we would call on colorPresent[pix][2] yOnBoard += 1 if yOnBoard >= globals.rowPixels: yOnBoard = 0 board += 1 # Add the converted frames to the respective deques redFrames.extend(redFrame) greenFrames.extend(greenFrame) #print redFrame, greenFrame # Go on to the next frame last_frame = new_frame im.seek(im.tell()+1) except EOFError: # return a tuple containing the deques of redFrames & greenFrames, plus the GIF size console.cwrite("Final length: %d * %d" % (len(redFrames), len(greenFrames))) return (redFrames, greenFrames, size[0], size[1])
def __init__(self): # Carry out the rest of the "serial welcome" here by posting initial messages: default is "Awaiting Messages" updateMessages() # Start the thread super(serialThread, self).__init__() console.cwrite("IO Thread is now running; when it stops may not necessarily be advertised...")
def __init__(self): super(twitterStreamThread, self).__init__() console.cwrite("Twitter thread is now running; when it stops may not necessarily be advertised...")
def __init__(self): super(animationThread, self).__init__() console.cwrite("Animation thread is now running; when it stops may not necessarily be advertised...")