Python tkinter - 不能多次显示相同的帧?

时间:2016-10-20 12:16:03

标签: python tkinter

因此,使用下面的代码,我可以将页面从Page1切换到Page2,Page2到Page3,Page3到Page4,但不能从Page4切换到Page1。

显示错误消息:

Traceback (most recent call last):
  File "C:\Python33\lib\tkinter\__init__.py", line 1489, in __call__
    return self.func(*args)
  File "F:\CCTV\test\Page4.py", line 29, in buttonLoginClicked
    self.controller.show_frame(Page1)
NameError: global name 'Page1' is not defined

我想知道这是因为我不能再次切换到相同的帧,或者我只是做错了什么。如果我将所有类放在同一页面上,它似乎解决了问题,但我希望将所有类放在不同的页面上。也许这与导入页面有关?这是我的代码的简化版本,以帮助显示问题:

CCTV:

import tkinter as tk
from tkinter import ttk, messagebox
from Page1 import *
from Page2 import *
from Page3 import *
from Page4 import *

class CCTV(tk.Tk):

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

        container = tk.Frame(self)
        container.pack()
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for F in (Page1, Page2, Page3, Page4):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(column=0, row=0, sticky="nsew")

        self.openPage()

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

    def openPage(self):
        self.show_frame(Page1)

app = CCTV()
app.geometry("800x600")
app.mainloop()

第1页:

import tkinter as tk
from tkinter import ttk, messagebox
from Page2 import *

class Page1(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.createView()

    def createView(self):
        inner_frame = tk.Frame(self)
        inner_frame.pack(side="top", fill="none")

        self.labelTitle = ttk.Label(inner_frame, text="Page 1")
        self.buttonLogin = ttk.Button(inner_frame, text="Page 2", command=self.buttonLoginClicked)

        self.labelTitle.grid(row=1, columnspan=4, pady=10)
        self.buttonLogin.grid(row=2, columnspan=4, pady=10)

        self.grid_rowconfigure(0, weight=1)
        self.grid_rowconfigure(3, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(3, weight=1)


    def buttonLoginClicked(self):
        self.controller.show_frame(Page2)

2页:

import tkinter as tk
from tkinter import ttk, messagebox
from Page3 import *

class Page2(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.createView()

    def createView(self):
        inner_frame = tk.Frame(self)
        inner_frame.pack(side="top", fill="none")

        self.labelTitle = ttk.Label(inner_frame, text="Page 2")
        self.buttonLogin = ttk.Button(inner_frame, text="Page 3", command=self.buttonLoginClicked)

        self.labelTitle.grid(row=1, columnspan=4, pady=10)
        self.buttonLogin.grid(row=2, columnspan=4, pady=10)

        self.grid_rowconfigure(0, weight=1)
        self.grid_rowconfigure(3, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(3, weight=1)


    def buttonLoginClicked(self):
        self.controller.show_frame(Page3)

第3页:

import tkinter as tk
from tkinter import ttk, messagebox
from Page4 import *

class Page3(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.createView()

    def createView(self):
        inner_frame = tk.Frame(self)
        inner_frame.pack(side="top", fill="none")

        self.labelTitle = ttk.Label(inner_frame, text="Page 3")
        self.buttonLogin = ttk.Button(inner_frame, text="Page 4", command=self.buttonLoginClicked)

        self.labelTitle.grid(row=1, columnspan=4, pady=10)
        self.buttonLogin.grid(row=2, columnspan=4, pady=10)

        self.grid_rowconfigure(0, weight=1)
        self.grid_rowconfigure(3, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(3, weight=1)


    def buttonLoginClicked(self):
        self.controller.show_frame(Page4)

第4页:

import tkinter as tk
from tkinter import ttk, messagebox
from Page1 import *

class Page4(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.createView()

    def createView(self):
        inner_frame = tk.Frame(self)
        inner_frame.pack(side="top", fill="none")

        self.labelTitle = ttk.Label(inner_frame, text="Page 4")
        self.buttonLogin = ttk.Button(inner_frame, text="Page 1", command=self.buttonLoginClicked)

        self.labelTitle.grid(row=1, columnspan=4, pady=10)
        self.buttonLogin.grid(row=2, columnspan=4, pady=10)

        self.grid_rowconfigure(0, weight=1)
        self.grid_rowconfigure(3, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(3, weight=1)


    def buttonLoginClicked(self):
        self.controller.show_frame(Page1)

2 个答案:

答案 0 :(得分:1)

There's no good way to fix your code as written -- you have circular imports which I'm frankly surprised works at all. When you import Page1 in the main program, it causes Page2 to be imported because Page1 imports it. This causes Page3 to be imported because Page2 imports it. This causes Page4 to be imported because Page3 imports it. Then your main program explicitly imports Page2, which causes Page3 to be imported again, which causes Page4 to be imported again, and so on.

The root of your problem is that you are needing to import a page in order to switch to it. Don't do that. Instead, redefine show_frame to take the name of a page, so that you don't have to import a page in order to switch to it (except in the main program, of course).

The short version is here. Notice that the code gets the name of the page and uses it as the dictionary key:

class CCTV(tk.Tk):
    def __init__(...):
        ...
        for F in (Page1, Page2, Page3, Page4):
            page_name = F.__name__
            frame = F(container, self)
            self.frames[page_name] = frame
            ...

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

With that, you can switch to a frame without importing it, by simply doing this:

self.controller.show_frame("Page1")

A complete working example is here: https://stackoverflow.com/a/7557028/7432

答案 1 :(得分:1)

导入的模块只运行一次,每个导入它的连接文件都共享该模块的同一副本。因此,如果循环导入(在B中导入A,在C中导入B,在A中导入C),它将被搞砸,并且第二次不会被识别。 例如: 您有文件A.py

from B import b
from C import c

print (b, c) # this is fine

B.py的内容为:

from C import c # not proper initialization as it has already been in A.py already
print (c)

那将返回错误:

global name 'c' is not defined

事情是导入的文件将是全部相同的副本,因此您无法在不同的连接文件中实际导入相同的模块,您更愿意做的是:

pages = { 
          Page1: Page2,
          Page2: Page3,
          Page3: Page4,
          Page4: Page1
      }
for F, goto_page in pages.items():
  frame = F(container, self, goto_page)
  self.frames[F] = frame
  frame.grid(column=0, row=0, sticky="nsew")