Ein Popup-Menü öffnet sich, wenn man beispielsweise in einem Text-Widget auf die rechte oder linke Maustaste drückt. Wichtig: Das Menü öffnet sich gerade an der Stelle, an der sich der Mauszeiger befindet. Dies bedeutet: Dem Popup- Menü muss irgendwie mitgeteilt werden, an welcher Position es sich öffnen soll. Ein Beispiel (siehe popup.py):
from tkinter import * ## Tkinter importieren
## Eventhandler, liefert Maus-Koordinaten
def popupMenu(event):
popup.post(event.x_root, event.y_root)
root = Tk() ## Toplevel-Widget
foben = Frame(root,width=500) ## Frame
foben.pack(expand=YES, fill=BOTH)
textfenster = Text(foben,width=90) ## Textfenster
textfenster.pack(fill=BOTH,expand=YES)
popup = Menu(foben,tearoff=0) ## Popup-Menue
popup.add_command(label='Info', command=info)
popup.add_separator()
popup.add_command(label='Beenden', command=ende)
## Maustaste an Event-Methode binden
textfenster.bind('<Button-3>',popupMenu)
Ergebnis:
Das Popup-Menü wird wie ein ganz normales Menü vereinbart: popup = Menu(foben,tearoff=0)
foben
ist der Rahmen (allgemein: das Widget), dem das Menü zugeordnet ist (anschaulich: das Widget, in dem das Menü erscheint).
Der Paramater tearoff
bestimmt darüber, ob das Menü ein sogenanntes abreissbares Menü (tearoff=1
) ist oder nicht (tearoff=0
). Ein abreissbares Menü kann von seiner momentanen Position "abgerissen" werden, es liegt dann als selbständiges Tk-Fenster auf dem Desktop! Diese Voreinstellung ist bei Popup-Menüs nicht sinnvoll.
Mit der Methode add_command()
kann ein Eintrag zum Menü hinzugefügt werden. Die Eigenschaft label
legt die Beschriftung des Menü-Eintrags fest, die zugeordnete Aktion wird über command
bestimmt.
Für die Trennungslinie zwischen den Einträgen ist die Methode add_separator()
verantwortlich.
Die rechte Maustaste wird an die Methode popupMenu(event)
gebunden, sie liefert die Mauskoordinaten zurück, die zur Anzeige des Popup-Menüs an der betreffenden Stelle benötigt werden.
Dialog-Fenster sind überraschend schwer zu programmieren! Warum ist das so? Erstens hat man es mit zwei Fenstern zu tun, wobei das Schließen des Dialog-Fensters nicht gleich die ganze Tkinter-Anwendung beenden darf. Zweitens findet normalerweise irgendein Datenaustausch zwischen dem Dialog-Fenster und dem Tkinter-Skript statt, man braucht also (Tkinter-)Variablen, die dann nach Beenden des Dialogs ausgelesen werden. Beispiel:
Dieser Dialog wird aufgerufen im Skript dialog.py. Wir betreten hier endgültig das weite Feld der objektorientierten Programmierung: Fenster und Buttons sind eben Objekte mit bestimmten Eigenschaften und Verhaltensweisen, sprich Methoden. Man fasst das Verhalten dieser Objekte in Klassen zusammen, und eine konkrete Instanz dieser Klasse ergibt dann ein Objekt (dieser Klasse). Beispiel: Es gibt die Text-Widget-Klasse, und ein konkretes Textfenster vereinbaren wir so:
textfenster = Text()
textfenster
ist hier der Name des Objektes, über den wir Zugriff auf die
Eigenschaften und Methoden der Klasse Text()
haben. Nichts anderes haben wir
oben in Teil 3 über Textfenster gemacht. Mit Klassen ist meist ein Modell der
Vererbung verbunden. Dies bedeutet, dass Klassen Eigenschaften und Methoden
vererben können, die man dann in den Subklassen verändern (Man sagt auch:
überschreiben) oder ergänzen kann. Klassen bieten damit ein sehr flexibles
Konzept zur Erweiterung der Fensterbibliothek! Eine Erweiterung der Klasse Dialog
ist
die Klasse simpledialog
, dieses Modul gehört zum Standardumfang von Python's Tkinter Bibliothek.
Wir überschreiben in unserer Anwendung die folgenden beiden Methoden der Dialog-Klasse:
body(self, master)
apply(self)
Der Parameter master
bezeichnet beim Aufruf das Objekt, dem unser
Dialogfenster zugeordnet ist. In unseren Beispielen war das entweder root
oder ein Frame-Widget.
Der Parameter self
hat eine besondere Bedeutung:
Wenn ich eine Instanz einer Klasse gebildet habe und auf die Eigenschaften des
Objektes zugreifen möchte, so wird self
durch den Objektnamen ersetzt.
Mit anderen Worten: self
bezeichnet die konkrete Instanz einer Klasse,
also das Objekt, das ins "Leben" gerufen wurde; im Beispiel oben war dies
textfenster
.
In der body
-Methode bestimmen wir das Aussehen des
Dialogfensters, also ob es ein Label oder eine Eingabezeile oder
beispielsweise Checkbuttons haben soll.
Die Methode apply
wird beim Klick
auf den Ok
-Button ausgeführt, sie sorgt im Beispiel für das Auslesen der
Eingabezeile und speichert deren Inhalt in einer Variablen. Außerdem wird in
der Methode eine Flag-Variable result
gesetzt, die anzeigt: alles ist gut
gegangen! Hier die Klasse DialogFenster
, abgeleitet aus der Klasse simpledialog.Dialog
(→ dialog.py):
from tkinter import simpledialog
class DialogFenster(simpledialog.Dialog):
## so leitet man in Python aus einer Klasse ab!
## Aufruf: NamenDialog = DialogFenster(root)
def body(self, master): ## wird ueberschrieben
self.title('Login?')
self.namen = Label(master, text='Name: ')
self.namen.pack(side=LEFT)
self.e1 = Entry(master,width=18)
self.e1.insert('0','Tommy')
self.e1.pack(side=LEFT)
return self.e1 # Fokus auf die Eingabezeile setzen
def apply(self): ## wird ueberschrieben
self.a1 = self.e1.get()
self.result = 1 ## alles ok!
Die Klassendefinition DialogFenster
"steht", wie aber wird sie benutzt? Dazu
muss ein Objekt ins "Leben" gerufen werden. Im Skript dialog.py gibt es
hierzu die Methode benutzer()
, die als Aktion einem Eintrag im Popup-Menü
zugeordnet ist (command=benutzer
). In der Methode benutzer()
wird zuerst
unser Dialogfenster ins "Leben" gerufen, dann wird die Flag-Variable result
getestet, und schließlich wird der Inhalt der Eingabezeile des Dialogfensters
im Textfenster (& in einer Message-Box) ausgegeben:
def benutzer():
NamenDialog = DialogFenster(root)
if NamenDialog.result <> None:
textfenster.insert(END,'Hallo, ' + NamenDialog.a1 + '!\n')
messagebox.showinfo('Dialog-Fenster','Hallo, ' + NamenDialog.a1 + '!')
popup.py
um einen weiteren Eintrag mit entsprechender callback
-Methode (siehe den Abschnitt
über's Binding). popup.py
ist eine Python-Test-Version eingebaut, sie testet um welche Python-Version es sich handelt. Wo befindet sich dieser Programm-Teil, und weshalb ist er notwendig? dialog.py
(Dieses Skript enthält keine Lösung der Aufgabe, du kannst aber im Chat-Client nachschauen: im Skript chat_client.py
gibt es die Methode einstellungen()
, die ein Fenster für die Einstellungen öffnet, beispielsweise für die Einstellung der TCP/IP-Adresse des Servers) → sp, 2023-07-28