Menübreite anpassen

Fragen zu Tkinter.
Zuzu_Typ

Der folgende Ausschnitt aus einem Programm erzeugt ein Fenster mit einem Menü, welches im Reiter "File" einige Untermenüs mit acceleratorn darstellen, stattdessen werden die accelerator nur halb und direkt an den Untermenüs dargestellt. ( Bild )

Ich nutze übrigens Python x64 version 3.5.0:374f501f4567

Code: Alles auswählen

from tkinter import *
class Main:
    def __init__(self):
        self.root = Tk()
        self.root.geometry("800x600")
        self.root.title("Titel")
        self.menus = {}
        self.menus["main"] = Menu(self.root)

        def add_cascade(master, **kw):
            s = kw.get("label")
            i = s.find('_')
            if i >= 0:
                s = s[:i] + s[i+1:]

            kw["label"] = s
            kw["underline"] = i
            master.add_cascade(**kw)

        #File
        self.menus["file"] = Menu(self.menus["main"], tearoff=0)
        add_cascade(self.menus['main'], label="File", menu=self.menus["file"])
        add_cascade(self.menus["file"], label="New", accelerator="Strg+N")
        add_cascade(self.menus["file"], label="Open", accelerator="Strg+O")
        add_cascade(self.menus["file"], label="Recent")
        add_cascade(self.menus["file"], label="Save", accelerator="Strg+S")
        add_cascade(self.menus["file"], label="Save as", accelerator="Strg+Umschalt+S")
        
        self.menus["edit"] = Menu(self.menus["main"],tearoff=0)
        self.menus["selection"] = Menu(self.menus["main"],tearoff=0)
        self.root.config(menu=self.menus["main"])
        self.root.mainloop()

Main()
Lässt sich das irgendwie richten?

Vielen Dank für eure Hilfe!
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ich weiss nicht, was ich machen muss, damit ich die Acceleratoren sehe. Ich sehe bei mir nur die Menüeeinträge und die werden richtig dargestellt.
Zuzu_Typ

Eigentlich nichts, welche Version nutzt du?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Also, ich benutze tcl/tk 8.5.18 aus Source compiliert

Es kann natürlich auch an meiner Linux Konfiguration liege, denn ich bekomme nicht alle Maus Cursors richtig und bei den Fonts nur TkFixedFont richtig. TkDefautlfont wird genauso angezeigt und andere Schriften nur mit einem groben Pixelmuster.
Zuzu_Typ

Achso, ne, dann liegt es daran, dass das Windows spezifisch ist (wenn ich mich richtig erinnere)
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo @Zuzu_Typ:

Hast Du für das von Dir angesprochene Problem mit der falschen Breite für Untermenüs mit Acceleratoren eine Lösung gefunden?

Bei mir sieht es genau so aus, wie bei Dir.

MfG, kodela
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo,

hier einmal ein anderer Ansatz, der zumindest zeigt, dass Python Untermenüs mit Acceleratoren sehr wohl richtig anzeigen kann:

Code: Alles auswählen

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import Tkinter as tk

def hallo():
    print ("Hallo!")

def klick():
    print ("Das war ein Klick!")

def main():   
    root = tk.Tk()
    root.title("Erstes Fenster")
    root.geometry("300x200")

    menu = tk.Menu(root)
    filemenu = tk.Menu(menu, tearoff=0)
    filemenu.add_command(label="Öffnen", accelerator="Strg+O", command=hallo)
    filemenu.add_command(label="Speichern", command=klick)
    filemenu.add_separator()
    filemenu.add_command(label="Beenden", command=root.quit)
    menu.add_cascade(label="Datei", menu=filemenu)

    editmenu = tk.Menu(menu, tearoff=0)
    editmenu.add_command(label="Wiedergabe", command=klick)
    editmenu.add_command(label="Pause", command=klick)
    editmenu.add_command(label="Einfügen", command=hallo)
    menu.add_cascade(label="Bearbeiten", menu=editmenu)

    helpmenu = tk.Menu(menu, tearoff=0)
    helpmenu.add_command(label="Über", command=klick)
    menu.add_cascade(label="Hilfe", menu=helpmenu)
    
    root.config(menu=menu)
    root.mainloop()
    
if __name__ == '__main__':
    main()
Hier nur für Datei Öffnen eine Kurztaste mit aufgenommen, die richtig angezeigt wird, aber leider nicht funktioniert. So weit ich das bisher herausgefunden habe, müssen Kurztasten separat eingebunden werden.

MfG, kodela
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ja das geht: Accelerator bei command wird bei mir angezeigt, accelerator bei cascade, wie bei Zuzu_Typ aber bei mir nicht
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hier eine Fassung mit funktionierender Kurztaste:

Code: Alles auswählen

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import Tkinter as tk

def hallo():
    print ("Hallo!")

def klick():
    print ("Das war ein Klick!")

def key(event):
    if event.keycode == 79:
        hallo()

def main():   

    frame = tk.Tk()
    frame.title("Erstes Fenster")
    frame.bind("<Key>", key)
    frame.geometry("300x200")

    menu = tk.Menu(frame)
    filemenu = tk.Menu(menu, tearoff=0)
    filemenu.add_command(label="Öffnen", accelerator="Strg+O", command=hallo)
    filemenu.add_command(label="Speichern", command=klick)
    filemenu.add_separator()
    filemenu.add_command(label="Beenden", command=frame.quit)
    menu.add_cascade(label="Datei", menu=filemenu)

    editmenu = tk.Menu(menu, tearoff=0)
    editmenu.add_command(label="Wiedergabe", command=klick)
    editmenu.add_command(label="Pause", command=klick)
    editmenu.add_command(label="Einfügen", command=hallo)
    menu.add_cascade(label="Bearbeiten", menu=editmenu)

    helpmenu = tk.Menu(menu, tearoff=0)
    helpmenu.add_command(label="Über", command=klick)
    menu.add_cascade(label="Hilfe", menu=helpmenu)
    
    frame.config(menu=menu)
    frame.mainloop()
    
if __name__ == '__main__':
    main()
Das ist weder die einzige noch die beste Lösung. 79 ist zumindest unter Windows der Keycode für Strg+O, aber auch für Strg+Shift+O.

MfG, kodela
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo,

die if-Abfrage in Zeile 13 muss lauten:

Code: Alles auswählen

if event.state == 4 and event.keysym == 'o':
MfG, kodela
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo,

hier noch einmal das Beispiel mit einigen Erweiterungen:

Code: Alles auswählen

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import Tkinter as tk


def oeffnen():
    print 'Es soll geöffnet werden.'

def speichern():
    print 'Es soll gespeichert werden.'
    
def speichern_unter():
    print 'Es soll unter neuem Namen gespeichert werden.'

def hallo():
    print ("Hallo!")

def klick():
    print ("Das war ein Klick!")

def key(event):
    if event.state == 4 and event.keysym == 'o':
        oeffnen()
    elif event.state == 4 and event.keysym == 's':
        speichern()
    elif event.state == 5 and event.keysym == 'S':
        speichern_unter()


def main():   

    frame = tk.Tk()
    frame.title("Erstes Fenster")
    frame.bind("<Key>", key)
    frame.geometry("500x300")

    menu = tk.Menu(frame)
    filemenu = tk.Menu(menu, tearoff=0)
    filemenu.add_command(label="Öffnen . . .", \
            accelerator="Strg+O", command=oeffnen)
    filemenu.add_command(label="Speichern . . .", \
            accelerator="Strg+S", command=speichern)
    filemenu.add_command(label="Speichern unter . . .\t\t\t", \
            accelerator="Strg+Shift+S", command=speichern_unter)
    filemenu.add_separator()
    filemenu.add_command(label="Beenden", command=frame.quit)
    menu.add_cascade(label="Datei", menu=filemenu)

    editmenu = tk.Menu(menu, tearoff=0)
    editmenu.add_command(label="Wiedergabe", command=klick)
    editmenu.add_command(label="Pause", command=hallo)
    editmenu.add_command(label="Einfügen", command=klick)
    menu.add_cascade(label="Bearbeiten", menu=editmenu)

    helpmenu = tk.Menu(menu, tearoff=0)
    helpmenu.add_command(label="Über", command=hallo)
    menu.add_cascade(label="Hilfe", menu=helpmenu)
    
    frame.config(menu=menu)
    frame.mainloop()
    
if __name__ == '__main__':
    main()
Da es dem Themenersteller um die Breite des Menüs ging und Tkinter diese nach dem längsten Eintrag ausrichtet, habe ich diesem noch einige Tabzeichen hinzugefügt, damit der Abstand zwischen dem Text und dem Accelerator-Eintrag etwas größer wird.

Ich weise darauf hin, dass immer dann, wenn die Shifttaste nicht mit zu drücken ist, der Buchstabe als kleiner einzutragen ist, andernfalls als großer.

Ein Problem ist die Feststelltaste, wenn diese gesetzt ist, funktioniert die Strg-Taste alleine mit einem Buchstaben nicht. Ich bin mir auch nicht sicher, ob die Funktionen für die Auswertung der Menüoptionen inner- oder außerhalb der Main-Funktion stehen sollten. Auch die Konstruktion "menu=menu" in frame.config() gibt mir Rätsel auf.

Ich gehe davon aus, dass die Spezialisten hier im Forum rettend Aufklärung betreiben werden und sage jetzt schon einmal danke dafür.

MfG, kodela
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo,

der Key-Eventhandler muss in den Main-Block, also etwa so:

Code: Alles auswählen

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import Tkinter as tk


def oeffnen():
    print 'Es soll geöffnet werden.'

def speichern():
    print 'Es soll gespeichert werden.'
    
def speichern_unter():
    print 'Es soll unter neuem Namen gespeichert werden.'

def hallo():
    print ('Hallo!')

def klick():
    print ('Das war ein Klick!')


def main():   

    frame = tk.Tk()
    frame.title('Erstes Fenster')
    frame.geometry('500x300')

    def key(event):
        # nur Strg + Zeichentaste
        if event.state == 4:
            if event.keysym == 'o':
                oeffnen()
            elif event.keysym == 's':
                speichern()
            elif event.keysym == 'q':
                frame.destroy()
        # Strg + Shift + Zeichentaste
        elif event.state == 5:
            if event.keysym == 'S':
                speichern_unter()

    frame.bind('<Key>', key)
    
    menu = tk.Menu(frame)
    filemenu = tk.Menu(menu, tearoff=0)
    filemenu.add_command(label='Öffnen . . .', \
            accelerator='Strg+O', command=oeffnen)
    filemenu.add_command(label='Speichern . . .', \
            accelerator='Strg+S', command=speichern)
    filemenu.add_command(label='Speichern unter . . .\t\t\t', \
            accelerator='Strg+Shift+S', command=speichern_unter)
    filemenu.add_separator()
    filemenu.add_command(label='Beenden', \
            accelerator='Strg+Q', command=frame.quit)
    menu.add_cascade(label='Datei', menu=filemenu)

    editmenu = tk.Menu(menu, tearoff=0)
    editmenu.add_command(label='Wiedergabe', command=klick)
    editmenu.add_command(label='Pause', command=hallo)
    editmenu.add_command(label='Einfügen', command=klick)
    menu.add_cascade(label='Bearbeiten', menu=editmenu)

    helpmenu = tk.Menu(menu, tearoff=0)
    helpmenu.add_command(label='Über', command=hallo)
    menu.add_cascade(label='Hilfe', menu=helpmenu)
    
    frame.config(menu=menu)
    frame.mainloop()
    
if __name__ == '__main__':
    main()
MfG, kodela
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@kodela: warum sollte der Key-Eventhandler in den main-Block. Dort hat er nichts zu suchen. Was für Rätsel meinst Du mit config? config wird mit Keyword-Argumenten aufgerufen und setzt eben das Menü des Frames.
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo @Sirius3:

Danke für Deine Hinweise.

Den Key-Eventhandler habe ich in den main-Block verschoben, weil ich darin die einzige Möglichkeit sah, über 'frame.destroy()' das Programm zu beenden. Du kennst sicher eine Möglichkeit, dies besser zu lösen. Mir fällt allerdings im Augenblick dazu nichts ein.

Mit der Konstruktion "menu=menu" in frame.config() ist der Code in Zeile 68 gemeint. Hier wird beim Aufruf von frame.config() der Ausdruck 'menu=menu' übergeben. Diesen Ausdruck habe ich von einem Beispielcode übernommen, ich denke, es war aus der Dokumentation zu Tkinter. Code, den ich übernehme, will ich auch verstehen. Das ist mir mit diesem Ausdruck bisher aber nicht gelungen. In meinem schlichten Denken kann sich ein Wert für eine Variable, hier der für 'menu', nicht verändern, wenn man ihr ihren eigenen Wert zuweist. Das muss aber anders sein, denn wenn man mit 'frame.config()' nur 'menu' übergibt, kommt es zu einem Fehler.

Was ich für dieses Beispiel auch noch nicht gelöst habe, ist das Verhalten von Shortkeys bei aktivierter Großschreibung. Ich denke, das bekomme ich aber selbst hin.

MfG, kodela
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@kodela: das ist keine Variabeln-Zuweisung. Das ist ein Key-Word-Argument. Ähnlich wie Du es bei add_command ja auch schon benutzt.
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo @Sirius3:

Danke für die Aufklärung. Zu Key-Word-Argumenten muss ich mich aber erst einmal schlau machen.

Hier habe ich den Key-Word-Handler, wie er auch mit aktivierter Umschalttaste funktioniert:

Code: Alles auswählen

def key(event):
        _state = event.state & ~2
        # nur Strg + Zeichentaste
        if _state == 4:
            if event.keysym.upper() == 'O':
                oeffnen()
            elif event.keysym.upper() == 'S':
                speichern()
            elif event.keysym.upper() == 'Q':
                frame.destroy()
        # Strg + Shift + Zeichentaste
        elif _state == 5:
            if event.keysym.upper() == 'S:
                speichern_unter()
Wie mache ich es aber, dass dieser Handler außerhalb des main-Blockes funktioniert?

MfG, kodela
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Indem du GUIs ueber Klassen programmierst, wie man das tun sollte. Einen anderen und sauberen Weg gibt es nicht, um `frame` zu kennen.

Zu den Schluesselwortargumenten: Nach deiner Logik haettest du dieses Namensproblem auch mit normalen Argumenten bzw das `event` deines Handlers waere ausserhalb zugaenglich.
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

@cofi:
Danke, dann werde ich alles in eine Klasse packen.
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

@cofi:

Dies ist mein Versuch, die GUI in eine Klasse zu packen:

Code: Alles auswählen

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import Tkinter as tk


def oeffnen():
    app.eingabe.delete(0, tk.END)
    app.eingabe.insert(0,'Es soll geöffnet werden.')

def speichern():
    app.eingabe.delete(0, tk.END)
    app.eingabe.insert(-1, 'Es soll gespeichert werden.')
    
def speichern_unter():
    app.eingabe.delete(0, tk.END)
    app.eingabe.insert(-1, 'Es soll unter neuem Namen gespeichert werden.')

def hallo():
    app.eingabe.delete(0, tk.END)
    app.eingabe.insert(-1, 'Hallo!')

def klick():
    app.eingabe.delete(0, tk.END)
    app.eingabe.insert(-1, 'Das war ein Klick!')



class GUI_Interface:
    
    def __init__(self, tk):
        
        self.frame = tk.Tk()
        self.frame.title('Erstes Fenster')
        self.frame.geometry('300x130')    
    
        menu = tk.Menu(self.frame)
        filemenu = tk.Menu(menu, tearoff=0)
        filemenu.add_command(label='Öffnen . . .', \
                accelerator='Strg+O', command=oeffnen)
        filemenu.add_command(label='Speichern . . .', \
                accelerator='Strg+S', command=speichern)
        filemenu.add_command(label='Speichern unter . . .\t\t\t', \
                accelerator='Strg+Shift+S', command=speichern_unter)
        filemenu.add_separator()
        filemenu.add_command(label='Beenden', \
                accelerator='Strg+Q', command=self.frame.quit)
        menu.add_cascade(label='Datei', menu=filemenu)

        editmenu = tk.Menu(menu, tearoff=0)
        editmenu.add_command(label='Wiedergabe', command=klick)
        editmenu.add_command(label='Pause', command=hallo)
        editmenu.add_command(label='Einfügen', command=klick)
        menu.add_cascade(label='Bearbeiten', menu=editmenu)

        helpmenu = tk.Menu(menu, tearoff=0)
        helpmenu.add_command(label='Über', command=hallo)
        menu.add_cascade(label='Hilfe', menu=helpmenu)

        self.frame.config(menu=menu)

        self.panel = tk.Frame(self.frame)

        self.eingabe = tk.Entry(self.panel)
        self.eingabe.insert(0,'Hallo Welt!')
        self.eingabe.pack(fill=tk.X, padx=15, pady=20)
                
        self.b_clear = tk.Button(self.panel, text="Löschen", 
                      width=12, height=1, command=self.clear_text)
        self.b_clear.pack(side="left", padx=20, pady=10)

        self.b_quit = tk.Button(self.panel, text="Beenden", 
                      width=12, height=1, command=self.frame.quit)
        self.b_quit.pack(side="right", padx=20, pady=10)
        
        self.panel.pack(fill=tk.X)
        
        def key(event):
            _state = event.state & ~10
            # nur Strg + Zeichentaste
            if _state == 4:
                if event.keysym.upper() == 'O':
                    oeffnen()
                elif event.keysym.upper() == 'S':
                    speichern()
                elif event.keysym.upper() == 'Q':
                    self.frame.destroy()
            # Strg + Shift + Zeichentaste
            elif _state == 5:
                if event.keysym.upper() == 'S':
                    speichern_unter()
        
        self.frame.bind('<Key>', key)
        
    def clear_text(self):
      self.eingabe.delete(0,tk.END)


app = GUI_Interface(tk)

def main():   
    
    app.frame.mainloop()


if __name__ == '__main__':
    main()
Da bleiben natürlich noch eine Reihe von Fragen offen, aber das sehe ich doch einmal als eine Basis an, auf der man aufbauen kann. Im Bezeichner der Klasse ist natürlich "Interface" zweimal enthalten. Das ist mir erst jetzt aufgefallen.

MfG, kodela
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@kodela: Zeile 99 gehört nach Zeile 102 und dann schau mal, was alles nicht mehr tut. key muß eine Methode der Klasse sein, oeffnen, speichern ... auch. Bei clear_text hast Du ja gezeigt, dass Du's kannst.
Antworten