Python: Unterschied zwischen den Versionen
K (→Funktionen) |
|||
Zeile 203: | Zeile 203: | ||
a,b,c = addiere(9) | a,b,c = addiere(9) | ||
Wenn man objektorientiert arbeitet, kann man natürlich auch ein Objekt mit den entsprechenden Werten zurückgeben. | Wenn man [[Objektorientierung|objektorientiert]] arbeitet, kann man natürlich auch ein Objekt mit den entsprechenden Werten zurückgeben. | ||
== Umgang mit Dateien == | == Umgang mit Dateien == |
Version vom 29. Mai 2022, 09:47 Uhr
Einführung
Python ist eine Interpretersprache wie PHP oder SQL. Zur Zeit ist Python 3 im Einsatz und die Unterstützung von Version 2 ist beendet. Viele Programme für Python 2 laufen aber weiterhin ohne Anpassung unter Python 3. Die Sprache wird unter anderem häufig auf dem Raspberry Pi verwendet.
Python-Konsole
Wie andere Interpreter-Sprachen bietet auch Python eine Konsole, in der man Befehle direkt eingeben kann. Diese Konsole wird direkt mit python
aufgerufen. Je nach Betriebsystem kann es sein, dass der Befehl noch auf Python 2 verweist und der Interpreter explizit mit python3
aufgerufen werden muss.
Danach wird direkt der Prompt für die Eingabe von Befehlen angezeigt. Da Python mit Einrückungen arbeitet, müssen diese auch in der Konsole angegeben werden. Um die Befehle abarbeiten zu lassen, wird nach der letzten Zeile 2x Enter gedrückt.
Wie bei der Bash kann man ältere Befehle mit den Pfeiltasten nochmals aufrufen, um ein wiederholtes Tippen zu vermeiden.
Python Programm ausführen
Wenn man den Namen des gespeicherten Programms angibt, wird dieses direkt interpretiert: python meinprogramm.py
. Gibt es beim Ausführen Probleme wegen fehlender Zugriffsrechte, so muss man es als Root ausführen: sudo python meinprogramm.py
Beim Raspi kann man den Zugriff auf die GPIO auch ohne root verwenden.
Ausführung im Hintergrund
Dies ist mehr eine Eigenschaft vom Betriebsystem. Mit & wird die Ausführung in den Hintergrund verlagert:python meinprogramm.py &
. Das System gibt dann die Prozess-ID zurück, mit der man das Programm wieder in den Vordergrund holen kann:fg 123
oder wieder mit dem Programmnamenfg python meinprogramm.py
.
Kommandozeilen-Argumente auswerten
import sys for (i,value) in enumeration(sys.argv): print("arg: %d%s" % (i, value))
Im argv-Objekt sind dann die Parameter drin. Im 0 ist der Programmname und danach die Parameter.
Kommentare
Als Kommentarzeichen gilt die Raute: # Alles nach dem Zeichen in einer Zeile wird ignoriert.
# Das ist ein Kommentar print("Hallo") # Nun ein Kommentar
Mehrere Zeile werden mit """ eingeleitet und abgeschlossen:
""" Das sind Kommentarzeilen """
Variablen
Variablen müssen nicht deklariert werden, sondern der Typ wird durch die Zuweisung bestimmt.
Variablen sollten immer mit einem Kleinbuchstaben beginnen. Will man längere Wörter, kann man diese mit Unterstrich trennen oder mit CamelCase:var var_var varVar
.
Definition | Typ |
---|---|
a = 123 | Gazzahl (Integer) |
a = 123.45 | Fliesskommazahl (Float) |
a = "Hallo" oder a = 'Hallo' oder a = '''Hallo''' oder a = """Hallo""" | String. Die Varianten sind dazu da, um einzelne " oder ' im Text einfach zu übernehmen, wobei dies auch mit \" oder \' möglich wäre. Bei der Dreifach-Schreibweise sind auch Strings über mehrere Zeilen möglich. |
a = True | Bool (Gross-, Kleinschreibung wird beachtet) |
Mit Variablen rechnen
Es werden die normalen Rechenoperationen unterstützt wie +-*/
. Dazu % für Modulo (Teilung ohne Rest) und ** für die Potenzierung. Die Klammern werden berücksichtigt und für Bit-Operationen stehen noch weitere Möglichkeiten zur Verfügung.
Strings
- Strings sind wie in Java fixe Arrays und können nicht verändert werden (generiert ein neues Objekt). Für den Zugriff auf einzelne Buchstaben kann man entsprechend den Array-Zugriff b = a[2] verwenden.
- Zwei Strings kann man wie Zahl verknüpfen:
c = a + b
. Will man dieser aber mit einer Zahl, kommt eine Fehlermeldung. Hier muss man eine explizite Konvertierung vornehmen:c = a + str(15)
. - Will man einen String in eine Zahl konvertieren, so geht dies mit der entsprechenden Konvertierung:
a = "15" b = int(a)
. Dies geht entsprechend auch mit float. Mit Angabe der Basis kann man auch in eine Binär- oder Hexzahl wandeln:a = int(1100,2)
odera = int(A9B3,16)
. - Die Länge eines Strings bestimmt man mit
len()
. Dies funktioniert auch mit einem Array. - Einen Substring kann man mit
s = a.find("test")
suchen, wobei dann der Index des ersten Zeichens geliefert wird oder -1, wenn nichts gefunden wurde. - Will man einen Substring nach der Suche kopieren, so gibt man den Bereich als Array an, wobei die erste Position den Startbuchstaben bestimmt und die zweite Zahl den Endbuchstaben, der aber nicht übernommen wird:
s = a[2:5]
. Lässt man die Zahl vor oder nach dem Doppelpunkt weg, so wird jeweils ab Anfang oder bis zum Ende genommen:s = a[:5]
/s = a[2:]
. - Teilstring ersetzen wird mit
a = "Hallo X" a.replace("X","Markus")
durchgeführt, wobei Gross- und Kleinschreibung und auch Leerzeichen beachtet werden. - Mit
a = "Hallo X" b = a.upper()
wird der String in Grossbuchstaben und mitb = a.lower()
in Kleinbuchstaben zurückgegeben, da Strings ja unveränderlich sind. Eine Neuzuweisung ist mita = a.lower()
möglich.
Arrays
Arrays werden in zwei Formen angeboten (Listen und Dictionaries).
Listen
Listen sind immer geordnet, doch können diese auch aus unterschiedlichen Datentypen bestehen. Der Zugriff auf Elemente geschieht über die Position. Eine Liste erkennt man an den eckigen Klammern. Bei den untenstehenden Beispielen werden die Listen immer nur in der Zeile beeinflusst. Das nächste Beispiel nimmt wieder die originale Liste.
a = [1, 3, 6, 2, 19] b = [True, 45.3, "Hans"]
- Die Nummerierung beginnt bei 0.
- Für den Zugriff wird die Position angegeben:
print(a[2])
-> 6 - Das Veränderung eines Elements geschieht entsprechend auch über den Index:
a[2] = 15
-> [1, 3, 15, 2, 19] - Mit
a.append(9)
wird das Element hinten an die Liste hinzugefügt: [1, 3, 6, 2, 19, 9] - Mit
a.insert(3, 5)
wird das Element an diese Position hineingeschoben: [1, 3, 6, 5, 2, 19] - Will man eine Liste an eine andere anhängen, so geht das mit extend:
a.extend(b)
-> [1, 3, 6, 2, 19, True, 45.3, "Hans"] - Mit
pop()
wird das letzte Element aus der Liste entfernt. Wenn man den Wert verarbeiten möchte, so kann man ihn an eine Variable übergeben, sonst wird er verworfen:c = a.pop()
-> c = 19 - Gibt man bei pop den Index an, so wird das Element an dieser Stelle entfernt:
a.pop(0)
-> [3, 6, 2, 19] - Will man bei einem Befehl auf einen nicht existierenden Eintrag zugreifen, so kommt ein "Index out of range"-Fehler. Entsprechend sollte man diesen immer mit einem try-catch-Block abfangen.
- Mit
split()
kann man einen String in eine Liste umwandeln. Ohne Parameter wird der Space als Trennzeichen genommen und sonst das übergebene Zeichen:c = "AB CD--EF--GH".split("--")
-> ["AB CD","EF","GH"] - Mit
a.sort()
-> [1, 2, 3, 6, 19] kann man eine Liste alphabetisch oder numerisch sortieren lassen. Da hier die Liste direkt angepasst wird, kann man (falls man beide Listen braucht), das Original mit dem copy-Befehl in eine andere Variable "retten". Da die Funktion ausgelagert ist, muss man zuerst die Bibliothek importieren:import copy b = copy.copy(a)
. - Möchte man einen Bereich aus der Liste weiter verarbeiten, so kann man dies mit dem :-Operator:
b = a[2:4]
-> [6, 2]. Auch hier ist die zweite Stelle nicht inklusiv. Lässt man einen Bereich weg, so gilt entsprechend vom Anfang oder bis zum Ende.b = a[2:]
-> [6, 2, 19]. Es gibt auch die Möglichkeit, vom Ende der Liste her zu arbeiten, wenn man negative Zahlen einsetzt:b = a[-2:]
-> [2, 19]. Entsprechend wird hier von der zweitletzten Position bis zum Ende gearbeitet. - Will man eine Funktion auf eine Liste anwenden, so kann man dies mit for und einer [] ganz kompakt:
[x.upper() for x in a]
. Hier wird die Anweisung zwar vor der Zuweisung ausgeführt (x wird ja erst nach dem for zugewiesen), doch der Code im Hintergrund referenziert korrekt, so dass man die Anweisung mit dem upper nicht erst in einer zusätzlichen Zeile machen muss.
Dictionarys
- Ein Dictionary ist eigentlich eine Liste mit einem fix vergebenen Schlüssel. Der Vorteil ist vor allem die schnelle Suchgeschwindigkeit, wenn man den Schlüssel weiss. Im Gegensatz zu einer Liste wird ein Dictionary mit {} umschlossen. Der Schlüssel ist die erste Stelle und der Wert die Zweite. Ein Name für die Personalnummer würde also folgendermassen erstellt:
a = {123:"Anton", 456:"Fritz", 987:"Susi"}
- Ein Dictionary kann beliebige Werte für den Schlüssel annehmen. Auch Schachtelungen mit Verweisen auf Unterdirectories sind möglich. Hierbei wird an Stelle des Wertes einfach die Variable des einzusetzenden Directories gesetzt.
b = {111:a}
- Da der Key den Zugriff regelt ist die Reihenfolge nicht gleich dem Anlegen. Man kann daher nicht einfach mit
b = a[2]
auf Susi zugreifen, sondern muss den Key angeben:b = a[987]
. - Beim Zugriff auf einen nicht vorhandenen Key kommt es zu einem Fehler, so dass auch hier try catch verwendet werden sollte.
- Ein neuer Eintrag wird mit
a[333] = "Hans"
hinzugefügt. Wie man sieht, werden hier wieder die eckigen Klammern verwendet, da das Directory ja schon angelegt ist. Vorsicht: Ist der Schlüssel schon vorhanden, so wird er überschrieben. Entsprehend sollte man vor dem einfügen prüfen, ob der Eintrag auch nicht existiert und entsprechend nachfragen, ob überschrieben werden soll. - Wie bei der Liste wird ein Eintrag mit pop entfernt. Da es aber keine eigentliche Reihenfolge gibt, muss immer der Key angegeben werden:
a.pop(987)
- Wie bei der Liste kann man auch hier mit for durch das Dictionary iterieren. Will man dabei den Schlüssel und den Wert verarbeiten, so vergibt man einfach für beide eine Hilfsvariable
for key, value in a.items(): print(key+value)
Vergleiche
Es stehen die üblichen Vergleiche zur Verfügung wie <,>,=>,<=,== oder != / <>
wobei != und <> äquivalent sind. Dazu kann man auch mit and
und or
verknüpfen, wobei die Schlüsselwörter klein geschrieben werden.
Beim vergleichen von Strings wird lexikalisch vorgegangen (a ist kleiner als b), doch Grossbuchstaben sind kleiner als Kleinbuchstaben (a > A -> True).
Verzweigungen
Im Gegensatz zu anderen Sprachen bietet Python der Einfachheit halber nur das if elif else
ohne switch usw. Da Python seine Konnstrukte mit Einrückungen bildet, muss dies berücksichtigt werden, damit die Befehle korrekt interpretiert werden. Entsprechend sollte immer mit Leerzeichen und nie mit Tab eingerückt werden.
x = 100 if x > 99: print("Die Zahl ist mindestens dreistellig.") elif x > 9 print("Die Zahl ist mindestens zweistellig.") elif x > -1 print("Die Zahl ist mindestens einstellig.") else: print("Die Zahl ist negativ.") print("Diese Ausgabe kommt immer")
elif else
können entsprechend weggelassen werden.
Schleifen
Python liefert hier zwei Typen für definierte und undefinierte Anzahl Durchgänge.
for
for i in range (1,11): print(i)
Die Laufvariable kann auch rückwärts angegeben werden (11,1). Die zweite Variable ist wie bei Arrays nicht inklusiv. Entsprechend läuft diese Schleife nur bis 10.
Die Schleife kann auch durch ein Array iterieren.
a = [1, "Hallo", 17.5, True] for x in a: print(x)
Hier wird jeweils ein Element in die Hilfsvariable x kopiert und dieses dann ausgewertet, bis das Array/Liste durch ist.
Indexierte Arrays heissen in Python Dictionaries. Auch bei diesen kann for
angewendet werden.
telefone = {"Hans":"+1234", "Fritz":"+4321"} for name, nummer in telefone.items(): print("Name: " name + " Nummer: " + num)
while
Hier ist die Abbruch-Bedingung nicht bekannt oder es gibt keine. Entsprechend wird die Schleife so lange durchlaufen, bis die Bedingung nicht mehr zutrifft. Etwa wenn man auf eine Tastatur-Eingabe wartet.
eingabe = "" while eingabe != "x": eingabe = input("Bitte Befehl eingeben: ") print(eingabe) print("Schlaufe verlassen nach Eingabe von x.")
Eine Schleife kann auch ohne Abbruchbedingung formuliert werden und mit einem break verlassen werden. So kann man mehrere Abbruchbedingungen schaffen:
eingabe = "" while True: eingabe = input("Bitte Befehl eingeben: ") print(eingabe) if eingabe == 0: break if eingabe == "a": break print("Schlaufe verlassen nach Eingabe von: "+eingabe)
Natürlich kann man auch eine Abbruchbedingung und break kombinieren, wenn dies sinnvoll erscheint.
Ausgabe
- Für die Ausgabe wird der
print()
Befehl verwendet:print("Der Wert beträgt: "x)
. - Will man die Ausgabe formatieren, so wird
format()
eingesetzt. Für Werte mit 2 Stellen nach dem Komma:x = 1.2345 "x={:.2f}".format(x)
- Entsprechend kann man auch die Zeit als String ausgeben:
from datetime import datetime d = datetime.now() "{:%d.%m.%Y %H:%M:%S}".format(d)
Eingabe
Für die Eingabe von Tastaturevents kann Input
genommen werden: eingabe = Input("Bitte eine Zahl eingeben: ")
Funktionen
Funktionsblöcke werden mit def funktionsname(parameter)
erstellt. Parameter sind optional und können mit Standardwerten belegt werden. Mit return werden Werte zurückgegeben. Die Konvention der Benennung folgt der von Variablen und sollte entsprechend mit einem Kleinbuchstaben beginnen. Funktionsblöcke werden entsprechend eingerückt.
def addiere(eins = 5, zwei = 7): drei = eins + zwei; print(drei) return drei print(addiere(9))
Es ist möglich, mehrere Werte zurückzugeben. Hierfür muss man nur beim return die entsprechenden Variablen kommagetrennt anhängen und entsprechend die geiche Anzahl Variablen beim Aufruf zur Verfügung stellen, so dass diese korrekt abgefüllt werden.
def addiere(eins = 5, zwei = 7): drei = eins + zwei; print(drei) return eins, zwei, drei a,b,c = addiere(9)
Wenn man objektorientiert arbeitet, kann man natürlich auch ein Objekt mit den entsprechenden Werten zurückgeben.
Umgang mit Dateien
Zugriff auf Dateien
Da beim Zugriff Fehler passieren können, muss dies immer in einer Fehlerbehandlung abgefangen werden, so dass am Schluss der Close sauber ausgeführt wird. Natürlich muss man in einem Programm zuerst prüfen, ob die Datei existiert und ob man Test anhängen oder überschreiben will usw.
Der Pfad der Datei kann relativ zum eigenen Verzeichnis oder absolut mit dem Wurzelverzeichnis am Anfang / angegeben werden. Da Python platformunabhängig ist, sorgt Python dafür, dass Pfade korrekt mit dem Betriebsystem aufgelöst werden.
Für das Öffnen stehen verschiedene Flags zur Verfügung: r = read, w = write, a = append, b = binary, t = text (Standard), + = read and write. Man kann diese Flags auch mit + verknüpfen, wenn man etwa im binären Modus lesen will (r+b).
Datei schreiben / erstellen
Es braucht immer ein Handle für die Operationen. Entsprechend gilt immer folgendes Vorgehen:
- Fehlerhandling:
try:
- Öffnen:
f = open('test.txt','w')
- Schreiben:
f.write('Test-Text')
- Schliessen:
f.close()
- Fehler bearbeiten:
except IOError: print("Fehler beim Schreiben der Datei")
Datei lesen
Es braucht immer ein Handle für die Operationen. Entsprechend gilt immer folgendes Vorgehen:
- Fehlerhandling:
try:
- Öffnen:
f = open('test.txt','r')
- Lesen:
text = f.read()
- Schliessen:
f.close()
- Fehler bearbeiten:
except IOError: print("Fehler beim Lesen der Datei")
Es ist auch möglich, die Datei zeilenweise mit text = f.readline()
einzulesen.
Datenstruktur in Datei verwalten
Python verwendet das "Pickling", um Daten zu speichern. Dabei wird einfach die gewählte Struktur abgespeichert und neu geladen. Auch hier sollte die Fehlerbehandlung integriert werden.
import pickle mylist = ["abc", 1, 2, "Hans"] f = open("mylist.pickle","w") pickle.dump(mylist,f) f.close()
f = open("mylist.pickle") daten = pickle.load(f) f.close() print(daten)
Fehlerbehandlung
Sämtliche Befehle können mit try
"versucht" werden. Bei einem Fehler wird nach except
verwiesen. Im Erfolgsfall kann man auch einen else
-Block ansteuern und zusätzlich am Schluss noch finally
, das immer angesteuert wird.
list = [1, 2, 3] try: list[5] except Exception as e: print("Element nicht in der Liste") print(e) else: print("Element innerhalb der Liste") finally: print("Wird immer durchlaufen")
Das Exception-Objekt muss nicht verwendet werden, doch es liefert Informationen zum Fehler. Ist vor allem dann nützlich, wenn man Fehler "nach oben" weitrgibt und dann die wirkliche Ursache noch mitteilen möchte.
Objektorientierung
Klasse und Konstruktor
Wie bei Java wird zuerst eine Klasse erstellt und davon eine Instanz gebildet. Die Syntax ist aber etwas anders:
class Person: '''Kommentar zur Klasse Person''' def __init__(self, first_name, last_name, tel): self.first_name = first_name self.last_name = last_name self.tel = tel
Der Kommentar mit drei ' dient der Dokumentation der Klasse und kann später mit Person.__doc__
abgefragt werden, wenn man etwa eine externe Bibliothek einbindet.
Der Konstruktor heisst im Gegensatz zu Java immer init und hat vor- und nachher zwei Unterstriche zur Kennzeichnung. Als Parameter muss am Anfang jeder Methode (Funktionen heissen in objektorientierten Sprachen so) immer self stehen, das auf sich selbst referenziert. This im Gegensatz zu "this" bei Java. Die Klassenvariablen werden Member-Variablen genannt.
Eine Instanz dieser Klasse würde dann folgendermassen erstellt: p = Person("Hans","Muster","+41123")
Methoden
Methoden werden wie Funktionen erstellt, müssen aber wie erwähnt als ersten Parameter self haben:
def full_name(self): return self.first_name+" "+self.last_name
Eine Anwendung wäre dann print(p.full_name())
-> Hans Muster
Möchte man eine Methode innerhalb der Klasse aufrufen, so wird nicht der Klassenname verwendet, sondern ein self.methode()
Vererbung
Python beherrscht Mehrfach-Vererbung. Bei der Vererbung wird bei der Klassendefinition in Klamer die Elternklasse angegeben. Bei Mehrfachvererbung werden die Klassen einfach durch Kommas getrennt. Beim Vererben muss der Konstruktor der Elternklasse aufgerufen werden, bevor eigene Member-Variablen erstellt werden. Bei Python 3 geschieht dies mit super. unter Python 2 noch mit dem Klassennamen der Elternklasse Person. Bei mehreren Eltern müssen entsprechend alle aufgerufen werden.
class Arbeiter(Person): def __init__ (self, first_name, last_name, tel, gehalt): supper.__init__ (self, first_name, last_name, tel) self.gehalt = gehalt def gehalt_festlegen(self, gehalt) self.gehalt = gehalt def gehalt_erhoehen(self, gehaltserhoehung) self.gehalt += gehaltserhoehung
Externe Module / Bibliotheken einbinden
Dies geht ganz einfach mit dem Import Befehl. Hier kann man festlegen, ob man nur bestimmte Methoden oder alles verwenden will. Dies ist dann abhängig vom verfügbaren Speicherplatz. Wenn man nur eine bestimmte Methode braucht, dann lohnt es sich nicht, das ganze Modul zu holen, nur weil es schneller geschrieben ist.
import Modulname import Modulname as X from Modulname import * from Modulname import Methode
- Verwendet man nur den Modulnamen, so muss man nachher auf den Inhalt mit
Modulname.
zugreifen. - Beim Import mit dem * hat man alles zur Verfügung und kann ohne Modulname auf die Funktinen und Variablen zugreifen, doch dies braucht nicht nur Speicherplatz, sondern es kann auch zu Konflikten kommen, wenn man mehrere Module importiert und diese Methoden mit gleichem Namen besitzen.
- Beim Import mit "as X" kann man auf den Inhalt des Moduls zugreifen, indem man X. verwendet. Dies kann bei langen Modulnamen sinnvoll sein, doch auch für Verwirrung sorgen, wenn man im Nachhinein zuerst wieder herausfinden muss, von welchem Modul diese Methode ist.
Alle existierenden Module findet man auf Python Dokumentation
Zufallselemente
from random import randint random.randint(1,6)
Es wird eine int-Zahl zwischen den Bereichen generiert, wobei die Randzahlen eingeschlossen sind.
from random import choice random.choice(["a", "b", "c"])
Aus der Liste wird ein zufälliges Element zurückgegeben.
Webserver (bottle)
Es handelt sich um einen kleinen Webserver, der über eine Bibliothek eingebunden wird. Er kann mit entsprechender Konfiguration auch https verarbeiten. Es gibt auch ein entsprechendes deutsches Wiki.
Installaton
sudo apt-get install python3-bottle
Einbindung in ein Programm
from bottle import route, run, template # Einbindung der notwendigen bottle-Bibliotheken from datetime import datetime @route('/') # Script an den root des webservers binden (http://localhost/) def index(name = 'Zeit'): # Indes-Seite generieren mit dem Namen "Zeit" dt = datetime.now() # Aktuelles Zeit-Objekt holen time = "{:%d.%m.%Y %H:%M:%S}".format(dt) # Umwandeln in einen Datums- und Zeitstring. return template('<b>Datum:{{t}}</b>', t = time) # Über die template-Funktion das Datum ausgeben. # Um auf die Variable im html-Code zuzugreifen muss der Python-Code in doppelte geschweifte Klammern gepackt werden. run(host = '192.168.1.199', port = 80) # Starten mit Angabe des Hosts und des Ports. Mit debug = true kann man entsprechende Fehlerausgaben protokollieren.
Im Artikel zum Raspi wird eine Einbindung der GPIO in den Webserver aufgezeigt.
Python-GUI
Für Python gibt es unterdessen mehrere Bibliotheken, um ein GUI einzubinden. Neben tkinter sind dies unter anderem WxPython, PyQt und PySide, PyGTK, Kivy sowie PyFLTK.
tkinter
tkinter ermöglicht die grafische Darstellung von Python-Programmen durch Anbindung an tcl tk.
GPIO-Pin (LED) schalten
Folgendes Beispiel schaltet einen GPIO-Pin auf Grund einer Checkbox. Quellcode auf github:
from tkinter import * # gesamte Bibliothek einbinden. import RPi.GPIO as GPIO # Die RPi.GPIO eibinden und unter dem Namensraum GPIO zur Verfügung stellen. import time # Die time-Bibliothek einbinden. GPIO.setmode(GPIO.BCM) # BCM-Nummerierung verwenden. GPIO.setup(18, GPIO.OUT) # Pin 18 ansteuern. class App: # Die Klasse App eröffnen. def __init__(self, master): # Konstruktor erstellen. frame = Frame(master) # Ein Frame ist ein Fenster. Hier wird vom Hauptfenster geerbt. frame.pack() # Als Layoutmanager wird pack verwendet. Bei diesem werden die Elemente immer an den Rand der gewählten Richtung "gesetzt". self.check_var = BooleanVar() # Eine boolsche Variable definieren. check = Checkbutton(frame, text='Pin 18', command=self.update, variable=self.check_var, onvalue=True, offvalue=False) # Die Checkbox generieren. check.grid(row=1) # Die Box im Layoutmanager grid unterbringen. Bei diesem werden die Elemente in Spalten und Reihen angeordnet. def update(self): # Die update-Funktion bereitstellen GPIO.output(18, self.check_var.get()) # Den Pin auf Grund des Status der Checkbox setzen. root = tk() # tk-Objekt erstellen root.wm_title('On/Off Schalter') # Fenstertitel setzen app = App(root) # Die Elemente aus der App-Klasse im Fenster platzieren root.geometry("200x50+0+0") # Die Fenstergrösse setzen root.mainloop() # Endlosschlaufe
PWM mit Schieberegler steuern
Folgendes Beispiel steuert eine RGB-LED auf Grund von Schiebereglern. Quellcode auf github. Dort ist auch ein einfacher Beispiel mit einem Regler zu sehen. Standard-Befehle, die schon im obigen Beispiel kommentiert wurden, werden hier nicht mehr ausgeführt.
from Tkinter import * import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BCM) GPIO.setup(18, GPIO.OUT) GPIO.setup(23, GPIO.OUT) GPIO.setup(24, GPIO.OUT) pwmRed = GPIO.PWM(18, 500) pwmRed.start(100) pwmGreen = GPIO.PWM(23, 500) pwmGreen.start(100) pwmBlue = GPIO.PWM(24, 500) pwmBlue.start(100) class App: def __init__(self, master): frame = Frame(master) frame.pack() Label(frame, text='Red').grid(row=0, column=0) Label(frame, text='Green').grid(row=1, column=0) Label(frame, text='Blue').grid(row=2, column=0) scaleRed = Scale(frame, from_=0, to=100, orient=HORIZONTAL, command=self.updateRed) scaleRed.grid(row=0, column=1) scaleGreen = Scale(frame, from_=0, to=100, orient=HORIZONTAL, command=self.updateGreen) scaleGreen.grid(row=1, column=1) scaleBlue = Scale(frame, from_=0, to=100, orient=HORIZONTAL, command=self.updateBlue) scaleBlue.grid(row=2, column=1) def updateRed(self, duty): pwmRed.ChangeDutyCycle(float(duty)) def updateGreen(self, duty): pwmGreen.ChangeDutyCycle(float(duty)) def updateBlue(self, duty): pwmBlue.ChangeDutyCycle(float(duty)) root = Tk() root.wm_title('RGB LED Control') app = App(root) root.geometry("200x150+0+0") root.mainloop()
Das Beispiel kann natürlich auch für Gleichstrommotoren oder LED-Felder verwendet werden.
Werte aus Messung darstellen
Hier wird mit einem Ultraschallsensor die Distanz gemessen. Danach wird diese in einem Fenster dargestellt. Der Code für die Einlesung ist identisch.
from Tkinter import * import RPi.GPIO as GPIO import time trigger_pin = 18 echo_pin = 23 GPIO.setmode(GPIO.BCM) GPIO.setup(trigger_pin, GPIO.OUT) GPIO.setup(echo_pin, GPIO.IN) def send_trigger_pulse(): GPIO.output(trigger_pin, True) time.sleep(0.0001) GPIO.output(trigger_pin, False) def wait_for_echo(value, timeout): count = timeout while GPIO.input(echo_pin) != value and count > 0: count = count - 1 def get_distance(): send_trigger_pulse() wait_for_echo(True, 10000) start = time.time() wait_for_echo(False, 10000) finish = time.time() pulse_len = finish - start distance_cm = pulse_len / 0.000058 distance_in = distance_cm / 2.5 return (distance_cm, distance_in) class App: def __init__(self, master): self.master = master frame = Frame(master) frame.pack() label = Label(frame, text='Distance (inches)', font=("Helvetica", 32)) label.grid(row=0) self.reading_label = Label(frame, text='12.34', font=("Helvetica", 110)) self.reading_label.grid(row=1) self.update_reading() def update_reading(self): cm, inch = get_distance() reading_str = "{:.2f}".format(inch) self.reading_label.configure(text=reading_str) self.master.after(500, self.update_reading) root = Tk() root.wm_title('Range Finder') app = App(root) root.geometry("400x300+0+0") root.mainloop()
Interrupts
Damit man bei Abfragen nicht immer eine Schleife bemühen muss und je nachdem wichtige Ereignisse verpasst, kann man sich auch einfach benachrichtigen lassen. Dies geschieht durch Einbinden der Interrupt-Behandlung.
import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BCM) def my_callback(channel): print('Taster wurde gedrückt!') GPIO.setup(18, GPIO.IN, pull_up_down = GPIO.PUD_UP) # Pin 18 initialisieren GPIO.add_event_detect(18, GPIO.FALLING, callback = my_callback, bouncetime = 100) # Bei fallender Flanke wird die Routine aufgerufen. Bei einem internen Pull-Up-Widerstand ist dies beim Drücken der Fall. Bei einem Pull-Down wäre es Rising. Möchte man erst aufs loslassen der Taste reagieren, müsste man hier RISING setzen. Der Parameter bouncetime ist optional und verhindert die Weitergabe von zusätzlichen Interrupts, um etwa ein Tastenprellen zu unterbinden. Der Wert wird in Millisekunden angegeben. i = 0 while True: i = i + 1 print(i) time.sleep(1) # Immer eine Sekunde Pause machen. Ohne Interrupt würden Tastendrücke in dieser Zeit "ignoriert"