In den vergangenen Artikeln haben wir Aktien zur Watchlist zugefügt und auch wieder entfernt.
Wichtig ist natürlich auch, dass wir die Watchlist speichern!
Wir wollen ja nicht jedes Mal alle Aktien neu eingeben müssen.
Also lass uns gleich mal loslegen.
Kivy Serie
- Erste Schritte mit Fenstern
- Den Benutzer Willkommen heißen
- Die Verpackung zählt!
- Weitere Ansichten (Views)
- Scrollbare Liste mit Aktiendaten
- Aktiendaten API anbinden (yfinance)
- Fehler “Instruction outside the main Kivy thread”
- Element aus dem Layout entfernen
- Aktien in der Watchlist speichern
Aktien in der Watchlist speichern
Um Daten zu speichern, gibt uns Kivy direkt ein Mittel an die Hand.
Mit Storage gibt Kivy uns einfachen Zugang zu einem einfachen Speicher. Hier kann unsere App Daten ablegen, sie wieder laden und bei Bedarf löschen.
Für unsere App habe ich mich für einen Json Speicher entschieden. Json ist ein gängiges Format und arbeitet super mit Python zusammen.
Damit kann ich es auch außerhalb von unserer App verwenden, wenn ich das möchte.
Um einen Speicher (Storage) zu erzeugen, erzeuge ich lediglich eine neue Instanz des jeweiligen Storage:
store = JsonStore('watchlist.json')
Bei der Erzeugung übergeben wir den Namen für unseren Speicher.
Ich habe hier einfach watchlist.json gewählt.
Zum einen, weil es eine Json Datei ist und zum anderen, weil sie unsere Watchlist Daten vorhalten soll.
Einträge fügen wir in der Funktion add_ticker_row hinzu.
Hier wissen wir also, dass die Abfrage der Daten funktioniert hat und ein Eintrag in die Watchlist gespeichert werden soll.
Der perfekte Moment für unseren Storage.
Dafür fügen wir am Ende der Funktion einfach die Initialisierung des Storage an und legen das aktuelle Symbol mit rein:
def add_ticker_row(self, symbol, name, price, *args):
self.message.text = ''
ticker = Label(text=symbol, size_hint_x=0.2)
self.stock_list.add_widget(ticker)
name = Label(text=name, size_hint_x=0.4)
self.stock_list.add_widget(name)
price = Label(text=price, size_hint_x=0.2)
self.stock_list.add_widget(price)
delete = Button(text='Entfernen', size_hint_x=0.2)
delete.bind(on_press=self.remove_ticker_row)
self.stock_list.add_widget(delete)
store = JsonStore('watchlist.json')
store.put(symbol)
Über die .put() Funktion wird zuerst ein Schlüssel übergeben. Anschließend können als Schlüssel-Werte Paare weitere Werte angegeben werden, die dem Schlüssel zugeordnet werden sollen.
Uns reicht hier der Schlüssel. Wir brauchen ja schließlich nur das Ticker Symbol der Aktie.
Solltest du mal mehr Werte brauchen, kannst du hier einfach nachlesen: Kivy Storage.
Wenn du jetzt ein Ticker Symbol zufügst, wird es automatisch in deiner Watchlist gespeichert.
Damit erhältst du eine Datei an dem von dir angegebenen Pfad.
In unserem Beispiel hier ist es die watchlist.json direkt im Pfad unseres Pythonskripts.
Anmerkung:
Ich habe hier den Parameter ticker zu symbol umbenannt, um eine Namensüberschneidung mit dem Label zu verhindern.
Der Inhalt der watchlist.json ist einfaches Json Format:
{
"RWE.DE": {},
"MSF.DE": {}
}
Aktien aus der Watchlist laden
Die Aktien werden also jetzt nach dem Zufügen in der Watchlist gespeichert.
Der nächste Schritt ist also, sie auch wieder zu laden, wenn die App startet.
Geladen werden sollen die Aktien im StockView. Hier wird immerhin unsere Watchlist angezeigt.
Die __init__ Funktion konfiguriert den View und bereitet ihn zur Anzeige vor.
Also ist das der perfekte Ort, um auch unsere Watchlist zu laden.
Wenn du den Storage in der Hand hast, können die Keys einfach in einer Schleife iteriert werden.
Und mehr als die Keys brauchen wir ja nicht.
Also lass uns das an das Ende unserer __init__ Funktion für den StockView anfügen:
class StockView(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 1
...
self.stock_add.add_widget(self.ticker_add)
store = JsonStore('watchlist.json')
for item in store:
self.query_stock_data(item)
Das Zufügen einer Aktie zur Watchlist ist ja bereits in eine eigene Funktion ausgelagert.
Dann können wir die direkt verwenden: query_stock_data
Das einzige Problem ist, dass sie aktuell noch kein Symbol entgegennimmt. Sie liest es selbst aus dem ticker_input Feld aus.
Das heißt, wir müssen die Funktion jetzt auch noch anpassen.
Wird eine Aktie über den Button in der App zugefügt, soll alles so bleiben wie es ist.
Wird sie aber durch das laden des Storage zugefügt, müssen wir das Symbol übergeben können.
Das schreit nach einem optionalen Parameter:
def query_stock_data(self, symbol=None):
symbol = symbol or self.ticker_input.text.strip()
....
Das war’s.
Damit können wir ein Symbol übergeben, wenn wir den Storage laden.
Übergeben wir nichts, ist symbol=None.
Mit dem or sagen wir “belege die Variable symbol mit dem Wert aus symbol. Ist der None, dann nimm den Wert aus self.ticker_input.text.strip()”
Und schon kümmert sich Python um alles Weitere.
Doppelte Einträge vermeiden
Als kleinen Bonus können wir hier auch direkt die Funktion erweitern, um doppelte Einträge in der Watchlist zu verhindern.
Beim Speichern passiert das schon automatisch, da die Aktien Ticker Symbole als Schlüssel genutzt werden.
Schlüssel dürfen immer nur einmal existieren.
Wenn wir jetzt noch in der App das Zufügen verhindern wollen, können wir auch hier unseren Storage zur Hilfe nehmen.
Ist also der Parameter symbol=None, dann kommt der Aufruf aus der App.
Wenn das Aktien Ticker Symbol aus self.ticker_input.text.strip() schon in dem Storage vorhanden ist, dann wäre es ein Duplikat.
Also: “Wenn symbol=None und self.ticker_input.text.strip() schon im Storage, dann gib eine Meldung aus. Andernfalls frag die Daten ab und füge zur Watchlist hinzu”.
Und genau so können wir das auch im Code abbilden:
def query_stock_data(self, symbol=None):
# symbol = symbol or self.ticker_input.text.strip()
store = JsonStore('watchlist.json')
if not symbol and self.ticker_input.text.strip().upper() in store:
self.message.text = 'Das Symbol ist bereits in der Liste.'
else:
symbol = symbol or self.ticker_input.text.strip()
stock = yfinance.Ticker(ticker=symbol)
data = stock.info
if 'regularMarketPrice' in data and data.get('regularMarketPrice') is not None:
price = f'{data.get("regularMarketPrice"):.2f} {data.get("currency")}'
Clock.schedule_once(partial(self.add_ticker_row, data.get('symbol'), data.get('longName'), price), 0.5)
else:
self.message.text = f'Für das angegebene Symbol {symbol} gab es kein Ergebnis.'
Fügen wir jetzt ein Aktien Ticker Symbol zur Watchlist hinzu, wird erst geprüft, ob es bereits im Storage existiert.
Wenn ja, geben wir einfach nur eine Nachricht aus und machen nichts weiter.
Wenn nein, belegen wir wieder die Variable symbol mit dem Wert aus dem Parameter, oder dem Eingabefeld.
Anschließend läuft die Logik ab, wie zuvor.
Aktien aus der Watchlist löschen
Als letzter Schritt fehlt noch, dass wir auch Aktien aus unserer Watchlist wieder herausbekommen.
In der App funktioniert das bereits über den Entfernen-Button.
Dabei muss jetzt aber auch noch unser watchlist.json angepasst werden.
Natürlich liefert der Kivy Storage auch dafür die passende Funktion mit: delete
An delete kann einfach ein Schlüssel übergeben werden, der aus dem Storage entfernt werden soll.
Alles, was wir tun müssen ist, also das delete an das Ende unserer remove_ticker_row Funktion zu hängen.
Und schon wird der jeweilige Eintrag aus dem Storage entfernt:
def remove_ticker_row(self, symbol, *args):
row = [element for element in args[0].parent.children if element.y == args[0].y]
for element in row:
self.stock_list.remove_widget(element)
store = JsonStore('watchlist.json')
store.delete(symbol)
Das Symbol bekommen wir, in dem wir es einfach wieder als Parameter zur Funktion hinzufügen.
Und schon kann es aus dem Parameter an das delete übergeben und somit aus dem Kivy Storage gelöscht werden.
Bleibt nur noch ein Problem: Wie übergeben wir den Parameter?
Immerhin ist der Aufruf der Funktion über einen Button gebunden.
Das heißt, wir haben hier nur eine Referenz zu der Funktion und können nicht einfach einen Parameter übergeben… Oder?
Doch.
Können wir schon.
Haben wir vorher schon gemacht.
Als wir eine Funktion mit Clock für eine spätere Ausführung eingeplant haben.
Dabei ist partial unser Retter in der Not.
In der Funktion add_ticker_row wird die Funktion an den Entfernen-Button gebunden.
Hier kannst du auch einfach mit partial die Funktion zusammen mit dem Parameter übergeben:
def add_ticker_row(self, symbol, name, price, *args):
self.message.text = ''
ticker = Label(text=symbol, size_hint_x=0.2)
self.stock_list.add_widget(ticker)
name = Label(text=name, size_hint_x=0.4)
self.stock_list.add_widget(name)
price = Label(text=price, size_hint_x=0.2)
self.stock_list.add_widget(price)
delete = Button(text='Entfernen', size_hint_x=0.2)
delete.bind(on_press=partial(self.remove_ticker_row, symbol))
self.stock_list.add_widget(delete)
store = JsonStore('watchlist.json')
store.put(symbol)
Fazit
Das war alles.
Mit Kivys Storage lassen sich super einfach und schnell Daten unserer App speichern und auch wieder laden.
Über JsonStore(<Dateiname>) lässt sich direkt eine Datei erzeugen.
Mit put und delete lassen sich Werte zufügen und auch wieder entfernen.
Einfacher kann es kaum sein.
Du hast noch Fragen?
Schau gerne im Discord vorbei!
Hier tümmeln sich Anfänger und Fortgeschrittene, die sich gerne bei Ihren Programmen unterstützen.
Ingo Janssen ist ein Softwareentwickler mit über 10 Jahren Erfahrung in der Leitung seines eigenen Unternehmens.
Er studierte Wirtschaftsinformatik an der TH Deggendorf und hat Softwareentwicklung an der FOM Hochschule in München unterrichtet.
Ingo hat mit einer Vielzahl von Unternehmen zusammengearbeitet, von kleinen und mittelständischen Unternehmen bis hin zu MDAX- und DAX-gelisteten Unternehmen.
Ingo ist leidenschaftlich daran interessiert, sein Wissen und seine Expertise mit anderen zu teilen. Aus diesem Grund betreibt er einen YouTube-Kanal mit Programmier-Tutorials und eine Discord-Community, in der Entwickler miteinander in Kontakt treten und voneinander lernen können.
Sie können Ingo auch auf LinkedIn, Xing und Gulp finden, wo er Updates über seine Arbeit teilt und Einblicke in die Tech-Branche gibt.
YouTube | Discord | LinkedIn | Xing | Gulp Profile