Tkinter - RuntimeError: превышена максимальная глубина рекурсии

Я начал программировать на Python в понедельник. Я с удовольствием изучал это. Но я застрял, пытаясь понять, как избежать рекурсии при переключении между меню tkinter! Я уверен, что это очень простой вопрос, и я ценю, что вы терпите мое невежество по этому вопросу, но я не смог найти ответ в другом месте.

Что я сейчас делаю, так это, в конце концов, выдает мне ошибку: RuntimeError: максимальная глубина рекурсии превышена при вызове объекта Python

Это шаблон, который я сейчас использую. ОБНОВЛЕНО: приведенный ниже код теперь является полной изолированной копией, воспроизводящей проблему, с которой я столкнулся! : D

<code>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()
</code>

И все это прекрасно работает, пока его неизбежный отказ от максимальной глубины рекурсии. Если кто-то может предложить лучший способ сделать что-то или у него есть ссылка на пример лучшего способа сделать это, я очень хочу учиться.

PS: я смотрел и пытался увеличить глубину рекурсии, но я чувствую, что это решение бедного человека, что является фундаментальной проблемой моего подхода.

Спасибо всем заранее.

Как и просили, вот трассировка:

<code>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
</code>
 A Diligent Student06 апр. 2012 г., 07:57
Я отправил отслеживание в соответствии с просьбой. :)
 Anthony Kong06 апр. 2012 г., 07:30
Вы можете опубликовать трассировку стека? Необходимо знать, какая функция вызывает исключение глубины рекурсии.

Ответы на вопрос(1)

Решение Вопроса

Только одинmainloop() необходим для обработки графического интерфейса tkinter.

С учетом сказанного, я думаю, вам просто нужен пример структуры класса:

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, потому что вы не управляете им как объектом в основной программе. В основном вам нужна личность. префикс, когда вы собираетесь использовать что-то в методе позже. Предыдущие примеры показывают, как что-либо (например, кнопки) может получить себя. префикс, даже когда совершенно не нужен.

Некоторые вещи, которые этот пример покажет:

& # X2022;pack() а такжеgrid() может использоваться в одном и том же графическом интерфейсе, если они не имеют общего мастера.

& # X2022; Текстовый виджет может не расширяться при изменении размера шрифта.

& # X2022; Как включить или отключить выделенный текст для выделенного текста.

& # X2022; Как по-настоящему центрировать графический интерфейс на экране.(больше информации здесь)

& # X2022; Как сделать так, чтобы окно Toplevel отображалось в том же месте относительно основного окна.

& # X2022; Два способа предотвратить разрушение окна Toplevel, поэтому его нужно создать только один раз.

& # X2022; Сделайте 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()
 07 апр. 2012 г., 08:52
@HonestAbe Отличный ответ, очень подробный. + 1
 07 апр. 2012 г., 08:41
@ADiligentStudent Я снова обновился. На этот раз с практическим примером, включающим Toplevel (вторичное) окно, которое, кажется, вы хотели сделать.
 06 апр. 2012 г., 08:34
@ADiligentStudent С удовольствием! Я добавил слегка измененную версию.
 A Diligent Student06 апр. 2012 г., 08:17
Ohhhhhh! Да, это именно тот шаблон, который я искал! Спасибо вам большое! : D

Ваш ответ на вопрос