2

I want to add an image to a topframe in tkinter. It works on the MainMenu, but not on the SideMenu. When run, it should show _test.gif on the first menu, and when the button is pressed, open the second menu and show _test2.gif. Currently, the second menu is blank - no image.

Code:

import tkinter as tk

class MainMenu(object):
    def __init__(self):
        self.launch_MainMenu()
    def launch_MainMenu(self):
        self._mainMenuWindow = tk.Tk()
        self.imageGIF = tk.PhotoImage(file="_test.gif");
        self.imageLabel = tk.Label(self._mainMenuWindow,image=self.imageGIF)
        self.imageLabel.grid(row=0,column=1,padx=10,pady=10)
        tk.Button(self._mainMenuWindow,text="Next Menu",
                  command=SideMenu).grid(row=0,column=2,padx=10)
        tk.mainloop()

class SideMenu(object):
    def __init__(self):
        self.launch_sideMenu()
    def launch_sideMenu(self):
        self._sideWindow = tk.Toplevel()
        self.imageGIF2 = tk.PhotoImage(file="_test2.gif")
        self.imageLabel2 = tk.Label(self._sideWindow,image=self.imageGIF2)
        self.imageLabel2.grid(row=0,column=1,padx=10,pady=10)
JimmyCarlos
  • 1,934
  • 1
  • 10
  • 24
  • This [adding image to tkinter gui](https://stackoverflow.com/questions/27049315/adding-image-to-tkinter-gui) will be of help to your question. You will need to add "self." Before the local variable(imageXxx). – kunif Jan 11 '18 at 23:05
  • @kunif: I thought that at first, too, but it's not the problem here. – martineau Jan 11 '18 at 23:06
  • Sorry, I oversimplified my original code. It's 2 different images. – JimmyCarlos Jan 11 '18 at 23:08
  • The variable of MainMenu class is not preceded by "self.". And, is it better for initialie Tk() and mainloop to be outside the MainMenu class? . – kunif Jan 11 '18 at 23:24
  • Thank you for your advice kunif. I've added self. to everything in the mainmenu, but the second image is still not showing. Could you offer some more help please? – JimmyCarlos Jan 11 '18 at 23:32
  • Is [the answer to this article](https://stackoverflow.com/questions/16115378/tkinter-example-code-for-multiple-windows-why-wont-buttons-load-correctly) close to what you want to do? If so, you should add an image display process to it. – kunif Jan 12 '18 at 01:19

2 Answers2

1

The following changes will make it work. A new method named add_sidemenu() has been added to class MainMenu and it is used as the value of the command= argument passed to the Button widget when it's being created.

In the new method it creates and saves a SideMenu instance. In your code this instance was getting deleted almost as soon as the it was created because it's not stored in variable. I also did the something similar with both the tk.PhotoImage instances—even it's not strictly necessary for the one in MainMenu because the tk.mainloop() call at the end of launch_MainMenu() prevents the local variable it's in from going out of scope too soon (so it's not deleted while it's still needed). I did it anyway, because that's generally a good practive to do it with all images used in tkinter (and folks not doing that is a big part of the reason there's so many related questions here).

I also tried to reformat your code so it follows PEP 8 - Style Guide for Python Code more closely. I strongly suggest you read and start following the guidelines in—your code will easier for other (and you) to read (as well as easing debugging and maintainence).

import tkinter as tk

class MainMenu(object):
    def __init__(self):
        self.launch_MainMenu()

    def launch_MainMenu(self):
        self._mainMenuWindow = tk.Tk()
        self.imageGIF = tk.PhotoImage(file="_test.gif");
        imageLabel = tk.Label(self._mainMenuWindow, image=self.imageGIF)
        imageLabel.grid(row=0, column=1, padx=10, pady=10)
        tk.Button(self._mainMenuWindow, text="Next Menu",
                  command=self.add_sidemenu).grid(row=0, column=2, padx=10)
        tk.mainloop()

    def add_sidemenu(self):
        self.sidemanu = SideMenu()
        self.sidemanu.grid(row=0, column=2, padx=10)

class SideMenu(object):
    def __init__(self):
        self.launch_sideMenu()

    def launch_sideMenu(self):
        self._sideWindow = tk.Toplevel()
        self.imageGIF2 = tk.PhotoImage(file="_test2.gif")
        self.imageLabel2 = tk.Label(self._sideWindow, image=self.imageGIF2)
        self.imageLabel2.grid(row=0, column=1, padx=10, pady=10)

mainMenu = MainMenu()
martineau
  • 119,623
  • 25
  • 170
  • 301
  • This is brilliant, thank you so much for stopping by to help me. The program I'm making is to help someone help other people who really needs it, and I am back on the road to finishing it! Have a great day! – JimmyCarlos Jan 11 '18 at 23:49
1

The problem is due to python's garbage collector. You're not keeping a reference to the instance of SideMenu so it's getting garbage-collected.

Due to a quirk of how tkinter is implemented, that causes the image object to be destroyed even though the widget itself is not destroyed.

It's highly unusual to have the target of a button be a class. Instead, create a proper function that creates an instance of SideMenu, and saves a reference to that object.

class MainMenu(object):
    ...
    def launch_ManMenu(self):
        ...
        tk.Button(..., command=self.sidemenu, ...)

    def sidemenu(self):
        self.sidemenu = SideMenu()

You also need to keep a reference of the image in SideMenu, just like you do in MainMenu:

class SideMenu(object):
    ...
    def launch_sideMenu(self):
        ...
        self.my_image = tk.PhotoImage(...)
        myCanvas.create_image(..., image=self.my_image)
        ...
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • This is brilliant, thank you so much for stopping by to help me. The program I'm making is to help someone help other people who really needs it, and I am back on the road to finishing it! Have a great day! I so wish I could green tick this answer as well! – JimmyCarlos Jan 11 '18 at 23:49