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)
Dabei ist foben
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 abreißbares Menü (tearoff=1
) ist oder nicht (tearoff=0
). Ein
abreißbares Menü kann von seiner momentanen Position "abgerissen" werden, es
liegt dann als selbstandiges 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 popupMenü(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 gemacht. Mit Klassen ist meist ein Modell der
Vererbung verbunden: das heisst, 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! Im Fall der Dialogfenster
machen wir es genau so, wie Fredrik Lundh in seiner Einführung in
Tkinter: F. Lundh hat das Modul tkSimpleDialog
mit der Klasse Dialog
geschrieben,
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 Dialog
(→ dialog.py):
import tkSimpleDialog
class DialogFenster(tkSimpleDialog.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')
tkMessageBox.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 sar_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, 2016-12-02