Multiple Frames organisieren

Fragen zu Tkinter.
Antworten
Benutzeravatar
Blackshie
User
Beiträge: 13
Registriert: Mittwoch 17. Januar 2018, 15:45

Guten Abend,

Ich suche eine Möglichkeit beim Arbeiten mit überlagerten Frames diese besser zu organisieren.
Momentan arbeite ich vor allem mit der methode .tkraise()
Vor allem scheitert es gerade daran zwischen einem Frame und dem zuletzt angezeigten zu wechseln.
Bezogen auf meinen untenstehenden Beispielcode bedeutet dass, der Button "Switch" soll zwischen Frame4 und, bei erneutem klick, dem ZULETZT aktiven vor dem wechsel switchen.
Leider habe ich keine Möglichkeit gefunden zu testen welches Frame gerade "oben" ist.
Das gerade aktive in einer globalen Variable zu speichern erscheint mir auch nicht sinnvoll.

Des weiteren wäre es gut die Frames wie gesagt besser zu organisieren.
Jeder Klasse jedes Frame-Objekt als Argument mitzugeben ist keine schöne Lösung...

Vielleicht die Frames über eine spezielle Klasse organisieren und dann die Klassen-Objekte in einem Dictionary speichen?

Hier hoffe ich auf Ideen und Möglichkeiten von euch.

MfG
Blackshie

Code: Alles auswählen

import tkinter as tk


def _show_frame(frame):
    frame.tkraise()

# Erstellen der Beispiel Frames
class TestFrame1(tk.Frame):

    def __init__(self, main, width, height):
        tk.Frame.__init__(self, main)

        self.config(bg="green")
        self.place(relx=0, rely=0, height=height, width=width)

class TestFrame2(tk.Frame):

    def __init__(self, main, width, height):
        tk.Frame.__init__(self, main)

        self.config(bg="red")
        self.place(relx=0, rely=0, height=height, width=width)

class TestFrame3(tk.Frame):

    def __init__(self, main, width, height):
        tk.Frame.__init__(self, main)

        self.config(bg="yellow")
        self.place(relx=0, rely=0, height=height, width=width)

class TestFrame4(tk.Frame):

    def __init__(self, main, width, height):
        tk.Frame.__init__(self, main)

        self.config(bg="orange")
        self.place(relx=0, rely=0, height=height, width=width)

class ControlFrame(tk.Frame):

    def __init__(self, main, width, fr_test_frame_1, fr_test_frame_2, fr_test_frame_3):
        tk.Frame.__init__(self, main)

        self.config(bg="grey")
        self.place(x=0, y=380, height=100, width=width)

        # Fenster Zeichen
        tk.Button(self, text="TestFrame1", command=lambda: _show_frame(fr_test_frame_1)).pack()
        tk.Button(self, text="TestFrame2", command=lambda: _show_frame(fr_test_frame_2)).pack()
        tk.Button(self, text="TestFrame3", command=lambda: _show_frame(fr_test_frame_3)).pack()
        tk.Button(self, text="Switch").pack() # Hier mangelt es...


def main():

    # Konstanten
    WM_HEIGHT = 480
    WM_WIDTH = 800

    # Hauptfenster
    main = tk.Tk()
    main.title("Test")
    main.geometry("{:}x{:}".format(WM_WIDTH, WM_HEIGHT))
    # main.attributes("-fullscreen", True)
    main.resizable(0, 0)

    fr_1 = TestFrame1(main, WM_WIDTH, WM_HEIGHT-100)
    fr_2 = TestFrame2(main, WM_WIDTH, WM_HEIGHT-100)
    fr_3 = TestFrame3(main, WM_WIDTH, WM_HEIGHT-100)
    fr_4 = TestFrame1(main, WM_WIDTH, WM_HEIGHT-100)


    ControlFrame(main, WM_WIDTH, fr_1, fr_2, fr_3)

    main.mainloop()

if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Was hast Du gegen ein Attribut auf Deiner ControlFrame-Instanz? Und Du übergibst Deine Frames ja nicht jeder Klasse sondern nur ControlFrame. Was doch auch okay ist, weil das ja auch die spezielle Klasse ist, die die Frames organisiert. Zur Vereinfachung würde ich nur die Frames in eine Liste packen.
Benutzeravatar
Blackshie
User
Beiträge: 13
Registriert: Mittwoch 17. Januar 2018, 15:45

Das ist ja nur ein Beispielcode.
Im fertigen Projekt später wird von (fast) jeden Frame über Buttons (oder andere Wege) irgendein anderes Frame aufgerufen.
Die ganzen Frames in eine Liste zu packen war auch eine Idee von mir, und diese Liste dann jeweils weiterzugeben.

Ich arbeite gerne mit überlagerten Frames, weil ich finde es ist eine einfache, übersichtliche und gute Lösung wenn man mehrere "Anzeigen" in einem Fenster haben will.
(Solange alles die selbe Größe hat ist es zumindest noch einfach)

Finde halt persönlich, es mangelt an Möglichkeiten multiple Frames zu organisieren bzw. mit diesem Frame-"Stack" umzugehen.
nur .tkraise() zu nutzen, um ein Frame ganz nach oben zu holen ist doch etwas wenig.
und .lift() bzw .lower() machen auch nichts anderes.


Wieso Attribut?
Ich verstehe nicht ganz wie das mein "Switch" Problem lösen wird?
Benutzeravatar
Blackshie
User
Beiträge: 13
Registriert: Mittwoch 17. Januar 2018, 15:45

bzw wie gesagt, das aktive Frame als Attribute an die eine Klasse zu binden führt auch nicht zum Erfolg, da auch die andere Frame-Klassen das wechseln der Frames später übernehmen werden
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Da TkInter anscheinend nicht die Funktionalität bietet, die Du gerne haben möchtest, mußt Du sie selbst programmieren. Und egal, wie Du die Steuerung machst, es gibt immer ein Hauptfenster, das alle Frames kennen muß und die einzelnen Frames nur dem Hauptfenster sagen, welcher denn nun als nächster angezeigt werden soll.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Blackshie

Könnte das folgende Skript die Lösung für dein Vorhaben sein?:

Code: Alles auswählen

from functools import partial
import tkinter as tk

APP_TITLE = "Frame Stack"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 500
APP_HEIGHT = 300


class AppFrame(tk.Frame):
    
    def __init__(self, app, frame_index, **kwargs):
        self.app = app
        self.frame_index = frame_index
        tk.Frame.__init__(self, app, **kwargs)
            
class Application(object):
    NUM_OF_PAGES = 4
    FRAME_COLORS = ['green', 'red', 'yellow', 'orange']
    
    def __init__(self, app_win):
        self.app_win = app_win

        self.main_frame = tk.Frame(app_win)
        self.main_frame.pack(fill='both', expand=True)
        self.main_frame.grid_rowconfigure(0, weight=1)
        self.main_frame.grid_columnconfigure(0, weight=1)

        self.pages = list()
        self.active_page = None
        self.previous_page = None

        self.build()
        
    def build(self):
        button_frame = tk.Frame(self.main_frame)
        button_frame.grid(row=1, column=0, pady=4)

        for frame_index in range(self.NUM_OF_PAGES):
            page = self.create_app_frame(frame_index)
            tk.Label(page, text="Frame Nr:{}".format(frame_index+1), font=20,
                bg=page['bg']).pack(expand=True)
            tk.Button(button_frame, text="Frame-{}".format(frame_index+1),
                command=partial(self.show_app_frame, frame_index)
                ).pack(side='left')

        tk.Button(button_frame, text="Switch",
            command=partial(self.switch_app_frame)).pack(side='left')
        
        self.show_app_frame(0)
       
    def show_app_frame(self, frame_index=0):
        if self.previous_page != None:
            self.previous_page = self.active_page
            self.active_page = frame_index
        else:
            self.active_page = frame_index
            self.previous_page = self.active_page  
        self.pages[self.active_page].lift()            
    
    def switch_app_frame(self):
        if self.previous_page != None:
            self.show_app_frame(self.previous_page)
                  
    def create_app_frame(self, frame_index):
        page = AppFrame(self.main_frame, frame_index,
            bg=self.FRAME_COLORS[frame_index])
        page.grid(row=0, column=0, sticky='wens')
        self.pages.append(page)
        return page

           
def main():
    app_win = tk.Tk()
    app_win.title(APP_TITLE)
    app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
    app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
    
    app = Application(app_win)
    
    app_win.mainloop()
 
 
if __name__ == '__main__':
    main()
Gruss wuf :-)
Take it easy Mates!
Antworten