Tkinter - RuntimeError: maximum recursion depth exceeded

前端 未结 1 1962
忘掉有多难
忘掉有多难 2020-12-06 21:51

I started programming in Python on Monday. I have enjoyed learning it. But I am stuck trying to understand how to avoid recursion when switching between tkinter menus! I

相关标签:
1条回答
  • 2020-12-06 22:16

    Only one mainloop() is needed to handle a tkinter GUI.

    With that said, I think you just need an example of the class structure:

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

    To avoid possible conflicts with other modules, some people prefer importing like this
    (clearly stating where everything comes from):

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

    And as you can see, creating the widgets can easily happen in the __init__


    I decided to make a more practical / educational example based on what I've learned in the past month. While doing so I had a bit of a revelation: not everything requires a self. prefix in a class! This is especially true with a tkinter class, because you aren't manipulating it as an object in the main program. Mostly you need the self. prefix when you are going to use something in a method later. The previous examples display how anything (like the buttons) can receive a self. prefix, even when completely unnecessary.

    Some things this example will show:

    pack() and grid() can be used in the same GUI as long as they don't share a master.

    • A Text widget can be made to not expand when the font size changes.

    • How to toggle a bold tag on and off of selected text.

    • How to truly center a GUI on the screen. (more information here)

    • How to make a Toplevel window appear in the same location relative to the main window.

    • Two ways to prevent a Toplevel window from being destroyed, so it only needs to be created once.

    • Make ctrl+a (select all) function properly.

    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 讨论(0)
提交回复
热议问题