|
|
|
|
|
|
|
|
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 befand. 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 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 in dem 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: ü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 folgende 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 (siehe 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 überschrieben
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 überschrieben
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.pyw gibt es die Methode einstellungen(), die ein Fenster für die Einstellungen öffnet, beispielsweise für die Einstellung der TCP/IP-Adresse des Servers)