Geführtes navigationsmenü in Python

Eine geführte Routinearbeit über eine grafische Oberfläche abwickeln zu lassen, ist in aller Regel für den, vor dem Bildschirm sitzenden, Benutzer meist nicht nur einfacher und übersichtlicher, sondern dazu noch weitaus augenfreundlicher als so manche textlastigen schwarz-weißen Bildschirmtexte.
Im speziellen meine ich hier das klassische Installations- oder Konfigurationsmenü, in dem man innerhalb eines Fensters beliebig durch die einzelnen Optionen vor und zurück blättern kann. Leider ist Python (tkinter) zur Lösung einer solchen Herausforderung nicht mit den passenden Widgets ausgestattet, weswegen man sich anderweitig behelfen muss.
Mit ein paar Tricks lässt sich jedoch auch hierfür eine Lösung erzielen.

Wie eingangs erwähnt, benutzen wir das GUI-Toolkit ‘tkinter’. Da ein solches Navigationsmenü in seinem Funktionsumfang beliebig erweitert werden könnte, folgt eine Auflistung, was das Ding im Nachhinein so alles kann und wie das Projekt realisiert wird:

  • Insgesamt 4 eigene grafische Fenster mit jeweils einer ‘Vor’ und einer ‘Zurück’-Taste (erste Seite enthält lediglich eine Taste)
  • Die Tasten ermöglichen es, beliebig zwischen den Fenstern zu wechseln (sequentiell, also in geordneter Reihenfolge)
  • Jede Seite stellt eigentlich ein eigenes Fenster dar
  • Der Wechsel zwischen den Seiten erfolgt nach einem entsprechendem Tastendruck wodurch ein neues Fenster geöffnet und das alte ausgeblendet wird
  • Wurde eine Seite bereits aufgerufen (und ausgeblendet), wird es wieder eingeblendet und die vorherige Seite ausgeblendet

Diese Methode mag zwar erst einmal wirr klingen, aber möglicherweise werden einige Unklarheiten nach einem Blick in den Code beseitigt:

#!/usr/bin/python3

from tkinter import *

def page1():
    global page1
    page1 = Tk()
    page1.attributes('-fullscreen', True)
    page1.title('Navigationsmenü Titel')

    def next_page():
        page1.withdraw()
        try:
            page2.state() == 'normal'
        except:
            page2()
        else:
            page2.deiconify()

    Label(page1, text="Erste Seite", bg='#429a76', fg='white', font=("Open Sans", 14)).pack(expand=True, fill=BOTH)

    Button(page1, text="Start", font=("Open Sans", 14), command=next_page).pack(ipady=10, fill='x', expand=TRUE, side=LEFT, anchor='s')

    page1.mainloop()

def page2():
    global page2
    page2 = Tk()
    page2.attributes('-fullscreen', True)
    page2.title('Navigationsmenü Titel')

    def next_page():
        page2.withdraw()
        try:
            page3.state() == 'normal'
        except:
            page3()
        else:
            page3.deiconify()

    def prev_page():
        page2.withdraw()
        page1.deiconify()

    Label(page2, text="Zweite Seite", padx=20, pady=20, bg='#429a76', fg='white', font=("Open Sans", 14)).pack(expand=True, fill=BOTH)

    Button(page2, text="Zurück", font=("Open Sans", 14), command=prev_page).pack(ipady=10, fill='x', expand=TRUE, side=LEFT, anchor='s')

    Button(page2, text="Weiter", font=("Open Sans", 14), command=next_page).pack(ipady=10, fill='x', expand=TRUE, side=LEFT, anchor='s')

    page2.mainloop()

def page3():
    global page3
    page3 = Tk()
    page3.attributes('-fullscreen', True)
    page3.title('Navigationsmenü Titel')

    def next_page():
        page3.withdraw()
        try:
            page4.state() == 'normal'
        except:
            page4()
        else:
            page4.deiconify()

    def prev_page():
        page3.withdraw()
        page2.deiconify()

    Label(page3, text="Dritte Seite", padx=20, pady=20, bg='#429a76', fg='white', font=("Open Sans", 14)).pack(expand=True, fill=BOTH)

    Button(page3, text="Zurück", font=("Open Sans", 14), command=prev_page).pack(ipady=10, fill='x', expand=TRUE, side=LEFT, anchor='s')

    Button(page3, text="Weiter", font=("Open Sans", 14), command=next_page).pack(ipady=10, fill='x', expand=TRUE, side=LEFT, anchor='s')

    page3.mainloop()

def page4():
    global page4
    page4 = Tk()
    page4.attributes('-fullscreen', True)
    page4.title('Navigationsmenü Titel')

    def next_page():
        page1.destroy()
        page2.destroy()
        page3.destroy()
        page4.destroy()

    def prev_page():
        page4.withdraw()
        page3.deiconify()

    Label(page4, text="Vierte Seite", padx=20, pady=20, bg='#429a76', fg='white', font=("Open Sans", 14)).pack(expand=True, fill=BOTH)

    Button(page4, text="Zurück", font=("Open Sans", 14), command=prev_page).pack(ipady=10, fill='x', expand=TRUE, side=LEFT, anchor='s')

    Button(page4, text="Beenden", font=("Open Sans", 14), command=next_page).pack(ipady=10, fill='x', expand=TRUE, side=LEFT, anchor='s')

    page4.mainloop()

page1()

Wir erstellen uns vier Funktionen, je eine pro Seite für unsere Navigation die page1, page2, page3 und page4 heißen. Pro Funktion wird also eine komplette Seite aufgerufen. Auf den Seiten ist lediglich ein bisschen Text (hier können nach belieben weitere Funktionen oder anderer Krimkrams eingefügt werden) sowie zwei Schaltflächen.
Diese beiden Schaltflächen starten nach Betätigung je das nächste Fenster in entsprechender Reihenfolge und blenden das derzeit aktive aus. Sämtliche gestarteten Fenster bleiben also während der gesamten Laufzeit solange aktiv, bis das auf der vierten Seite die Schaltfläche ‘Beenden’ ausgelöst wurde, die übrigen bleiben ausgeblendet. Geht man innerhalb der Navigation zurück (und wieder vor), werden die zuvor gestarteten Fenster nicht nochmal gestartet, sondern abermals eingeblendet.
Die Fenster werden im Vollbildmodus angezeigt können aber auch in einer beliebigen Pixel Höhe und Breite anders ausgerichtet werden.

Hier folgt ein Beispiel wie ein ‘fertiges’-Menü mit einer Radio-Button Auswahlmöglichkeit, einem Eingabefeld und einem Benutzername-Passwort-Eingabefeld aussehen könnte. Die eingegebenen Werte werden über die Variablen ‘radio_selected’, ‘set_value’, ‘username’ und ‘password’ zur weiteren Verarbeitung übergeben:

from tkinter import *
import tkinter.messagebox
from tkinter import ttk
import threading
import os
import subprocess

bg_color = '#429a76'
fg_color = 'white'
fg_color_entry = 'black'
set_font = 'Open Sans'
size_headline = 20
size_font = 15


def page1():
    global page1
    page1 = Tk()
    page1.attributes('-fullscreen', True)
    page1.title('Navigationsmenü Titel')
    page1['bg'] = bg_color

    def next_page():
        page1.destroy()
        try:
            page2.state() == 'normal'
        except:
            page2()
        else:
            page2.deiconify()

    Label(page1, text="Willkommen zum Linux Einrichtungsassistenten", bg=bg_color, fg=fg_color, font=(set_font, size_headline)).pack(expand=True, fill=BOTH)

    Button(page1, text="Einrichtung beginnen", font=(set_font, size_font), command=next_page).pack(ipady=5, fill='x', expand=TRUE, side=LEFT, anchor='s')

    page1.mainloop()

def page2():
    global page2
    page2 = Tk()
    page2.attributes('-fullscreen', True)
    page2.title('Navigationsmenü Titel')
    page2['bg'] = bg_color

    def next_page():
        global radio_selected
        radio_selected = selected.get()

        page2.withdraw()

        try:
            page3.state() == 'normal'
        except:
            page3()
        else:
            page3.deiconify()

    def prev_page():
        page2.withdraw()
        page1.deiconify()

    Label(page2, text="Wähle einen Wert aus", pady=100, bg=bg_color, fg=fg_color, font=(set_font, size_headline)).pack(fill=X)

    separator = ttk.Separator(page2, orient='horizontal')
    separator.pack(fill='x')

    Label(page2, bg=bg_color, fg=fg_color, font=(set_font, size_font)).pack(ipady=100)

    selected = StringVar(master=page2)
    radio1 = Radiobutton(page2, text=" Linux Desktop ", variable=selected, value="desktop", indicatoron = 0, width = 20, font=(set_font, size_font)).pack(ipadx=100, ipady=10)
    radio2 = Radiobutton(page2, text=" Linux Server ", variable=selected, value="server", indicatoron = 0, width = 20, font=(set_font, size_font)).pack(ipadx=100, ipady=10)

    Button(page2, font=(set_font, size_font)).pack(ipady=5, fill='x', expand=TRUE, side=LEFT, anchor='s')

    Button(page2, text="Weiter", font=(set_font, size_font), command=next_page).pack(ipady=5, fill='x', expand=TRUE, side=LEFT, anchor='s')

    page2.mainloop()

def page3():
    global page3
    page3 = Tk()
    page3.attributes('-fullscreen', True)
    page3.title('Navigationsmenü Titel')
    page3['bg'] = bg_color

    def next_page():
        global set_value
        set_value = entry_host.get()
        page3.withdraw()

        try:
            page4.state() == 'normal'
        except:
            page4()
        else:
            page4.deiconify()

    def prev_page():
        page3.withdraw()
        page2.deiconify()

    Label(page3, text="Trage einen Wert ein", pady=100, bg=bg_color, fg=fg_color, font=(set_font, size_headline)).pack(fill=X)

    separator = ttk.Separator(page3, orient='horizontal')
    separator.pack(fill='x')

    Label(page3, bg=bg_color, fg=fg_color, font=(set_font, size_font)).pack(ipady=100)

    Label(page3, text="Wert", bg=bg_color, fg=fg_color, font=(set_font, size_font)).pack()
    entry_host = Entry(page3, bg='white', fg=fg_color_entry, font=(set_font, size_font))
    entry_host.pack(ipadx=100, ipady=5)

    Button(page3, text="Zurück", font=(set_font, size_font), command=prev_page).pack(ipady=5, fill='x', expand=TRUE, side=LEFT, anchor='s')

    Button(page3, text="Weiter", font=(set_font, size_font), command=next_page).pack(ipady=5, fill='x', expand=TRUE, side=LEFT, anchor='s')

    page3.mainloop()

def page4():
    global page4
    page4 = Tk()
    page4.attributes('-fullscreen', True)
    page4.title('Navigationsmenü Titel')
    page4['bg'] = bg_color

    def next_page():
        global username
        global password
        username = entry_user.get()
        password = entry_pass.get()

        page4.withdraw()
        try:
            page5.state() == 'normal'
        except:
            page5()
        else:
            if radio_selected == "desktop":
                radio_type_label.config(text="Ausgewählter Wert:      Linux Desktop")
            elif radio_selected == "server":
                radio_type_label.config(text="Ausgewählter Wert:      Linux Server")
            set_value_label.config(text="Eingegebener Wert:     " + set_value)
            user_label.config(text="Eingegebener Benutzername:     " + username)
            page5.deiconify()

    def prev_page():
        page4.withdraw()
        page3.deiconify()

    Label(page4, text="Gib die Benutzerinformationen ein", pady=100, bg=bg_color, fg=fg_color, font=(set_font, size_headline)).pack(fill=X)

    separator = ttk.Separator(page4, orient='horizontal')
    separator.pack(fill='x')

    Label(page4, bg=bg_color, fg=fg_color, font=(set_font, size_font)).pack(ipady=100)

    Label(page4, text="Benutzername", bg=bg_color, fg=fg_color, font=(set_font, size_font)).pack()
    entry_user = Entry(page4, bg='white', fg=fg_color_entry, font=(set_font, size_font))
    entry_user.pack(ipadx=100, ipady=5)

    Label(page4, bg=bg_color, fg=fg_color, font=(set_font, size_font)).pack()

    Label(page4, text="Passwort", bg=bg_color, fg=fg_color, font=(set_font, size_font)).pack()
    entry_pass = Entry(page4, bg="white", fg="black", show="*", font=(set_font, size_font))
    entry_pass.pack(ipadx=100, ipady=5)

    Button(page4, text="Zurück", font=(set_font, size_font), command=prev_page).pack(ipady=5, fill='x', expand=TRUE, side=LEFT, anchor='s')

    Button(page4, text="Weiter", font=(set_font, size_font), command=next_page).pack(ipady=5, fill='x', expand=TRUE, side=LEFT, anchor='s')

    page4.mainloop()

def page5():
    global page5
    page5 = Tk()
    page5.attributes('-fullscreen', True)
    page5.title('Navigationsmenü Titel')
    page5['bg'] = bg_color

    def next_page():
        page2.destroy()
        page3.destroy()
        page4.destroy()
        page5.destroy()
        print("Die folgenden Werte wurden übermittelt")
        print(radio_selected)
        print(set_value)
        print(username)
        print(password)

    def prev_page():
        page5.withdraw()
        radio_type_label.config(text="")
        set_value_label.config(text="")
        user_label.config(text="")
        page4.deiconify()

    Label(page5, text="Zusammenfassung", pady=100, bg=bg_color, fg=fg_color, font=(set_font, size_headline)).pack(fill=X)

    separator = ttk.Separator(page5, orient='horizontal')
    separator.pack(fill='x')

    Label(page5, bg=bg_color, fg=fg_color, font=(set_font, size_font)).pack(ipady=100)

    global radio_type_label
    global set_value_label
    global user_label
    radio_type_label = Label(page5, bg=bg_color, fg=fg_color, font=(set_font, size_font))
    if radio_selected == "desktop":
        radio_type_label.config(text="Ausgewählter Wert:      Linux Desktop")
    elif radio_selected == "server":
        radio_type_label.config(text="Ausgewählter Wert:      Linux Server")
    else:
        radio_type_label.config(text="Aausgewählter Wert:      -")

    radio_type_label.pack(ipadx=100)
    if set_value == "":
        set_value_label = Label(page5, text="Eingegebener Wert:      -", bg=bg_color, fg=fg_color, font=(set_font, size_font))
    else:
        set_value_label = Label(page5, text="Eingegebener Wert:     " + set_value, bg=bg_color, fg=fg_color, font=(set_font, size_font))
    set_value_label.pack(ipadx=100)
    
    if username == "":
        user_label = Label(page5, text="Eingegebener Benutzername:      -", bg=bg_color, fg=fg_color, font=(set_font, size_font))
    else: 
        user_label = Label(page5, text="Eingegebener Benutzername:     " + username, bg=bg_color, fg=fg_color, font=(set_font, size_font))
    if username == "":
        user_label.config(text="Eingegebener Benutzername:      -")
    else:
        user_label.config(text="Eingegebener Benutzername:     " + username)
    user_label.pack(ipadx=100)

    Button(page5, text="Zurück", font=(set_font, size_font), command=prev_page).pack(ipady=5, fill='x', expand=TRUE, side=LEFT, anchor='s')

    Button(page5, text="Einrichtung abschließen", font=(set_font, size_font), command=next_page).pack(ipady=5, fill='x', expand=TRUE, side=LEFT, anchor='s')

    page5.mainloop()

page1()

Hier geht es zum dazugehörigen Video:
https://youtu.be/DXUF24Sjokg

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Nach oben scrollen