Обработка нескольких экземпляров окон через меню в Tkinter

У меня есть меню с пунктами, через которые я хочу открывать отдельные окна:

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

Проблема в том, что когда я снова выбираю элемент из меню, я получаю другой экземпляр окна:

Это не проблема в случае окон с текстом, но в случае окон с изображением, когда открывается новый экземпляр окна, изображение в старом экземпляре окна исчезает:

Мои вопросы:

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

  2. Если я не хочу разрешать несколько экземпляров одного и того же окна, как мне закрыть старый экземпляр окна при создании нового (выберите пункт меню еще раз)?

  3. Возможно, более разумным решением было бы отключить пункт меню после его выбора и снова включить после закрытия соответствующего окна. Как это можно сделать?

P.S. Вместо my_image.png в коде вы можете использовать любое подходящее изображение на вашем компьютере.

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

acw1668 31.08.2024 03:50
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

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

Для вопроса 2: используйте Grab_set(), чтобы предотвратить открытие нового окна.

Пожалуйста, задавайте вопросы только по одному.

Добавьте в строку 17 my_label.image = my_image. для эталонного изображения.

my_label.image = my_image

Скриншот:

Решение для моего вопроса 1 работает. Можете ли вы немного объяснить, что делает эта строка кода: my_label.image = my_image? Я создаю метку с изображением (Label(top, image = my_image)) и затем снова назначаю изображение метке с помощью my_label.image = my_image. Что это такое, что исправляет, почему работает? Я не понимаю.

Helios 31.08.2024 07:24

По вопросу 2. Да, похоже на добавление top.grab_set() в главное окно функциональных блоков, когда открыто новое окно.

Helios 31.08.2024 07:48

Когда Python собирает мусор с объекта PhotoImage (например, когда вы возвращаетесь из функции, которая сохранила изображение в локальной переменной), изображение очищается, даже если оно отображается виджетом Tkinter. Чтобы избежать этого, программа должна хранить дополнительную ссылку на объект изображения. Простой способ сделать это — присвоить изображение атрибуту виджета.

toyota Supra 31.08.2024 11:48
Ответ принят как подходящий

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

Ваш код делает это. Изображение предыдущих окон не исчезает.

Вопрос 2: Если я не хочу разрешать несколько экземпляров одного и того же окна, как мне закрыть старый экземпляр окна при создании нового (выберите пункт меню еще раз)?

Дайте разные имена двум окнам верхнего уровня, например text_top и image_top. В соответствующих оконных функциях верхнего уровня добавьте условный оператор следующим образом. Также сделайте Widown верхнего уровня глобальным (добавьте соответствующее глобальное объявление после импорта tkinter, о котором я здесь не упоминал)

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

Вопрос 3: Возможно, более разумным решением было бы отключить пункт меню после его выбора и снова включить после закрытия соответствующего окна. Как это можно сделать?

См. следующий код. изменения, представленные в виде комментариев внутри кода

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

>> «добавьте соответствующее глобальное объявление после импорта tkinter, о котором я здесь не упомянул». Вместо этого я создал «if ... winfo_exists()» с помощью операторов try и исключая.

Helios 31.08.2024 12:55

Другие вопросы по теме