У меня есть меню с пунктами, через которые я хочу открывать отдельные окна:
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()
Проблема в том, что когда я снова выбираю элемент из меню, я получаю другой экземпляр окна:
Это не проблема в случае окон с текстом, но в случае окон с изображением, когда открывается новый экземпляр окна, изображение в старом экземпляре окна исчезает:
Мои вопросы:
Если я хочу разрешить иметь несколько экземпляров окон, то как я могу предотвратить исчезновение изображений в старых экземплярах окон?
Если я не хочу разрешать несколько экземпляров одного и того же окна, как мне закрыть старый экземпляр окна при создании нового (выберите пункт меню еще раз)?
Возможно, более разумным решением было бы отключить пункт меню после его выбора и снова включить после закрытия соответствующего окна. Как это можно сделать?
P.S. Вместо my_image.png в коде вы можете использовать любое подходящее изображение на вашем компьютере.
но в случае окон с изображением, когда новый экземпляр окна открывается, изображение в старом экземпляре окна исчезает:
Если я хочу разрешить иметь несколько экземпляров окон, то как я могу предотвратить исчезновение >>изображений в старых экземплярах окон?
Для вопроса 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. Что это такое, что исправляет, почему работает? Я не понимаю.
По вопросу 2. Да, похоже на добавление top.grab_set() в главное окно функциональных блоков, когда открыто новое окно.
Когда Python собирает мусор с объекта PhotoImage (например, когда вы возвращаетесь из функции, которая сохранила изображение в локальной переменной), изображение очищается, даже если оно отображается виджетом Tkinter. Чтобы избежать этого, программа должна хранить дополнительную ссылку на объект изображения. Простой способ сделать это — присвоить изображение атрибуту виджета.
Вопрос 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 и исключая.
Используйте атрибут окна для хранения ссылки на изображение, чтобы избежать сборки мусора, которая является причиной исчезновения изображения.