Grafische Programme mit Tkinter

Elektronik-Labor  Projekte  Mikrocontroller  Raspberry     




Python-Programme schreiben Texte und Ergebnisse meist direkt in die Python Shell.  Oft lassen sich aber kleine grafische Anwendungen besser bedienen. Außerdem kann man Ausgaben einbauen, die immer am gleichen Ort stehen. Und schließlich lassen sich Programme bequem wieder beenden. In Python ist die grafische Programmierung mit Tkinter (für Tk-Interface) bereits enthalten. Man muss sie in das eigene Programm importieren und eine Instanz von Tk bilden, z.B. mit dem Namen root.  Das Fenster bekommt mit root.title einen eigenen Namen, in disem Fall "Output GPIO 4".

Mit Label kann ein Text eingefügt werden, mit Button eine aktive Schaltfläche. Es gibt unterschiedliche Methoden, wie die einzelnen Elemente im Fenster angeordnet werden können. Die einfachste heißt pack(). Dabei werden alle Elemente in der Reihenfolge des Quelltextes von oben nach unten mittig eingefügt. Das breiteste Element bestimmt die Fensterbreite.

root = Tk()
root.title("Output GPIO 4")
Label(root, text="------------------").pack()
lb = Label(root, text="high")
lb.pack()
Label(root, text="------------------").pack()
Button(root, text="high", width=40,command=an).pack()
Button(root, text='low',width=40, command=aus).pack()
anzeige(lb)
mainloop()


Ein Button kann eine Funktion aufrufen, die weiter oben im Programm steht. Dazu gibt es hier die beiden Funktionen an() und aus().

def an():
GPIO.output(4, 1) #Pin 4 einschalten

def aus():
GPIO.output(4, 0) #Pin 4 ausschalten

Für eine sich ändernde Ausgabe kann man ein Label verwenden, das immer wieder neu aufgerufen wird. Der erste Aufruf mit anzeige(lb) steht im Hauptprogramm. Die Funktion anzeige(lb) enthält dann eine weitere Funktion anz(), die auf das Label lb zugreifen kann. Mit lb.config kann man dann dem Label einen neuen Text zuweisen.

def anzeige(lb):
def anz():
if GPIO.input(4):
lb.config(text="GPIO 4 high")
else:
lb.config(text="GPIO 4 low")
lb.after(100, anz)
anz()

In anzeige(lb) wird die darin enthaltende Funktion anz() einmal aufgerufen. Sie liest den Zustand des GPIO 4 und zeigt ihn an. Interessant ist, dass der Port aus Ausgang initialisiert wurde, man seinen aktuellen Zustand aber jederzeit zurücklesen kann. Die Funktion anz ruft sich dann immer wieder selbst auf. lb.after(100, anz) ist eine Funktion des Labels lb und bildet einen Timer, der die angegebene Funktion nach einer eingestellten Wartezeit erneut aufruft. In diesem Fall wird das Label alle 100 ms erneuert und reagiert damit zeitnah auf Änderungen des Ausgangs.



Die grüne LED an GPIO 4 kann nun durch Anklicken der Schaltflächen beliebig ein- und ausgeschaltet werden. Der letzte Zustand bleibt mit dem Beenden des Programms stehen.



# Tk_GPIO4out.py Output GPIO 4
from Tkinter import *
import RPi.GPIO as GPIO #Bibliothek laden
GPIO.setmode(GPIO.BCM) #Broadcom-Modus
GPIO.setwarnings(False) #Warnungen abschalten
GPIO.setup(4, GPIO.OUT) #Pin 4 als Ausgang

def anzeige(lb):
if GPIO.input(4):
lb.config(text="GPIO 4 high")
else:
lb.config(text="GPIO 4 low")
lb.after(100, anzeige, lb)


def an():
GPIO.output(4, 1) #Pin 4 einschalten

def aus():
GPIO.output(4, 0) #Pin 4 ausschalten

root = Tk()
root.title("Output GPIO 4")
Label(root, text="------------------").pack()
lb = Label(root, text="high")
lb.pack()
Label(root, text="------------------").pack()
Button(root, text="high", width=40,command=an).pack()
Button(root, text='low',width=40, command=aus).pack()
anzeige(lb)
mainloop()


Ein Gegentakt-Blinker

Das folgende Tkinter-Programm realisiert einen Gegentaktblinker an  den Ports 4 und 17. Die Blinkgeschwindigkeit ist umschaltbar. Die Port-Umschaltung wird diesmal mit in der Anzeige-Funktion anz() ausgeführt und verwendet das Timing  der Funktion lb.after(t, anz).  Der letzte Zustand ist in der globalen Variablen d gespeichert. Sie muss sowohl im Hauptprogramm als auch in der Funktion als global deklariert werden, in der sie verändert werden soll.

Genauso ist auch die gewünschte Zeitverzögerung t als globale Variable deklariert. T kann nämlich in den Funktionen schnell() und langsam() neu zugewiesen werden. Auf diese Weise führt ein Klick auf eine der Schaltflächen zu einer neuen Verzögerungszeit in der Anzeige.


# Tk_GPIO4blink17.py Blinker an GPIO 4/17

from Tkinter import *
import RPi.GPIO as GPIO #Bibliothek laden
from time import sleep
GPIO.setmode(GPIO.BCM) #Broadcom-Modus
GPIO.setwarnings(False) #Warnungen abschalten
GPIO.setup(4, GPIO.OUT) #GPIO 4 Ausgang
GPIO.setup(17, GPIO.OUT) #GPIO 17 Ausgang

global d
d=0
global t
t=500

def anzeige(lb):
global d
global t
if (d==1):
d=0
lb.config(text="GPIO 17 an")
GPIO.output(4, 0) #GPIO 4 ausschalten
GPIO.output(17, 1) #GPIO 11 einschalten
else:
d=1
lb.config(text="GPIO 4 an")
GPIO.output(4, 1) #GPIO 4 einschalten
GPIO.output(17, 0) #GPIO 17 ausschalten
lb.after(t, anzeige, lb)

def schnell():
global t
t=200

def langsam():
global t
t=500

def on_closing():
GPIO.cleanup()
root.destroy()

root = Tk()
root.title("Wechsellinker GPIO 4/17")
Label(root, text="------------------").pack()
lb = Label(root, text="high")
lb.pack()
Label(root, text="------------------").pack()
Button(root, text="schnell", width=40,command=schnell).pack()
Button(root, text='langsam',width=40, command=langsam).pack()
anzeige(lb)
root.protocol("WM_DELETE_WINDOW", on_closing)
mainloop()



Um zu vermeiden, dass der letzte Portzustand nach dem Beenden des Programms erhalten bleibt, wird GPIO.cleanup() einegsetzt. Damit werden alle ebnutzten Ports des Programms wieder in den Grundzusatnd versetzt, also zu hochohmigen Eingängen.  die Funktion def on_closing() wird dazu beim Klick auf das Schließen-Symbol aufgerufen.

def on_closing():
GPIO.cleanup()
root.destroy()



Verbesserungen im Programmierstil  von Frank Behlich



(Bild aus Python 2.7.10 unter Windows)

Ich bin in Bereich der Elektronik ein Laie, doch für meine Arbeit (Betreuung von Hortkindern) habe ich schon einiges verwendet und auch mit Python Programme geschrieben (Software für Trickfilme, zum Portraitzeichnen mit Beamer etc.) Bin eigentlich auch in diesem Bereich Laie, doch habe ich einfach Spaß an der Sache und sehe es als anspruchsvollere Kreutzworträtsel. Hier habe ich einige Verbesserungen im Programmierstil vorgenommen. Bei mir läuft es, doch konnte ich es nicht mit dem Raspberry ausprobieren, da ich noch keine Platine angeschlossen habe. Die Zeilen für den Raspberry habe ich auskommentiert.

#gpio_high_low_button_1.py GPIO-Schalter
#! /usr/bin/env python
# -*- coding: utf-8

try:
import Tkinter as tk
except ImportError:
import tkinter as tk

#import RPi.GPIO as GPIO #Bibliothek laden
#GPIO.setmode(GPIO.BOARD) #Verdrahtung nach der Pin-Nummer
#GPIO.setwarnings(False) #Warnungen abschalten

PORT = (12)

class GPIO_on_off(tk.LabelFrame):
def __init__(self, root, port):
tk.LabelFrame.__init__(self, root, text = "{0} {1} {2}".format("GPIO",
port, "LOW"), highlightthickness = 11, font = "Arial 9")
self.root = root
self.port = port
#GPIO.setup(port, GPIO.OUT) #Pin als Ausgang
#GPIO.output(self.port, False) #Pin anschalten
self.button_state = False
self.button = tk.Button(self, width=4, bd=2, height=1,
highlightthickness=11, activebackground = "lightgreen",
bg="lightgreen", font="Arial 15", command=self.change_state)
self.button.pack()

def change_state(self):
if self.button_state:
#GPIO.output(self.port, False) #Pin auschalten
self.button.config(bg="lightgreen", relief="raised",
activebackground="lightgreen")
self.config(text="{0} {1} {2}".format("GPIO", self.port,
"LOW"))
self.button_state = False
else:
#GPIO.output(self.port, True) #Pin anschalten
self.button.config(bg="red", relief="sunken",
activebackground="red")
self.config(text="{0} {1} {2}".format("GPIO", self.port,
"HIGH"))
self.button_state = True

def on_exit(self):
#GPIO.cleanup() #Grundzustand herstellen
self.root.destroy()

if __name__ == '__main__':
root = tk.Tk()
gpio_button = GPIO_on_off(root, PORT)
gpio_button.pack()
root.protocol("WM_DELETE_WINDOW", gpio_button.on_exit)
root.resizable(0, 0)
root.mainloop()


GPIO-Schalter von Frank Behlich



Ausführliche Erläuterungen: GPIO_schalter.pdf

#gpio_high_low_button_4.py GPIO-Schalterleiste mit 4 Schaltern
#! /usr/bin/env python
# -*- coding: utf-8

try:
import Tkinter as tk
except ImportError:
import tkinter as tk

#import RPi.GPIO as GPIO #Bibliothek laden
#GPIO.setmode(GPIO.BOARD) #Verdrahtung nach der Pin-Nummer
#GPIO.setwarnings(False) #Warnungen abschalten

PORTS = (12, 16, 18, 22)

class GPIO_on_off(tk.LabelFrame):
def __init__(self, root, port):
tk.LabelFrame.__init__(self, root, text = "{0} {1} {2}".format("GPIO",
port, "LOW"), highlightthickness = 11, font = "Arial 9")
self.root = root
self.port = port
#GPIO.setup(port, GPIO.OUT) #Pin als Ausgang
#GPIO.output(self.port, False) #Pin anschalten
self.button_state = False
self.button = tk.Button(self, width=4, bd=2, height=1,
highlightthickness=11, activebackground = "lightgreen",
bg="lightgreen", font="Arial 15", command=self.change_state)
self.button.pack()

def change_state(self):
if self.button_state:
#GPIO.output(self.port, False) #Pin auschalten
self.button.config(bg="lightgreen", relief="raised",
activebackground="lightgreen")
self.config(text="{0} {1} {2}".format("GPIO", self.port,
"LOW"))
self.button_state = False
else:
#GPIO.output(self.port, True) #Pin anschalten
self.button.config(bg="red", relief="sunken",
activebackground="red")
self.config(text="{0} {1} {2}".format("GPIO", self.port,
"HIGH"))
self.button_state = True

def on_exit(self):
#GPIO.cleanup() #Grundzustand herstellen
self.root.destroy()

if __name__ == '__main__':
root = tk.Tk()
tk.Label(root, font="Arial 15", bd=5,
text="-------------- >>> Meine Schalterleiste <<< -------------",
).pack()
for port in PORTS:
gpio_button = GPIO_on_off(root, port)
gpio_button.pack(side="left")
root.protocol("WM_DELETE_WINDOW", gpio_button.on_exit)
root.title("GPIO SCHALTERLEISTE")
root.resizable(0, 0)
root.mainloop()





GPIO-Lauflicht von Frank Behlich

Hier ein Lauflicht oder auch als Gegentakter verwendbar -->(gpio_push_pull = GPIO_Push_Pull(root))<-- mit Port 12 u. 16 und Zeit 200. Es können alle beliebigen Ports und Zeiten verwendet werden. Im Script -->(pio_push_pull = GPIO_Push_Pull(root, 150, [12, 16, 18, 22]))<--- sind es Port 12, 16, 18, 22 und eine Zeit von 150.


#gpio_push_pull.py --> Lauflicht
#! /usr/bin/env python
# -*- coding: utf-8

try:
import Tkinter as tk
except ImportError:
import tkinter as tk

#import RPi.GPIO as GPIO #Bibliothek laden
#GPIO.setmode(GPIO.BOARD) #Verdrahtung nach der Pin-Nummer
#GPIO.setwarnings(False) #Warnungen abschalten

PORTS = [12, 16]
SPEED = 200

class GPIO_Push_Pull(tk.LabelFrame):

def __init__(self, root, speed = SPEED, ports = PORTS):
tk.LabelFrame.__init__(self, root, text = "{0} {1}".format("GPIO",
"|".join(str(x) for x in ports)), highlightthickness = 8,
font = "Arial 9")
self.root = root
self.speed = speed
self.ports = ports
self.run = None
for port in ports:
#GPIO.setup(port, GPIO.OUT) #Pin als Ausgang
#GPIO.output(port, False) #Pin ausschalten
self.button = tk.Button(self, width=4, bd=2, bg="red",
highlightthickness = 11, activebackground="lightgreen",
height=1, font="Arial 15", command=self.start_stop)
self.button.pack()

def start_stop(self):
if self.run is None:
self.button.config(bg="red", relief="sunken",
activebackground = "red")
self.push_pull()
else:
self.button.config(bg="lightgreen", relief="raised",
activebackground = "lightgreen")
self.after_cancel(self.run)
self.run = None

def push_pull(self):
#GPIO.output(self.ports[0], True) #Pin anschalten
#GPIO.output(self.ports[-1], False) #Pin auschalten
self.ports.reverse()#dreht die Liste
new_port_off = self.ports.pop()#entfernt das letzte Element
self.ports.reverse()#dreht die Liste wieder
self.ports.append(new_port_off)#fuegt entferntes Element an
print self.ports
self.run = self.after(self.speed, self.push_pull)

def on_exit(self):
#GPIO.cleanup() #Grundzustand herstellen
self.root.destroy()

if __name__ == '__main__':
root = tk.Tk()
gpio_push_pull = GPIO_Push_Pull(root, 150, [12, 16, 18, 22])
root.protocol("WM_DELETE_WINDOW", gpio_push_pull.on_exit)
gpio_push_pull.pack()
root.resizable(0, 0)
root.mainloop()



Elektronik-Labor  Projekte  Mikrocontroller  Raspberry