Kontrollelemente | Python-Kurs | Python |
Ein Canvas bildet die Grundlage für die Grafik in Python Tkinter. Es ist ein Widget und kann darum wie ein normaler Button in ein TkInter-Fenster integriert werden.
Innerhalb eines Canvas werden Linien, Rechtecke oder Kreisausschnitte gezeichnet. Dabei werden die grafischen Elemente als Items behandelt, die auch nachdem sie gezeichnet wurden, manipuliert werden können.
Grafik-Primitive
- Linien zeichnen:
create_line(xStart, yStart, xEnde, yEnde) - Rechtecke zeichnen:
create_rectangle(xStart, yStart, xEnde, yEnde) - Kreise oder Ovale zeichnen: Die Größe des Ovals wird durch das
umgebende Rechteck festgelegt.
create_oval(xStart, yStart, xEnde, yEnde) - Text in die Grafik einzeichnen:
create_text(xStart, yStart, text="Der Text")
- fill = "farbe": Die Fläche wird eingefärbt.
- outline = "farbe": Die Außenlinie wird eingefärbt.
- dash = (laenge, luecke): Linie wird unterbrochen.
Ein Beispiel-Raster
Das folgende Canvas der Höhe 240 und der Breite 160 wird durch Linien in ein Raster von acht Spalten und zwölf Zeilen aufgeteilt.import tkinter, sys fenster = tkinter.Tk() # Lege die Dimensionen fest breite = 160 hoehe = 240 spalten = 8 zeilen = 12 # Das Canvas wird im Fenster definiert anzeige = tkinter.Canvas(fenster, width=breite, height=hoehe) anzeige.pack() # Zunächst die senkrechten Linien. Der Abstand in xdiff xdiff = breite/spalten for x in range(1,spalten+1): anzeige.create_line(x*xdiff,0,x*xdiff, hoehe) # Nun die waagerechten Linien. Der Abstand in ydiff ydiff = hoehe/zeilen for y in range(1,zeilen+1): anzeige.create_line(0,y*ydiff,breite, y*ydiff) # Starte das Fenster und erzeuge alle Inhalte fenster.mainloop()
Mausereignis empfangen
Klickt der Benutzer mit der Maus in das Canvas ist das ein Ereignis. Das Programm kann dieses Ereignis durch den Aufruf von bind an sich binden. Dazu werden zwei Parameter übergeben.- Der erste Parameter bezeichnet das Ereignis. Der Klick mit der linken Maustaste wird durch die Zeichenkette "<Button-1>" gekennzeichnet.
- Der zweite Parameter enthält den Namen der Funktion, die aufgerufen werden soll, sobald das Ereignis eintritt. Diese Funktion muss einen Parameter haben.
Treffer mit Kreisen markieren
Die Callback-Funktion (hier klickCallback) erfährt in ihrem Parameter alle Details über das Ereignis. Der spannenste Wert ist natürlich, wo das Ereignis eingetroffen ist und der befindet sich in den Attributen x und y.def klickCallback(event): print(event.x) print(event.y) anzeige.create_oval(event.x-2, event.y-2,event.x+2,event.y+2) anzeige.bind("<Button-1>", klickCallback) # Starte das Fenster und erzeuge alle Inhalte fenster.mainloop()Zur Kontrolle wird der Punkt auf der Konsole ausgegeben und ein kleiner Kreis um den Einschlagort gemalt.
Getroffene Felder markieren
Sollen die Felder markiert werden, muss die Einschussstelle erst einmal durch die Breite der Spalten bzw. durch die Höhe der Zeilen geteilt werden. Dann werden sie in ganze Zahlen umgewandelt.Damit kann man nun das Rechteck berechnen, indem die Spaltenbreite bzw. die Zeilenhöhe wieder aufmultipliziert wird.
def klickCallback(event): x = int(event.x/xdiff) y = int(event.y/ydiff) anzeige.create_rectangle(x*xdiff,y*ydiff,(x+1)*xdiff, (y+1)*ydiff, fill="red") anzeige.bind("<Button-1>", klickCallback)Mit dem fill-Parameter wird das Rechteck rot gefüllt.
Alles zusammen
#!/usr/bin/python import tkinter, sys fenster = tkinter.Tk() # Lege die Dimensionen fest breite = 160 hoehe = 240 spalten = 8 zeilen = 12 # Das Canvas wird im Fenster definiert anzeige = tkinter.Canvas(fenster, width=breite, height=hoehe) anzeige.pack() # Zunächst die senkrechten Linien. Der Abstand in xdiff # Zunächst die senkrechten Linien. Der Abstand in xdiff xdiff = breite/spalten for x in range(1,spalten+1): anzeige.create_line(x*xdiff,0,x*xdiff, hoehe) # Nun die waagerechten Linien. Der Abstand in ydiff ydiff = hoehe/zeilen for y in range(1,zeilen+1): anzeige.create_line(0,y*ydiff,breite, y*ydiff) def klickCallback(event): print(event.x) print(event.y) x = int(event.x/xdiff) y = int(event.y/ydiff) anzeige.create_rectangle(x*xdiff,y*ydiff,(x+1)*xdiff, (y+1)*ydiff, fill="red") anzeige.bind("<Button-1>", klickCallback) # Starte das Fenster und erzeuge alle Inhalte fenster.mainloop()
Nach drei Klicks sieht das Programm dann so aus:
Resize des Canvas bei Fenservergrößerung
In vielen Fällen soll die Zeichnung innerhalb des Canvas beim Aufziehen des Fensters vergrößert bzw. beim Zusammenziehen verkleinert werden.Um dies zu erreichen, müssen folgende Schritte durchgeführt werden:
- Der Rahmen, in dem sich der Canvas befindet, muss im pack die Option expand=True erlauben und den Raum selbst füllen fill=tk.BOTH.
- Der Canvas selbst muss auf das Ereignis der Vergrößerung reagieren. Im Beispiel ist dazu die Klasse Canvas erweitert worden und im Konstruktor das Configure-Ereignis an die Methode on_resize weitergeleitet worden.
- In on_resize wird ein scale-Aufruf an alle Elemente gesandt werden. Zu diesem Zweck müssen alle Canvas-Elemente mit dem Aufruf addtag_all ein Tag erhalten, in diesem Fall ist es "all".
Hier das passende Listing:
import tkinter as tk class FlexCanvas(tk.Canvas): # FlexflexCanvas beerbt Canvas def __init__(self,parent,**kwargs): tk.Canvas.__init__(self,parent,**kwargs) self.bind("<Configure>", self.on_resize) # Merke Dir die Ausgangsgroesse self.height = self.winfo_reqheight() self.width = self.winfo_reqwidth() # EventHandler bei Resize def on_resize(self,event): # Berechne die eingetretene Vergrößerung wscale = event.width/self.width hscale = event.height/self.height # Merke Dir die jetzige Groesse self.width = event.width self.height = event.height # Setze die Groesse des Canvas um self.config(width=self.width, height=self.height) # Veraendere die Groesse aller Elemente des canvas self.scale("all", 0, 0, wscale, hscale) def paint(self): # Zeichne eine Olive self.create_oval(0, 0, self.width, self.height, fill="green") def main(): fenster = tk.Tk() rahmen = tk.Frame(fenster) rahmen.pack(fill=tk.BOTH, expand=True) flexCanvas = FlexCanvas(rahmen, bg="white", highlightthickness=0) flexCanvas.pack(fill=tk.BOTH, expand=True) flexCanvas.paint() # tag all of the drawn widgets flexCanvas.addtag_all("all") fenster.mainloop() if __name__ == "__main__": main()
Canvas-Methoden
- bbox(item): Die Methode liefert die Koordinaten des Rechtecks in Form eines Tupels, das einen Rahmen um das als Parameter übergebene Item spannt. Wird kein Parameter übergeben, wird das Rechteck herangezogen, das sich um alle Items spannt.
- find_all(): Die Methode liefert ein Tupel mit allen Items des Canvas-Widgets zurück.
- find_closest(): Die Methode sucht anhand der x- und y-Koordinate, die als Parameter übergeben werden, nach dem nächstgelegenen Item und liefert es zurück.
- scale(xscale, yscale, xoffset, yoffset):
Alle Items im Canvas-Widget können skaliert und verschoben werden.
Dazu verwendet die Methode vier Parameter:
- xscale: Die horizontale Vergrößerung
- yscale: Die vertikale Vergrößerung
- xoffset: Die Verschiebung in horizontaler Richtung
- yoffset: Die Verschiebung in vertikaler Richtung
- canvasx(): Die Methode erwartet als Parameter eine horizontale Fensterkoordinate, die sie als horizontale Koordinate des Canvas-Widgets zurückgibt.
- canvasy(): Die Methode erwartet als Parameter eine vertikale Fensterkoordinate, die sie als vertikale Koordinate des Canvas-Widgets zurückgibt.