it-swarm.com.de

Wechseln Sie zwischen zwei Frames in tkinter

Ich habe meine ersten Skripte mit einer netten, kleinen Benutzeroberfläche darauf aufgebaut, wie die Tutorials gezeigt haben, aber keine beschreibt, was für ein komplexeres Programm zu tun ist.

Wenn Sie ein Startmenü für Ihren Startbildschirm haben und nach Auswahl des Benutzers zu einem anderen Programmabschnitt wechseln und den Bildschirm entsprechend neu zeichnen, wie ist dies elegant?

Führt man nur .destroy() das 'Startmenü' aus und erstellt dann ein neues mit den Widgets für einen anderen Teil? Und diesen Vorgang umkehren, wenn sie die Zurück-Taste drücken?

77
Max Tilley

Eine Möglichkeit besteht darin, die Rahmen übereinander zu stapeln. Anschließend können Sie sie in der Stapelreihenfolge einfach übereinander anheben. Das oberste ist das sichtbare. Dies funktioniert am besten, wenn alle Frames die gleiche Größe haben, aber mit ein wenig Arbeit können Sie es mit Frames beliebiger Größe zum Laufen bringen.

Hinweis : Damit dies funktioniert, müssen alle Widgets für eine Seite diese Seite (dh: self) oder einen Nachkommen als haben ein Elternteil (oder Master, abhängig von der von Ihnen bevorzugten Terminologie).

Hier ist ein Beispiel, um Ihnen das allgemeine Konzept zu zeigen:

import tkinter as tk                # python 3
from tkinter import font  as tkfont # python 3
#import Tkinter as tk     # python 2
#import tkFont as tkfont  # python 2

class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")

        # the container is where we'll stack a bunch of frames
        # on top of each other, then the one we want visible
        # will be raised above the others
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in (StartPage, PageOne, PageTwo):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame

            # put all of the pages in the same location;
            # the one on the top of the stacking order
            # will be the one that is visible.
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame("StartPage")

    def show_frame(self, page_name):
        '''Show a frame for the given page name'''
        frame = self.frames[page_name]
        frame.tkraise()


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is the start page", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)

        button1 = tk.Button(self, text="Go to Page One",
                            command=lambda: controller.show_frame("PageOne"))
        button2 = tk.Button(self, text="Go to Page Two",
                            command=lambda: controller.show_frame("PageTwo"))
        button1.pack()
        button2.pack()


class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 1", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()


class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 2", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()


if __== "__main__":
    app = SampleApp()
    app.mainloop()

start pagepage 1page 2

Wenn Sie das Konzept der Instanzerstellung in einer Klasse verwirrend finden oder wenn verschiedene Seiten während der Erstellung unterschiedliche Argumente benötigen, können Sie jede Klasse explizit separat aufrufen. Die Schleife dient hauptsächlich dazu, den Punkt zu veranschaulichen, an dem jede Klasse identisch ist.

Um beispielsweise die Klassen einzeln zu erstellen, können Sie die Schleife (for F in (StartPage, ...) folgendermaßen entfernen:

self.frames["StartPage"] = StartPage(parent=container, controller=self)
self.frames["PageOne"] = PageOne(parent=container, controller=self)
self.frames["PageTwo"] = PageTwo(parent=container, controller=self)

self.frames["StartPage"].grid(row=0, column=0, sticky="nsew")
self.frames["PageOne"].grid(row=0, column=0, sticky="nsew")
self.frames["PageTwo"].grid(row=0, column=0, sticky="nsew")

Im Laufe der Zeit haben Benutzer andere Fragen gestellt, indem sie diesen Code (oder ein Online-Tutorial, das diesen Code kopiert hat) als Ausgangspunkt verwendet haben. Vielleicht möchten Sie die Antworten auf diese Fragen lesen:

130
Bryan Oakley

Hier ist eine weitere einfache Antwort, jedoch ohne Klassen.

from tkinter import *


def raise_frame(frame):
    frame.tkraise()

root = Tk()

f1 = Frame(root)
f2 = Frame(root)
f3 = Frame(root)
f4 = Frame(root)

for frame in (f1, f2, f3, f4):
    frame.grid(row=0, column=0, sticky='news')

Button(f1, text='Go to frame 2', command=lambda:raise_frame(f2)).pack()
Label(f1, text='FRAME 1').pack()

Label(f2, text='FRAME 2').pack()
Button(f2, text='Go to frame 3', command=lambda:raise_frame(f3)).pack()

Label(f3, text='FRAME 3').pack(side='left')
Button(f3, text='Go to frame 4', command=lambda:raise_frame(f4)).pack(side='left')

Label(f4, text='FRAME 4').pack()
Button(f4, text='Goto to frame 1', command=lambda:raise_frame(f1)).pack()

raise_frame(f1)
root.mainloop()
25
recobayu

Eine Möglichkeit, Frames in tkinter zu wechseln, besteht darin, den alten Frame zu zerstören und ihn durch Ihren neuen Frame zu ersetzen.

Ich habe Bryan Oakleys Antwort geändert, um den alten Rahmen vor dem Ersetzen zu zerstören. Als zusätzlichen Bonus können Sie auf die Verwendung eines container -Objekts verzichten und jede generische Frame -Klasse verwenden.

# Multi-frame tkinter application v2.3
import tkinter as tk

class SampleApp(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self._frame = None
        self.switch_frame(StartPage)

    def switch_frame(self, frame_class):
        """Destroys current frame and replaces it with a new one."""
        new_frame = frame_class(self)
        if self._frame is not None:
            self._frame.destroy()
        self._frame = new_frame
        self._frame.pack()

class StartPage(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        tk.Label(self, text="This is the start page").pack(side="top", fill="x", pady=10)
        tk.Button(self, text="Open page one",
                  command=lambda: master.switch_frame(PageOne)).pack()
        tk.Button(self, text="Open page two",
                  command=lambda: master.switch_frame(PageTwo)).pack()

class PageOne(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        tk.Label(self, text="This is page one").pack(side="top", fill="x", pady=10)
        tk.Button(self, text="Return to start page",
                  command=lambda: master.switch_frame(StartPage)).pack()

class PageTwo(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        tk.Label(self, text="This is page two").pack(side="top", fill="x", pady=10)
        tk.Button(self, text="Return to start page",
                  command=lambda: master.switch_frame(StartPage)).pack()

if __== "__main__":
    app = SampleApp()
    app.mainloop()

Start pagePage onePage two

Erläuterung

switch_frame() akzeptiert alle Klassenobjekte, die Frame implementieren. Die Funktion erstellt dann einen neuen Rahmen, um den alten zu ersetzen.

  • Löscht den alten _frame, Falls vorhanden, und ersetzt ihn durch den neuen Frame.
  • Andere mit .pack() hinzugefügte Frames, wie z. B. Menüleisten, sind davon nicht betroffen.
  • Kann mit jeder Klasse verwendet werden, die tkinter.Frame Implementiert.
  • Die Fenstergröße wird automatisch an neue Inhalte angepasst

Versionsgeschichte

v2.3

- Pack buttons and labels as they are initialized

v2.2

- Initialize `_frame` as `None`.
- Check if `_frame` is `None` before calling `.destroy()`.

v2.1.1

- Remove type-hinting for backwards compatibility with Python 3.4.

v2.1

- Add type-hinting for `frame_class`.

v2.0

- Remove extraneous `container` frame.
    - Application now works with any generic `tkinter.frame` instance.
- Remove `controller` argument from frame classes.
    - Frame switching is now done with `master.switch_frame()`.

v1.6

- Check if frame attribute exists before destroying it.
- Use `switch_frame()` to set first frame.

v1.5

  - Revert 'Initialize new `_frame` after old `_frame` is destroyed'.
      - Initializing the frame before calling `.destroy()` results
        in a smoother visual transition.

v1.4

- Pack frames in `switch_frame()`.
- Initialize new `_frame` after old `_frame` is destroyed.
    - Remove `new_frame` variable.

v1.3

- Rename `parent` to `master` for consistency with base `Frame` class.

v1.2

- Remove `main()` function.

v1.1

- Rename `frame` to `_frame`.
    - Naming implies variable should be private.
- Create new frame before destroying old frame.

v1.0

- Initial version.
22
Stevoisiak