Search code examples
pythontkintermenu

Handling multiple instances of windows through menu in Tkinter


I have a menu with items through which I want to open separate windows:

from tkinter import *
from PIL import ImageTk, Image

def show_text():
    top = Toplevel()
    top.title("Text")
    top.geometry("200x100")
    my_label = Label(top, text="Text")
    my_label.pack()

def show_image():
    global my_image
    top = Toplevel()
    top.title("Graph")
    my_image = ImageTk.PhotoImage(Image.open("my_image.png"))
    my_label = Label(top, image = my_image)
    my_label.pack()

root = Tk()
root.title("Main window")
root.geometry("300x200")

my_menu = Menu(root)
root.config(menu=my_menu)

item_menu = Menu(my_menu)
my_menu.add_cascade(label="Info", menu=item_menu)
item_menu.add_command(label="Text", command=show_text)
item_menu.add_command(label="Graph", command=show_image)

root.mainloop()

The problem is that when I select an item from menu again I get another instance of window:

enter image description here

It's not a problem in case of windows with text, but in case of windows with picture when the new instance of window opens, the picture in the old instance of window disappear:

enter image description here

My questions:

  1. If I want to allow to have multiple instances of windows, then how can I prevent pictures in old instances of windows from disappearing?

  2. If I don't want to allow multiple instances of the same window, then how can I close old instance of window when create new (select menu item again)?

  3. Maybe more prudent decision would be to disable menu item after it was chosen and enable again after the corresponding window is closed. How can this be done?

P.S. Instead of my_image.png in the code you can use any suitable image on your computer.


Solution

  • Question 1: If I want to allow to have multiple instances of windows, then how can I prevent pictures in old instances of windows from disappearing?

    Your code does this. The previous windows' image don't disappear.

    Quetion 2: If I don't want to allow multiple instances of the same window, then how can I close old instance of window when create new (select menu item again)?

    Give different names for two toplevel windows say text_top and image_top. In the respective toplevel window functions, add a conditional statement as follows. Also make the toplevel widown global( add respective global declaration after importing tkinter which I've not mentioned here)

    def show_text():
        global text_top 
        
        if text_top.winfo_exists():
            text_top.destroy()
            
        text_top = Toplevel(root)
        # Rest of the function
    
    def show_image():
        global image_top
        
        if image_top.winfo_exists():
            image_top.destroy()
        image_top = Toplevel(root)
        # Rest of the function
    

    Question 3: Maybe more prudent decision would be to disable menu item after it was chosen and enable again after the corresponding window is closed. How can this be done?

    See the following code. changes given as comments inside the code

    from tkinter import *
    from PIL import ImageTk, Image
    
    def show_text():
    
        # A function to enable text_widget while closing the toplevel window
        def enable_text_menu():
            item_menu.entryconfig(0, state = NORMAL) # 0 is index
            top.destroy()
        
        top = Toplevel()
        top.title("Text")
        top.geometry("200x100")
        
        # to disable the menu item- syntax: item_menu.entryconfig(index, state=DISABLED)
        item_menu.entryconfig(0, state = DISABLED)
        my_label = Label(top, text="Text")
        my_label.pack()
        
        # the following line triggers the function enable_text_menu on closing Toplevel
        top.protocol("WM_DELETE_WINDOW", enable_text_menu)
    
    def show_image():
    
        # A function to enable graph_widget while closing the toplevel window
        def enable_image_menu():
            item_menu.entryconfig(1, state=NORMAL) # 1 is index
            top.destroy()
            
        global my_image
        top = Toplevel()
        top.title("Graph")
        
        # to disable the menu item- syntax: item_menu.entryconfig(index, state=DISABLED)
        item_menu.entryconfig(1, state=DISABLED)
        my_image = ImageTk.PhotoImage(Image.open("my_image.png"))
        my_label = Label(top, image = my_image)
        my_label.pack()
        
        # the following line triggers the function enable_image_menu on closing Toplevel
        top.protocol("WM_DELETE_WINDOW", enable_image_menu)
    
    root = Tk()
    root.title("Main window")
    root.geometry("300x200")
    
    my_menu = Menu(root)
    root.config(menu=my_menu)
    
    # In the following line, 'tearoff=0' for not including the dashed line on the top
    item_menu = Menu(my_menu, tearoff=0)
    my_menu.add_cascade(label="Info", menu=item_menu)
    item_menu.add_command(label="Text", command=show_text)
    item_menu.add_command(label="Graph", command=show_image)
    
    
    root.mainloop()