[Tkinter] Showing windows after one another - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [Tkinter] Showing windows after one another (/thread-41652.html) |
Showing windows after one another - Ben123 - Feb-22-2024 import tkinter as tk from PIL import Image, ImageTk class Class1(): def __init__(self) -> None: self.root = tk.Tk() self.root.geometry('800x800') def RunWindow(self): self.root.mainloop() def CloseWindow(self): self.root.quit() def ShowWindow(self): image_og = Image.open('IMG_0126.jpg').resize((500, 350)) self.image_tk = ImageTk.PhotoImage(image_og) PicLabel = tk.Label(self.root, image = self.image_tk) PicLabel.place(relx = 0.1, rely = 0.1) nextButton = tk.Button(self.root,text = 'next', command = self.CloseWindow) nextButton.place(relx = 0.7, rely = 0.8) test = Class1() test.ShowWindow() test.RunWindow() print('yes') test1 = Class1() test1.ShowWindow() test1.RunWindow()When this is run it comes up with an error message saying Traceback (most recent call last): File "DocumentLocation", line 31, in <module> test1.ShowWindow() File "DocumentLocation", line 20, in ShowWindow PicLabel = tk.Label(self.root, image = self.image_tk) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/tkinter/__init__.py", line 3214, in __init__ Widget.__init__(self, master, 'label', cnf, kw) File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/tkinter/__init__.py", line 2628, in __init__ self.tk.call( _tkinter.TclError: image "pyimage2" doesn't exist I don't understand what the issue is? any help would be appreciated RE: Showing windows after one another - deanhystad - Feb-22-2024 quit() exits mainloop. It does not destroy the window. You can see that in the example below. import tkinter as tk class Class1(tk.Tk): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.entry = tk.Entry(self) self.entry.pack() tk.Button(self, text='next', command=self.quit).pack() self.mainloop() def text(self): return self.entry.get() test = Class1() print(test.text())If I type "Hello World" in the entry and press the next button, it prints If quit() destroyed the window, there would be no entry widget that I could query to get the entered text.I run he program again, enter "Hello World" in the entry and press the close window frame decoration (X), This deletes the window. Since there are no other windows this also exits the mainloop. When I try to get the entered text I get an exception. There is no entry widget because it got destroyed along with the window. Your problem is related, but the opposite. When you press the "next" button, you exit the mainloop, but the window is still there. Then you commit the unforgiveable sin of calling Tk() a second time. This not only creates a top level window, it also re-initializes the tkinter framework. I cannot say exactly why this raises the error we see, but it is something you should never do, and we need to expect bad things to happen. For your example you want to use destroy, not quit. You can easily test with your existing code by destroying the window using the X window decoration. I have a question. Why are you destroying a window, and then creating a new one? This is an unusual pattern and there's probably a better way to do what you want to do. RE: Showing windows after one another - menator01 - Feb-22-2024 I would just update the window with the data to be displayed. Quick example import tkinter as tk class Window: def __init__(self, parent): parent.geometry('400x400+350+350') parent['pady'] = 8 # Create a list and starting point self.colors = ['orange', 'red', 'green', 'gold', 'brown', 'purple'] self.index = 0 # Label and buttons self.label = tk.Label(parent, text=f'Background Color is {self.colors[self.index]}', font=(None, 20, 'normal'), bg=self.colors[self.index]) self.label.pack(expand=True, fill='both') self.back_button = tk.Button(parent, text='Back', command=self._prev) self.back_button.pack(side='left', padx=8, pady=8) self.next_button = tk.Button(parent, text='Next', command=self._next) self.next_button.pack(side='left', padx=8, pady=8) # Method for going backwards in the list def _prev(self): if self.index <= 0: self.index = len(self.colors)-1 else: self.index -= 1 self.update() # Method for going forward in the list def _next(self): if self.index >= len(self.colors)-1: self.index = 0 else: self.index += 1 self.update() # Update the labels. This could be put in the above methods def update(self): self.label['text'] = f'Background Color is {self.colors[self.index]}' self.label['bg'] = self.colors[self.index] if __name__ == '__main__': root = tk.Tk() Window(root) root.mainloop() RE: Showing windows after one another - Ben123 - Feb-23-2024 Thank you very much for your help. I realised yesterday when looking on stack overflow for a solution that tk() should never be called more than once. I am self taught and therefore if the first video i watched didn't warn me then I was doomed. I have now reworked my code with toplevel windows and no longer need to destroy things randomly. RE: Showing windows after one another - deanhystad - Feb-23-2024 You can call Tk() more than once. I do that here to display all the png files in my games folder. import tkinter as tk from pathlib import Path class ShowImage(tk.Tk): def __init__(self, image_file): super().__init__() image=tk.PhotoImage(file=image_file) tk.Label(self, image=image).pack() tk.Button(self, text='Next Image', command=self.destroy).pack() self.mainloop() for image_file in Path("games").glob("*.png"): ShowImage(image_file)This works because pressing the Next button destroys the only window, ending the tkinter session. To draw another tkinter window I need to call Tk() again. The program may draw dozens of windows, but each window is a completely new tkinter session. But that is a silly way to display multiple images. Each time you press the Next button you get a new window. The next window might appear in a different location on the screen, forcing you to move the mouse to press the button again. A better solution is to use the one button to show all the images. This program also displays image files in a directory, but it does it using the same window for each image. import tkinter as tk from pathlib import Path class ShowImage(tk.Tk): """Loop through images in a folder.""" def __init__(self, folder="."): super().__init__() self.label = tk.Label(self) self.label.pack() tk.Button(self, text='Next Image', command=self.next_image).pack() self.files = Path(folder).iterdir() self.next_image() def next_image(self): """Display next image from folder.""" try: # Loop through files looking for image file to display. while image_file := next(self.files): try: # Try to open file as an image. self.image = tk.PhotoImage(file=image_file) self.label["image"] = self.image return except tk.TclError: pass # File was not an image file. Try next file. except StopIteration: self.destroy() # No more files.A fun thing about this program is its use of "duck typing". Some of the files in the folder might not be image files. Some of the files might be image files but aren't in a format that is understood by tk.PhotoImage. Instead of trying to filter out what files to display and what to skip ahead of time, the program tries to open the file as an image. If this fails, the program catches the error and tries the next file. |