Denna handledning utökar den tidigare handledningen, Hur man bygger en Tweet-kontrollerad RGB-LCD, genom att lägga till webbsidor. Med det här kan du ändra konfigurationen av Tweetbox i flyg från din bärbara dator, surfplatta eller telefon.
Du lär dig användbara tekniker i den här handledningen så att du kan lägga till webbsidor till dina egna Raspberry Pi-projekt.
sudo pip installera tornado
Alla koden för detta projekt finns här:
https://github.com/jerbly/tutorials/tree/master/tweetbox
För att återskapa, i slutet av den tidigare handledningen hade du en Raspberry Pi med RGB LCD ansluten till Twitter som mottog offentliga tweets som matchade ett filter. RGB-bakgrundsbelysningen skulle ändra färg beroende på andra matchande ord i tweeten. För att konfigurera filter- och färgkartan måste du ändra koden och starta om programmet.
I detta första skede lägger jag till en webbserver till programmet. Då kan du gå till en webbsida och uppdatera konfigurationen live. Jag använder Tornado, det finns många olika webbramar för Python och jag har använt många av dem under åren men det här är nu min gå till ramverk. Den markerar en massa lådor för många olika projekt.
Ta en titt på Hej världen Exempel på Tornado-sidan så att du kan se hur man ställer in en hanterare och startar en tornado-servertråd. Jag använder samma exakt samma princip här.
Jag tar den tweetbox.py
kod från den tidigare handledningen och lägg till webbramverket. Till att börja med har jag bara en enkel blankett som gör att vi kan ändra vad jag spårar på Twitter. Lägg först till en viss import till toppen av manuset:
importera tornado.ioloop import tornado.web
Nästa behöver jag två hanterare: en för att visa HTML-formuläret och en sekund för att få formulärinsändningen:
klass MainHandler (tornado.web.RequestHandler): def get (self): self.render ("mallar / form1.html") klass ConfigHandler (tornado.web.RequestHandler): def post (self): config_track = self.get_argument "config_track") omstart (config_track) self.write ("Nu spårning% s"% config_track) application = tornado.web.Application ([(r "/" MainHandler), (r "/ config", ConfigHandler) )
Anteckna det MainHandler
användningar skaffa sig
och ConfigHandler
användningar posta
, HTML-formuläret använder postmetoden för att skicka formulärdata tillbaka till webbservern. Att betjäna formuläret MainHandler
kallar bara funktionen för att göra en mall som gjorts och skickas till webbläsaren. Mer om detta på ett ögonblick.
När formuläret kommer tillbaka ConfigHandler
utdrag ett argument, config_track
. Detta skickas till a omstart
funktionen för att ändra inställningarna med tweepy innan du returnerar en enkel sträng som visar vad som spåras.
Skapa mallen genom att lägga till en katalog som heter mallar till källkatalogen och skapa form1.html
filen där:
Raspberry Pi Tweetbox
Det här är en mycket enkel form med en textinmatningsfält och en inmatningsknapp. Det är allt som behövs i detta skede. Skapa sedan nästa omstart
fungera:
def restart (track_text): stream.disconnect () time.sleep (5) #Alla tid för tråden att koppla från ... stream.filter (track = [track_text], async = True)
Detta kopplar bort Twitter-strömmen och återansluter det med det nya filtret, track_text
, vilket är vad som lämnades från formuläret. En viktig förändring här, från den tidigare handledningen, är att Twitter-strömtråden körs i asynkronläge, async = Sant
. Detta kör strömförbindelsen i en bakgrundsgänga så att webbservern går som huvudtråden.
Lägg till ett par rader till slutet för att starta strömmen i asynkront läge och starta sedan webbservern:
stream.filter (spår = ['jeremy'], async = True) application.listen (8888) tornado.ioloop.IOLoop.instance (). start ()
Detta startar att webbservern lyssnar på port 8888. Peka på en webbläsare på http: // din-raspi-ipaddress: 8888 / och du kommer att se formuläret:
Tweetbox webbsida formAnge något annat att spåra som hallon och klicka på Skicka. Efter fem sekunder kommer det att växla till att spåra dessa tweets istället.
I detta skede lägger jag till färgkartan till konfigurationen så att du kan ställa in orden som utlöser RGB-bakgrundsbelysningen att ändra. Eftersom det finns sju inställningar vill jag inte verkligen skriva in dem varje gång jag kör programmet så jag sparar dessa till en fil.
Programmet använder pickle för att spara och ladda konfigurationsfilen, plus det finns en början fil existerar kolla så lägg till den här importen till toppen:
import sallad från genericpath import existerar
De DisplayLoop
klassen är redan ansvarig för hanteringen av backlight_map
så jag utvidgar detta så det tar hand om vad jag spårar för närvarande, track_text
. Läs och skriv konfigurationsmetoder läggs också till här:
klass DisplayLoop (StreamListener): PICKLE_FILE = '/home/pi/py/tweetbox.pkl' def __init __ (själv): self.lcd = Adafruit_CharLCDPlate () self.lcd.backlight (self.lcd.RED) self.lcd.clear () self.track_text = 'jeremy' self.backlight_map = 'röd': self.lcd.RED, 'green': self.lcd.GREEN, 'blue': self.lcd.BLUE, 'yellow': self. lcd.YELLOW, "teal": self.lcd.TEAL, "violet": self.lcd.VIOLET self.msglist = [] self.pos = 0 self.tweet = 'Inget ännu' def write_config (själv): data = "track_text": self.track_text, "backlight_map": self.backlight_map output = open (self.PICKLE_FILE, 'wb') pickle.dump (data, output) output.close () def read_config existerar (self.PICKLE_FILE): pkl_file = öppen (self.PICKLE_FILE, 'rb') data = pickle.load (pkl_file) pkl_file.close () self.track_text = data ["track_text"] self.backlight_map = data ["backlight_map "]
Byt förfrågningshanterare för att ta hand om färgerna. Också nedanför ser du att när jag gör huvudsidan passerar jag i den nuvarande konfigurationen. Det betyder att jag kan fylla i konfigurationsformuläret med de aktuella inställningarna när det begärs.
klass MainHandler (tornado.web.RequestHandler): def get (self): inverted_map = v: k för k, v i display_loop_instance.backlight_map.items () self.render ("mallar / form3.html", config_track = display_loop_instance .Configure.ChangeDirect.Change, config_green = inverted_map [Adafruit_CharLCDPlate.GREEN], config_breen = inverted_map [Adafruit_CharLCDPlate.GREEN], config_blue = inverted_map [Adafruit_CharLCDPlate.BLUE], config_yellow = inverted_map [Adafruit_CharLCDPlate.YELLOW], config_teal = inverted_map [Adafruit_CharLCDPlate.TEAL], config_violet = inverted_map [Adafruit_CharLCDPlate.VIOLET]) klass ConfigHandler (tornado.web.RequestHandler): def post (self): config_track = self.get_argument ("config_track") colour_map = self.get_argument ("config_red"): Adafruit_CharLCDPlate.RED, själv .get_argument ("config_green"): Adafruit_CharLCDPlate.GREEN, self.get_argument ("config_blue"): Adafruit_CharLCDPlate.BLUE, self.get_argument ("config_yellow"): Adafruit_CharLCDPlate.YELLOW, self.get_argument ("config_teal"): Adafruit_CharLCDPlate.TEAL , self.get_ argument ("config_violet"): Adafruit_CharLCDPlate.VIOLET set_config (config_track, colour_map) self.write ("Spåra nu% s"% config_track)
En teknik att notera här är hur jag inverterar färgkartan. När jag behandlar det vill jag att kartan ska vara ord> färg men när du konfigurerar den i den form jag vill ha färg> ord. En Python Dictionary begrip kan användas för att invertera kartan i ett enda uttalande: v: k för k, v i display_loop_instance.backlight_map.items ()
En ny HTML-blankett krävs för att stödja färginställningarna. Jag måste också fylla i formulärinmatningsrutorna med de aktuella inställningarna genom att använda Tornados mallsystem. Det här är väldigt grundläggande, jag tar bara värdena som skickas in till göra
funktionen och dra dem ut här i mallen till exempel config_track
.
Raspberry Pi Tweetbox
Nu när jag kan spara och ladda konfigurationen, den tidigare omstart
rutin behöver vara lite mer sofistikerad:
def set_config (track_text, colour_map): display_loop_instance.set_text ("Uppdatera konfiguration") stream.disconnect () display_loop_instance.track_text = track_text display_loop_instance.backlight_map = colour_map display_loop_instance.write_config () time.sleep (5) #Låt tid för tråden till koppla ifrån ... stream.filter (spår = [display_loop_instance.track_text], async = True) display_loop_instance.set_text ("Uppdaterad konfiguration")
Eftersom det är mer än en omstart är det nu namnet set_config
. Det är här där jag nu ringer write_config
för att spara ändringarna i filen.
Allt som är kvar är ett par förändringar att läsa i konfigurationen vid start:
display_loop_instance = DisplayLoop () display_loop_instance.read_config ()
Och för att starta strömmen från den här inställningen snarare än 'Jeremy'
:
stream = Stream (auth, display_loop_instance) stream.filter (spår = [display_loop_instance.track_text], async = True)
Starta programmet, peka på en webbläsare på http: // din-raspi-ipaddress: 8888 / och du kommer att se formuläret:
WebbformulärDet finns några saker som inte är mycket smarta om det här programmet:
Förseningen medan konfigurationen ändras beror på programmets asynkrona karaktär. Det finns en tråd som hanterar Twitter-strömmen, en tråd som rullar på skärmen och huvudtråden som kör webbservern.
När jag vill ändra inställningarna på strömmen måste jag koppla bort den och sedan ansluta mig till de nya alternativen. Tyvärr finns det ingen händelse från tweepy att berätta för mig när jag har kopplat bort och så fram till nu har jag bara försenat i fem sekunder mellan koppling och återanslutning.
För att ta bort denna fördröjning vad jag ska göra är att starta en ny anslutning medan den gamla är avkopplad så jag behöver inte vänta. Det betyder naturligtvis att det vid en tidpunkt kan finnas två strömmar som tar emot tweets. Detta skulle vara förvirrande på skärmen eftersom du skulle se den gamla spårningen och den nya spårningen kombinerad.
Därför, precis innan du kopplar från, kopplar jag den gamla strömmen till en lyssnare som inte gör något med de inkommande tweetsna. Här är definitionen av NullListener
och förändringarna till set_config
rutin:
klass NullListener (StreamListener): def on_data (self, data): pass def set_config (track_text, colour_map): skriv ut "starta om" display_loop_instance.set_text ("Uppdatera konfiguration") #Kill den gamla strömmen asynkront globalt flöde stream.listener = NullListener ) stream.disconnect () display_loop_instance.track_text = track_text display_loop_instance.backlight_map = color_map display_loop_instance.write_config () #Göra en ny ström stream = Stream (auth, display_loop_instance) stream.filter (spår = [display_loop_instance.track_text], async = True) display_loop_instance.set_text ("Uppdaterad konfiguration")
Angående Spårar nu ... svar, den nuvarande versionen av formuläret skickas till ConfigHandler
som ändrar inställningarna och returnerar detta fula svar. Egentligen vad jag vill ha är att formuläret återkommer med de nya inställningarna på plats.
Jag kan uppnå detta genom att omdirigera användaren tillbaka till /
URL. Dessutom finns det verkligen inget behov av ConfigHandler
ändå kan jag definiera skaffa sig
och posta
metoder på MainHandler
och skicka helt enkelt formuläret där istället:
klass MainHandler (tornado.web.RequestHandler): def get (self): inverted_map = v: k för k, v i display_loop_instance.backlight_map.items () self.render ("mallar / form4.html", config_track = display_loop_instance .track_text, config_red = inverted_map [Adafruit_CharLCDPlate.RED], config_green = inverted_map [Adafruit_CharLCDPlate.GREEN], config_blue = inverted_map [Adafruit_CharLCDPlate.BLUE], config_yellow = inverted_map [Adafruit_CharLCDPlate.YELLOW], config_teal = inverted_map [Adafruit_CharLCDPlate.TEAL], config_violet = inverted_map [Adafruit_CharLCDPlate.VIOLET]) def post (self): config_track = self.get_argument ("config_track") colour_map = self.get_argument ("config_red"): Adafruit_CharLCDPlate.RED, self.get_argument ("config_green"): Adafruit_CharLCDPlate. GREEN, self.get_argument ("config_blue"): Adafruit_CharLCDPlate.BLUE, self.get_argument ("config_yellow"): Adafruit_CharLCDPlate.YELLOW, self.get_argument ("config_teal"): Adafruit_CharLCDPlate.TEAL, self.get_argument ("config_violet"): Adafruit_CharLCDPlate.V IOLET set_config (config_track, colour_map) #Använd en omdirigering för att undvika problem med uppdateringar i webbläsaren från ett formulär post self.redirect ("/") application = tornado.web.Application ([(r "/" MainHandler) ])
Slutligen styling. Att göra detta blick väldigt vackert kan vara en helt ny handledning i sig, men en riktigt bra start är att införa ett ramverk för att ta hand om en hel del stilen för dig.
Jag är Bootstrap för styling och JQuery för scripting. Båda dessa finns tillgängliga på CDN så att du inte behöver ladda ner något, bara inkludera dem i huvuddelen av sidan:
Raspberry Pi Tweetbox
För att se till att formuläret ser bättre ut använder vi Bootstraps Horisontella Form-stil:
Slutligen för att lägga lite polska på gränssnittet anger vi för användaren att Raspberry Pi uppdateras när de klickar på lämna knapp. Detta innebär en liten del av Javascript för att fånga formulärinsändningsevenemanget, ändra knapptexten till Uppdaterande… och inaktivera knappen:
Och här är det färdiga webb-gränssnittet:
Den färdiga webbformuläretDenna handledning har utökats på den föregående för att lägga till ett webb-användargränssnitt till RGB LCD-tv-rutan. Du kan nu styra vad du följer på skärmen och bakgrundsfärgerna du vill ha från telefonen, surfplattan eller skrivmaskinen.