Hier baue ich ein kleines Widget, dass aus zwei Skalen besteht. Die Skalen werden sich automatisch updaten, wenn neue Werte vorliegen.
Unsere Messwerte
Die Messwerte sollen aus einem JSON File mit dem Namen data.json gelesen werden. Der Inhalt ist hier dargestellt.
{"time_code": "Thu Dec 20 20:18:46 2018", "temperature": 25.0, "humidity": 65.0}
Wie man mittels eines Sensor diese Daten erfasst, lern Ihr in diesem Artikel.
Das KV File
Zum Testen erstellen wir ein KV File, dass aus einem Boxlayout besteht, in welchem das Widget dargestellt wird.
Das Widget selber ist auch ein Boxlayout. Das ist wichtig, damit es sich selbstständig an die vorhandene Fläche anpasst. Andernfalls muss man aufwändig per Definition der hint Eigenschaften das Verhalten bestimmen.
Das Widget MyFirstWidget besteht aus zwei Buttons. Im Zentrum wird ein Text angezeigt werden, der den akutellen Wert und die Einheit der Messdaten wiedergibt. Darum herum wird eine Skala, genauer eine Kreislinie den Wert auch optisch darstellen.
Wichtig zu wissen:
- Der Nullpunkt der Linie liegt auf der zwölf Uhr Position. Da ich aber möchte, dass die Skala über 300° symmetrisch dargestellt wird, lasse ich die Linie bei -150° beginnen.
- Über den rgba Wert der Linie kann man die Farbe nach Belieben verändern
- Wenn man die Linie statt mit geradem Abschluss mit rundem Abschluss haben möchte, dann ändert man den Wert cap: 'none' auf cap: 'round'
- Der rgba Wert kann über einen Color Picker ermittelt werden. Allerdings muss der Wert (von 0-255) noch in eine Dezimale (0 bis 1) umgerechnet werden. Dazu muss der Wert durch 255 geteilt werden.
<MyRootLayout>:
orientation: 'vertical'
on_touch_down: self.ids.myfirstwidget.show_data('data.json')
MyFirstWidget:
id: myfirstwidget
<MyFirstWidget>:
Button:
id: btn1
text: root.val1 + root.unit1
font_size: 40
canvas:
Color:
rgba: 0.14,0.8,0.9,0.9
Line:
circle:(self.center_x, self.center_y, 100, -150, root.num1)
width: 15
cap: 'none'
Button:
id: btn2
text: root.val2 + root.unit2
font_size: 40
canvas:
Color:
rgba: 0.14,0.8,0.9,0.9
Line:
circle:(self.center_x, self.center_y, 100, -150, root.num2)
width: 15
cap: 'none'
Das Python File
Unser Widget bekommt zunächst ein paar Eigenschaften. Die sollen sich selbst updaten, wenn sie auf Seite des Python Files geändert werden. Dazu gibt es in Kivy eine einfache Möglichkeit. Definiert man eine Variable als Property, so kann diese an eine beliebige andere Variable gebunden werden und mittels des implementierten Observer Patterns automatisch aktualisiert werden.
Es gibt verschiebende Properties. Hier verwenden wir die NumericProperty und StringProperty. im kv file haben wir die folgenden Variablen definiert, die sich automatisch akutalisieren sollen:
num1: Stand der ersten Skala in ° (int)
num2: Stand der zweiten Skala in ° (int)
val1: Wert der ersten Messung (string)
val2: Wert der zweiten Messung (string)
unit1: Einheit der ersten Messung (string)
unit2: Einheit der zweiten Messung (string)
Hierbei muss man die Datentypen beachten und ggf. konvertieren, so dass der Wert mit der Property kompatibel ist.
Nachdem das erledigt ist, definieren wir unsere erste Funktion show_data(), welche aus einer gegebenen Datei die Temperatur und Feuchtewerte ausliest.
Anschießend werden die Werte noch auf den Anzeigebereich skaliert, damit die Skala nicht über die 300° hinaus ausschlägt.
Damit es etwas übersichtlicher und wiederverwendbar wird, habe ich diese Funktion in fit_to_scale() ausgelagert.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import NumericProperty, ObjectProperty, StringProperty
import json
class MyFirstWidget(BoxLayout):
num1 = NumericProperty(150)
num2 = NumericProperty(150)
val1 = StringProperty('--')
val2 = StringProperty('--')
val3 = StringProperty('--')
unit1= StringProperty('°C')
unit2= StringProperty('%rH')
def show_data(self, filename):
'''opens a json file and reads temperature and humidity'''
with open(filename, 'r') as read_file:
data=json.load(read_file)
self.val1 = str(data['temperature'])
self.val2 = str(data['humidity'])
self.val3 = data['time_code']
self.num1 = self.fit_to_scale(data['temperature'], 15, 40)
self.num2 = self.fit_to_scale(data['humidity'], 20, 90)
def fit_to_scale(self, value, minimum, maximum):
''' scales the given value between the set max and min limits'''
return ((value - minimum) / (maximum - minimum) * 300) - 150
class MyRootLayout(BoxLayout):
pass
class MyWidgetApp(App):
def build(self):
return MyRootLayout()
if __name__ == '__main__':
MyWidgetApp().run()
Das Ergebnis
Und schon hat man eine einfache Visualisierung von beliebigen Werten, die sich durch Modifizieren des kv files beliebig an das eigene Design anpassen lässt.
Startet man die App erscheint zunächst das nackte Widget, mit den vordefinierten Werten -- + Einheit im Zentrum einer Skala mit dem vordefinierten Stand.
Klickt man nun an beliebiger Stelle, so wird das on_touch_down Event ausgelöst. Dadurch wird die Funktion show_data() ausgeführt und die Werte aktualisieren sich.
Hier noch ein Beispiel mit modifizierter Farbgebung und runden Linienenden
Quelle
Mehr zu Linien und deren Eigenschaften findet Ihr hier
Wer sich noch mehr zu den Möglichkeiten des Binding in Kivy informieren will, wird hier fündig
Quellcode auf GitHub
https://github.com/HaikoKrais/TwoScalesWidget