Python Forum
tkinter.TclError: can't invoke "canvas" command
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
tkinter.TclError: can't invoke "canvas" command
#1
I am getting this tcl error and unable to solve it. Here is the full exception. This happens when I exit the program
Error:
Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\INDIAN\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1883, in __call__ return self.func(*args) File "C:\Users\INDIAN\Desktop\python exercises\pygametooth\untiltledtry 15-2-23.py", line 65, in select_folder canvas.delete("all") File "C:\Users\INDIAN\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 2818, in delete self.tk.call((self._w, 'delete') + args) _tkinter.TclError: invalid command name ".!canvas" Traceback (most recent call last): File "C:\Users\INDIAN\Desktop\python exercises\pygametooth\untiltledtry 15-2-23.py", line 135, in <module> canvas = Canvas(root, width=800, height=500, bg="white") File "C:\Users\INDIAN\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 2683, in __init__ Widget.__init__(self, master, 'canvas', cnf, kw) File "C:\Users\INDIAN\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 2567, in __init__ self.tk.call( _tkinter.TclError: can't invoke "canvas" command: application has been destroyed >>>
import tkinter as tk from tkinter import Canvas, NW from tkinter import filedialog import os import pygame import cv2 import numpy as np from PIL import Image, ImageTk image_tk = None # Placeholder variable for PhotoImage # Initialize Pygame pygame.init() def select_folder(): # Open a file dialog to select the folder root.filename = filedialog.askdirectory() # Load the images from the selected folder images = [] for f in os.listdir(root.filename): if f.endswith((".png", ".jpg", ".bmp", ".dcm")): image = cv2.imread(os.path.join(root.filename, f)) if image is not None: images.append(image) # Set the initial image current_image = 0 # Main loop running = True while running: # Handle events for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: current_image = (current_image - 1 + len(images)) % len(images) elif event.key == pygame.K_RIGHT: current_image = (current_image + 1) % len(images) # Clear the canvas canvas.delete("all") # Get the dimensions of the current image height, width = images[current_image].shape[:2] # Set the rotation angle angle = 90 # Calculate the center of the image center = (width // 2, height // 2) # Create the rotation matrix rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0) # Calculate the new dimensions of the rotated image new_width, new_height = height, width # Rotate the image rotated = cv2.warpAffine(images[current_image], rotation_matrix, (new_width, new_height)) # Convert the BGR image to RGB rgb_image = cv2.cvtColor(rotated, cv2.COLOR_BGR2RGB) # Convert the OpenCV image to a PIL ImageTk pil_image = Image.fromarray(np.rot90(rgb_image, 3)) ## pil_image = Image.fromarray(rgb_image) image_tk = ImageTk.PhotoImage(image=pil_image) # Display the image in the tkinter window canvas.create_image(0, 0, image=image_tk, anchor=NW) canvas.image = image_tk # Update the screen root.update() def exit_program(): root.destroy() def on_key_press(event): if event.keysym == "Left": pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {"key": pygame.K_LEFT})) elif event.keysym == "Right": pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {"key": pygame.K_RIGHT})) # Create the GUI ##root = tk.Tk() root.title("Multi-angle Image Viewer") root.geometry("800x600") # Add a button to select the folder button = tk.Button(root, text="Select Folder", command=select_folder) button1 = tk.Button(root, text="Exit", command=exit_program) button.pack() button1.pack() # Create the canvas to display the images canvas = Canvas(root, width=800, height=500, bg="white") canvas.pack() # Bind the arrow keys to the canvas canvas.bind("<Key>", on_key_press) canvas.focus_set() # Run the GUI # Run the GUI root.mainloop() ### Create the canvas to display the images canvas = Canvas(root, width=800, height=500, bg="white") canvas.pack() # Run the GUI root.mainloop()
Reply
#2
Remove root.mainloop() before the call Canvas(...). You don't need root.mainloop() more than once.
Reply
#3
Why did you post code that is not the code that generated the error message?

The reason you got an error is because you destroyed the root window. I get the same error with the code below when I close the root window using the decorations in the window frame.
import tkinter as tk root = tk.Tk() tk.Label(root, text="Hello World").pack(padx=50, pady=50) root.mainloop() print("Must have closed the main window") tk.Label(root, text="Goodbye World").pack(padx=50, pady=50) root.mainloop()
Error:
Must have closed the main window Traceback (most recent call last): File "c:\Users\djhys\Documents\python\musings\junk.py", line 8, in <module> tk.Label(root, text="Goodbye World").pack(padx=50, pady=50) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\Python311\Lib\tkinter\__init__.py", line 3214, in __init__ Widget.__init__(self, master, 'label', cnf, kw) File "C:\Program Files\Python311\Lib\tkinter\__init__.py", line 2628, in __init__ self.tk.call( _tkinter.TclError: can't invoke "label" command: application has been destroyed
Once you destroy the window it is gone. You cannot use it again. Not only is the window gone, but all the widgets in the window are invalid.

What are you trying to accomplish with the destroy button and multiple mainloops?
Reply
#4
the double mainloop() was an oversight sorry . i have tried creating the canvas before the select function but am still getting the same exception can some one help resolve the issue. the exception occurs when I press the exit button.
import tkinter as tk from tkinter import Canvas, NW from tkinter import filedialog import os import pygame import cv2 import numpy as np from PIL import Image, ImageTk image_tk = None # Placeholder variable for PhotoImage # Initialize Pygame pygame.init() # Set the window size ##window_size = (800, 600) # Create the window ##screen = pygame.display.set_mode(window_size) # Create the GUI root = tk.Tk() root.title("Multi-angle Image Viewer") root.geometry("800x600") ##Create the canvas to display the images canvas = Canvas(root, width=800, height=500, bg="white") canvas.pack() def select_folder(): # Open a file dialog to select the folder root.filename = filedialog.askdirectory() # Load the images from the selected folder images = [] for f in os.listdir(root.filename): if f.endswith((".png", ".jpg", ".bmp", ".dcm")): image = cv2.imread(os.path.join(root.filename, f)) if image is not None: images.append(image) # Set the initial image current_image = 0 # Main loop running = True while running: # Handle events for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: current_image = (current_image - 1 + len(images)) % len(images) elif event.key == pygame.K_RIGHT: current_image = (current_image + 1) % len(images) # Clear the canvas canvas.delete("all") # Get the dimensions of the current image height, width = images[current_image].shape[:2] # Set the rotation angle angle = 90 # Calculate the center of the image center = (width // 2, height // 2) # Create the rotation matrix rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0) # Calculate the new dimensions of the rotated image new_width, new_height = height, width # Rotate the image rotated = cv2.warpAffine(images[current_image], rotation_matrix, (new_width, new_height)) # Convert the BGR image to RGB rgb_image = cv2.cvtColor(rotated, cv2.COLOR_BGR2RGB) # Convert the OpenCV image to a PIL ImageTk pil_image = Image.fromarray(np.rot90(rgb_image, 3)) image_tk = ImageTk.PhotoImage(image=pil_image) # Display the image in the tkinter window canvas.create_image(0, 0, image=image_tk, anchor=NW) canvas.image = image_tk # Update the screen root.update() def exit_program(): root.destroy() def on_key_press(event): if event.keysym == "Left": pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {"key": pygame.K_LEFT})) elif event.keysym == "Right": pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {"key": pygame.K_RIGHT})) # Add a button to select the folder button = tk.Button(root, text="Select Folder", command=select_folder) button1 = tk.Button(root, text="Exit", command=exit_program) button.pack() button1.pack() # Bind the arrow keys to the canvas canvas.bind("<Key>", on_key_press) canvas.focus_set() # Run the GUI # Run the GUI root.mainloop()
Error:
Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\INDIAN\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1883, in __call__ return self.func(*args) File "C:/Users/INDIAN/Desktop/python exercises/pygametooth/wct.py", line 62, in select_folder canvas.delete("all") File "C:\Users\INDIAN\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 2818, in delete self.tk.call((self._w, 'delete') + args) _tkinter.TclError: invalid command name ".!canvas" >>>
Reply
#5
Your program still has multiple mainloops(). One on line 117 and another on line 50.

Your program layout is wrong. If you want to combine tkinter and pygame, you cannot have a pygame loop and a tkinter loop. I don't see why you are using pygame at all, but assuming that there is some need to use pygame, I would implement the pygame event loop as a periodic function call.

In this code I stripped out all the image stuff to focus on running the pygame event loop as a periodic event. I do the same thing with the image rotate loop. It is pretty obvious that there is no need for pygame at all. The on_key_press() function can adjust the current image directly.
import tkinter as tk from tkinter import filedialog import pygame def select_folder(): """Open a folder and load image files""" root.filename = filedialog.askdirectory() # Load images def pygame_event_handler(): if running: for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: print("LEFT") elif event.key == pygame.K_RIGHT: print("Right") root.after(20, pygame_event_handler) def rotate_images(): """Does image rotation stuff""" if running: # Do image rotation stuff root.after(10, rotate_images) def exit_program(): global running running = False root.destroy() def on_key_press(event): if event.keysym == "Left": pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {"key": pygame.K_LEFT})) elif event.keysym == "Right": pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {"key": pygame.K_RIGHT})) running = True pygame.init() root = tk.Tk() canvas = tk.Canvas(root, width=800, height=500, bg="white") canvas.pack() button = tk.Button(root, text="Select Folder", command=select_folder) button1 = tk.Button(root, text="Exit", command=exit_program) button.pack() button1.pack() # Bind the arrow keys to the canvas canvas.bind("<Key>", on_key_press) canvas.focus_set() pygame_event_handler() rotate_images() root.mainloop()
Reply
#6
Thanks deanhystad
I am trying to make a GUI in tkinter where all the image files from a folder run in a continuous loop controlled by the arrow keys. I have a few sets of images of objects from multiple angles and thus want to create a viewer which shows them as if the object is rotating. hence the use of pygame. Can you suggest a simpler alternative.
Reply
#7
I don't think pygame helps at all. Use "after" to periodically call a method that spins the image a small amount. If you run the method 20 times a second the spinning will look continuous.
Reply
#8
Can you please post a sample code for it.
(Feb-21-2023, 12:41 PM)deanhystad Wrote: I don't think pygame helps at all. Use "after" to periodically call a method that spins the image a small amount. If you run the method 20 times a second the spinning will look continuous.
Reply
#9
Quote:Can you please post a sample code for it.
I already have, in my second response to your post. Actually, I did it twice; once for rotating the image, and another for processing pygame events. Third time's the charm?

I would dump pygame and the canvas and do something like this:
import math import statistics import tkinter as tk from tkinter import filedialog from PIL import Image, ImageTk from pathlib import Path import numpy as np IMAGE_SIZE = (100, 100) # Resize all images to this size LABEL_SIZE = 140 # Max size of rotated image class SpinningImageButton(tk.Frame): """A Button that has an image you can rotate""" def __init__(self, parent, file, *args, command=None, **kwargs): super().__init__(parent, *args, **kwargs) self.running = False self.index = 0 self.command = command # Guess a background color for the image by using the # most common pixel color. Use this to fill extra pixels # when the image is rotated image = Image.open(file) pixels = np.array(image) pixels = map(tuple, pixels.reshape(-1, pixels.shape[-1])) rgb = statistics.mode(pixels)[:3] # Create multiple images which flashed in succession # make it appear like the image is spinning image.thumbnail(IMAGE_SIZE) self.images = [ ImageTk.PhotoImage(image.rotate(angle, fillcolor=rgb, expand=True)) for angle in range(0, 360, 10) ] # Make a label to display the image. self.image = tk.Label( self, image=self.images[0], width=LABEL_SIZE, height=LABEL_SIZE, bg=f'#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}') self.image.pack(anchor=tk.CENTER) self.image.bind('<Button-1>', func=self._click) def _click(self, *args): """Called when mouse clicks on me""" if self.command: self.command(self) def _next(self, period=20): """Display successive images to make it appear the image is spinning""" if self.running: self.index = (self.index + 1) % len(self.images) self.image.config(image=self.images[self.index]) self.after(period, self._next, period) def start(self, period=20): """Start spinning the image""" self.running = True self._next(period) def stop(self): """Stop spinning the image""" self.running = False class MyWindow(tk.Tk): """Window to show off my fancy spinning buttons""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Make a menubar instead of a bunch of buttons menubar = tk.Menu(self) menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="File", menu=menu) menu.add_command(label="Open", command=self.open_folder) menu.add_separator() menu.add_command(label="Quit", command=self.destroy) self.config(menu=menubar) self.images = [] self.frame = tk.Frame(self, width=300, height=300) self.frame.pack(expand=True, fill=tk.BOTH) def open_folder(self): """Open folder and populate window with image buttons""" folder = filedialog.askdirectory() if folder: # Replace old image buttons with new for image in self.images: image.grid_forget() self.images = [ SpinningImageButton(self.frame, file, command=self.toggle) for file in Path(folder).iterdir() if file.suffix in ('.png', '.jpg', '.bmp', '.dcm') ] # Organize the images on a grid. cols = int(math.ceil(len(self.images)**0.5)) for i, image in enumerate(self.images): image.grid(row=i // cols, column=i % cols) def toggle(self, image): """Toggle image spinning on/off when button clicked""" if image.running: image.stop() else: image.start(20) MyWindow().mainloop()
Instead of a canvas, I made my own image buttons that know how to spin their image. By making buttons instead pasting images on a canvas, I can use a layout manager (grid in this case) to organize the images and resize the screen.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] tkinter.TclError: expected integer but got " " TWB 2 7,556 Feb-19-2023, 05:11 PM
Last Post: TWB
  [Tkinter] Clickable Rectangles Tkinter Canvas MrTim 4 15,122 May-11-2021, 10:01 PM
Last Post: MrTim
  [Tkinter] Draw a grid of Tkinter Canvas Rectangles MrTim 5 13,609 May-09-2021, 01:48 PM
Last Post: joe_momma
  [Tkinter] _tkinter.TclError: can't invoke "destroy" command: application has been destroyed knoxvilles_joker 6 23,828 Apr-25-2021, 08:41 PM
Last Post: knoxvilles_joker
  .get() invoke after a button nested press iddon5 5 5,669 Mar-29-2021, 03:55 AM
Last Post: deanhystad
Thumbs Up tkinter canvas; different page sizes on different platforms? philipbergwerf 4 7,797 Mar-27-2021, 05:04 AM
Last Post: deanhystad
  Continue command in python tkinter? MLGpotato 7 13,992 Mar-27-2021, 04:59 AM
Last Post: deanhystad
  how to resize image in canvas tkinter samuelmv30 2 22,881 Feb-06-2021, 03:35 PM
Last Post: joe_momma
  "tkinter.TclError: NULL main window" Rama02 1 8,237 Feb-04-2021, 06:45 PM
Last Post: deanhystad
  how to rotate lines in tkinter canvas helpmewithpython 1 5,137 Oct-06-2020, 06:56 PM
Last Post: deanhystad

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020
This forum uses Lukasz Tkacz MyBB addons.
Forum use Krzysztof "Supryk" Supryczynski addons.