2012-04-06 129 views
4

我在星期一开始使用Python进行编程。我很喜欢学习它。但我坚持试图了解如何在tkinter菜单之间切换时避免递归!我相信这是一个非常基本的问题,我感谢你们对这个问题的无知,但我一直无法在其他地方找到答案。Tkinter - RuntimeError:超过最大递归深度

我现在做什么,最终,给我的错误:RuntimeError:最大递归深度,同时调用Python对象

这是超出我目前使用的模式。更新:下面的代码现在是一个完整的,孤立的副本,再现我面临的问题! :D

from tkinter import * 

def mainmenu(): 
    global frame, root 

    frame.destroy() 

    frame = Frame() 
    frame.pack() 

    button1 = Button(frame, text="anothermenulikethis", command = anothermenulikethis) 
    button2 = Button(frame, text="anothermenulikethis", command = anothermenulikethis) 
    button3 = Button(frame, text="mainmenu", command = mainmenu) 

    button1.pack(side=LEFT) 
    button2.pack(side=LEFT) 
    button3.pack(side=LEFT) 

    root.mainloop() 

def anothermenulikethis(): 
    global frame, root 

    frame.destroy() 

    frame = Frame() 
    frame.pack() 

    button1 = Button(frame, text="mainmenu", command = mainmenu) 
    button2 = Button(frame, text="mainmenu", command = mainmenu) 
    button3 = Button(frame, text="anothermenulikethis", command = anothermenulikethis) 

    button1.pack(side=LEFT) 
    button2.pack(side=LEFT) 
    button3.pack(side=LEFT) 

    root.mainloop() 

root = Tk() 
root.title("Recursive Menu Problem Isolation") 
root.geometry("1200x600") 
frame = Frame() 

mainmenu() 

它一切正常,直到它从最大递归深度的不可避免的失败。如果任何人都可以提出一个更好的做法,或者有一个更好的做法的例子,我很想学习。 PS:我已经看过并试图增加递归深度,但我觉得这是一个穷人的解决方案,以解决我的方法的根本问题。

谢谢大家。

按照要求,这里是回溯:

Exception in Tkinter callback 
Traceback (most recent call last): 
    File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 1399, in __call__ 
    return self.func(*args) 
    File "/Users/diligentstudent/Desktop/menutest.py", line 11, in mainmenu 
    button1 = Button(frame, text="anothermenulikethis", command = anothermenulikethis) 
    File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 2028, in __init__ 
    Widget.__init__(self, master, 'button', cnf, kw) 
    File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 1958, in __init__ 
    (widgetName, self._w) + extra + self._options(cnf)) 
    File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 1043, in _options 
    v = self._register(v) 
    File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 1079, in _register 
    f = CallWrapper(func, subst, self).__call__ 
RuntimeError: maximum recursion depth exceeded 
+0

你能发布stacktrace吗?需要知道哪个函数导致递归深度异常。 – 2012-04-06 05:30:54

+0

我已根据请求发布追踪。 :) – 2012-04-06 05:57:05

回答

7

只有一个mainloop()是需要处理Tkinter的GUI。

虽这么说,我觉得你只需要类结构的一个例子:

from tkinter import Tk,Button 

class Application(Tk): 

    def say_hi(self): 
     print('Hello world?!') 

    def close_app(self): 
     self.destroy() 

    def create_Widgets(self): 
     self.quitButton = Button(self, width=12, text='Quit', bg='tan', 
        command=self.close_app) 
     self.quitButton.grid(row=0, column=0, padx=8, pady=8) 

     self.helloButton = Button(self, width=12, text='Hello', 
        command=self.say_hi) 
     self.helloButton.grid(row=0, column=1, padx=8, pady=8) 

    def __init__(self): 
     Tk.__init__(self) 
     self.title('Hello world!') 
     self.create_Widgets() 

app = Application() 
app.mainloop() 

为了避免与其他模块可能存在的冲突,有些人喜欢进口喜欢这个
(写明这里的一切来自):

import tkinter as tk 

class Application(tk.Tk): 

    def __init__(self): 
     tk.Tk.__init__(self) 
     self.title('Hello world!') 

     self.quitButton = tk.Button(self, width=12, text='Quit', bg='tan', 
        command=self.close_app) 
     self.quitButton.grid(row=0, column=0, padx=8, pady=8) 

     self.helloButton = tk.Button(self, width=12, text='Hello', 
        command=self.say_hi) 
     self.helloButton.grid(row=0, column=1, padx=8, pady=8) 

    def say_hi(self): 
     print('Hello world?!') 

    def close_app(self): 
     self.destroy() 

app = Application() 
app.mainloop() 

正如你所看到的,创建控件可以很容易地在__init__

发生

我决定根据我在过去一个月里学到的内容做出一个更实用/有教育意义的例子。虽然这样做我有一点启示:并非所有事情都需要一个自我。在一个班的前缀!对于tkinter类来说尤其如此,因为您并未将其作为主程序中的对象进行操作。大多数你需要自我。当你打算在后面的某个方法中使用某些东西时,前缀。前面的例子显示了如何任何东西(如按钮)可以接受自我。前缀,即使完全没有必要。

有些东西这个例子显示:

pack()grid()可以在同一个界面,只要它们不共享一个主使用。

•字体大小更改时,可以使文本小部件不展开。

•如何切换打开和关闭选定文本的粗体标记。

•如何在屏幕上真正居中GUI。(more information here)

•如何使Toplevel窗口出现在相对于主窗口的相同位置。

•有两种方法可以防止Toplevel窗口被破坏,所以只需要创建一次。

•正确设置ctrl + a(全选)功能。

import tkinter as tk 
import tkFont 

class Application(tk.Tk): 

    def __init__(self): 
     tk.Tk.__init__(self) 
     self.title('T-Pad') 

    # Menubar 

     menubar = tk.Menu(self) 

     filemenu = tk.Menu(menubar, tearoff=0) 
     filemenu.add_command(label="Exit", command=self.close_app) 
     menubar.add_cascade(label="File", menu=filemenu) 

     formatmenu = tk.Menu(menubar, tearoff=0) 
     formatmenu.add_command(label="Font", command=self.show_sizeWin) 
     menubar.add_cascade(label="Format", menu=formatmenu) 

     self.config(menu=menubar) 

    # Bold Button 

     boldButton = tk.Button(self, width=12, text='Bold', 
           command=self.make_bold) 
     boldButton.pack() 

    # Text widget, its font and frame 

     self.defaultFont = tkFont.Font(name="defFont") 

     textFrame = tk.Frame(self, borderwidth=1, relief="sunken", 
          width=600, height=600) 

     textFrame.grid_propagate(False) # ensures a consistent GUI size 
     textFrame.pack(side="bottom", fill="both", expand=True) 


     self.mText = tk.Text(textFrame, width=48, height=24, wrap='word', 
          font="defFont") 
     self.mText.grid(row=0, column=0, sticky="nsew") 

    # Scrollbar and config 

     tScrollbar = tk.Scrollbar(textFrame, command=self.mText.yview) 
     tScrollbar.grid(row=0, column=1, sticky='nsew', pady=1) 

     self.mText.config(yscrollcommand=tScrollbar.set) 

    # Stretchable 

     textFrame.grid_rowconfigure(0, weight=1) 
     textFrame.grid_columnconfigure(0, weight=1) 

    # Bold Tag 

     self.bold_font = tkFont.Font(self.mText, self.mText.cget("font")) 
     self.bold_font.configure(weight="bold") 
     self.mText.tag_configure("bt", font=self.bold_font) 

    # Center main window 

     self.update_idletasks() 

     xp = (self.winfo_screenwidth()/2) - (self.winfo_width()/2) - 8 
     yp = (self.winfo_screenheight()/2) - (self.winfo_height()/2) - 30 
     self.geometry('{0}x{1}+{2}+{3}'.format(self.winfo_width(), self.winfo_height(), 
                       xp, yp)) 

    # Font Size Window (notice that self.sizeWin is given an alias) 

     sizeWin = self.sizeWin = tk.Toplevel(self, bd=4, relief='ridge') 

     self.sizeList = tk.Listbox(sizeWin, width=10, height=17, bd=4, 
           font=("Times", "16"), relief='sunken') 

     self.sizeList.grid() 

     doneButton = tk.Button(sizeWin, text='Done', command=sizeWin.withdraw) 
     doneButton.grid() 

     for num in range(8,25): 
      self.sizeList.insert('end', num) 

     sizeWin.withdraw() 

     sizeWin.overrideredirect(True) # No outerframe! 
     # Below is another way to prevent a TopLevel window from being destroyed. 
     # sizeWin.protocol("WM_DELETE_WINDOW", self.callback) 

    # Bindings 
     # Double click a font size in the Listbox 
     self.sizeList.bind("<Double-Button-1>", self.choose_size) 
     self.bind_class("Text", "<Control-a>", self.select_all) 

## def callback(self): 
##  self.sizeWin.withdraw() 

    def select_all(self, event): 
     self.mText.tag_add("sel","1.0","end-1c") 

    def choose_size(self, event=None): 
     size_retrieved = self.sizeList.get('active') 
     self.defaultFont.configure(size=size_retrieved) 
     self.bold_font.configure(size=size_retrieved) 

    def show_sizeWin(self): 
     self.sizeWin.deiconify() 
     xpos = self.winfo_rootx() - self.sizeWin.winfo_width() - 8 
     ypos = self.winfo_rooty() 
     self.sizeWin.geometry('{0}x{1}+{2}+{3}'.format(self.sizeWin.winfo_width(), 
               self.sizeWin.winfo_height(), xpos, ypos)) 

    def make_bold(self): 
     try: 
      current_tags = self.mText.tag_names("sel.first") 
      if "bt" in current_tags: 
       self.mText.tag_remove("bt", "sel.first", "sel.last") 
      else: 
       self.mText.tag_add("bt", "sel.first", "sel.last") 
     except tk.TclError: 
      pass 

    def close_app(self): 
     self.destroy() 

app = Application() 
app.mainloop() 
+0

Ohhhhhh!是的,这正是我寻找的模式!非常感谢! :D – 2012-04-06 06:17:06

+0

@ADiligentStudent我的荣幸!我添加了一个稍微修改的版本。 – 2012-04-06 06:34:45

+0

@ADiligentStudent我再次更新。这次有一个实际的例子,包括你想要制作的Toplevel(二级)窗口。 – 2012-04-07 06:41:58

相关问题