0

I created a simple GUI but every time I click one of the buttons, the GUI flickers. I looked at some other SO questions but none were abstract enough to help me solve my particular problem.

This is the code I'm working with (I know it's a lot of code but I thought any little detail could be the cause of the issue):

import tkinter as tk

class LeftMenu:

    def __init__(self, master):

        def call_home():
            h = Home(master)
            h.__init__(master)

        def call_overviews():
            h = Overviews(master)
            h.__init__(master)

        self.maincolor = 'darkgrey'
        self.menucolor = 'grey'

        # Menu creation
        menu = tk.Frame(master, bg=self.menucolor)

        # def quitcurrent():
        #     menu.quit()

        # Menu positioning
        menu.grid(row=0, column=0, sticky='nsew')
        # Menu items
        self.home = tk.Button(menu, text='Home', command=lambda: [call_home()])
        self.overview = tk.Button(menu, text='Overview', command=lambda: [call_overviews()])


        # Menu items location
        self.home.grid(row=0, column=0, pady=(25, 50), padx=15)
        self.overview.grid(row=1, column=0, pady=(0, 50), padx=15)


        # Menu items configuration
        buttons = [self.home, self.overview]
        for self.button in buttons:
            self.button.configure(width=20, height=5, highlightbackground=self.menucolor)



class Home(LeftMenu):

    def __init__(self, master):
        super().__init__(master)

        self.homeright(master)


    def homeright(self, master):
        # Main creation
        main = tk.Frame(master, bg=self.maincolor)

        # Main positioning
        main.grid(row=0, column=1, sticky='nsew')

        # Main items
        self.maintext = tk.Label(main, text='Welcome to the Apartment Manager', bg=self.maincolor, pady=250)
        self.maintext.pack()


class Overviews(LeftMenu):

    def __init__(self, master):
        super().__init__(master)

        self.homeright(master)

    def homeright(self, master):
        # Main creation
        main = tk.Frame(master, bg=self.maincolor)

        # Main positioning
        main.grid(row=0, column=1, sticky='nsew')

        # Main items
        self.maintext = tk.Label(main, text='It actually worked', bg=self.maincolor, pady=250)
        self.maintext.pack()


root = tk.Tk()
root.title('Apartment Manager')
root.geometry('750x750')
root.grid_columnconfigure(0, weight=1)
root.grid_columnconfigure(1, weight=7)
root.grid_rowconfigure(0, weight=1)
root.wm_withdraw()
root.update()

runit = Home(root)

root.after(1, root.deiconify)
root.mainloop()

I thought it might have to do something with the lines:

root.wm_withdraw()
root.update()
root.after(1, root.deiconify)

So I ran it without those lines, but it had no effect. Does anyone know what is causing the GUI to flicker every time I click on one of the buttons and how to solve it?

Using Python 3.8 and tkinter 8.6

Jem
  • 557
  • 9
  • 28
  • Please try to reduce this code down a bit. There's a lot of irrelevant code. For example, do we really need five classes (Home, Overviews, etc) or can the problem be replicated with just one or two? Is the database code really necessary, or can you hard-code some test data? – Bryan Oakley Jul 20 '20 at 15:05
  • @BryanOakley You're right. I narrowed down my code a bit. – Jem Jul 20 '20 at 15:08
  • Why do you do that (root.after(1, root.deiconify))? You try every millisecond to deiconify your window. Why would something needed? – Thingamabobs Jul 20 '20 at 15:10
  • 1
    Why are you explicitly calling `.__init__()` on your objects after creating them? That's automatically done for you, so you're duplicating all of your widget creation in those classes. – jasonharper Jul 20 '20 at 15:11
  • @Atlas435 I did that because of [this](https://stackoverflow.com/questions/62609543/my-tkinter-gui-seems-visually-out-of-focus-when-opened) – Jem Jul 20 '20 at 15:12
  • 1
    @Atlas435: that doesn't "try every millisecond". It calls the function exactly once, one millisecond in the future. – Bryan Oakley Jul 20 '20 at 15:14
  • You're right, my bad. – Thingamabobs Jul 20 '20 at 15:16

1 Answers1

3

I see at least two problems. First, when I click on a button it creates a new set of widgets for the right. Do you really want to do that, or can you create the instances of Home, etc only once and then hide or show it when the user clicks a button?

I think that's the main cause of the flicker. Click the Home button it creates a Home window. Click Overview and it creates an Overview window and overlays it on top of the Home window. Click Home again and you get another Home window. And so on.

The second problem is that you're directly calling the __init__ method. You should not do that. When you create an instance of a class (eg: h = Home(master)), the __init__ is automatically called. That may also contribute to the flicker, since each time you create the widgets for a window, you end up creating the widgets twice.

Finally, Home, etc should not be inheriting from LeftMenu. Doing so creates an extra set of buttons each time you click a button. Actually two extra sets of buttons. Once when you create the instance of Overview, and once when you directly call the __init__ of Overview.

Adding that all up:

  • when you click on the Overview it creates a new instance of Overview. This new instance has X widgets in it.
  • You then call the __init__ directly which creates X more widgets.
  • And because they inherit from LeftMenu you also create new copies of all of the buttons twice.

This happens every time you click on one of those buttons.

All of these widgets are being stacked on top of one another, causing tkinter to have to do a lot of recalculation of the geometry every time you click a button. The more you click, the more recalculations it has to perform.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • 1
    So I should create the right-side widgets when I run the code, and only show / hide them depending on the button click instead of creating them with the buttons? Next to that, I should also find a way for the left-side buttons to always remain there and not get created over and over again on every button click? And not calling `__init__` again then of course, but I understood that part of your explanation clearly, even as a beginner ;) – Jem Jul 20 '20 at 15:19
  • 1
    @Jem: _should you_? It depends on the exact behavior you want. You can create them once, or you can destroy and recreate them. Each has pros and cons, though for the most part either solution will work. – Bryan Oakley Jul 20 '20 at 15:21