Drake Sunryder
Drake Sunryder

Reputation: 15

Receiving "NoneType Object is not Callable" error when calling function via .trace()

Okay, so I'm trying to put together a really simple app that essentially just takes a scanned barcode value, which is tied to an image file, and goes into a dictionary of images, finds the image whose key matches the barcode value, and displays that image in the Tkinter window.

I actually got it working consistently when just using a raw input() value, but when I tried to incorporate an Entry box into the window to take the barcode value, that's when I ran into problems.

I want the Entry widget to kick off a function whenever it's edited, so that all that needs to be done is scan the barcode and the image will appear. I looked up solutions to this and the most common one I found was to use a StringVar, tie it to the Entry widget, and then use .trace() to kick off the desired function whenever the value in the Entry widget is altered.

The snag is that whenever I scan the barcode into the Entry box, I get the following error:

Exception in Tkinter callback  
Traceback (most recent call last):  
  File "c:\program files\python37\Lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)  
TypeError: 'NoneType' object is not callable  

This is my full code. I tried to comment out and explain the process as best I could. Naturally, it won't be able to grab the image files and populate the dictionary with them, but hopefully just looking at it, you'll be able to tell me where I went wrong.

from PIL import Image
from PIL import ImageTk as itk
import tkinter as tk
import cv2
import glob
import os, os.path


# INITIALIZE TKINTER WINDOW #
window = tk.Tk()
window.title('Projector Test')
#window.overrideredirect(1)


# Function to kick off whenever Tkinter Entry value is edited. Grabs value of StringVar and assigns it to variable 'barcode'. Checks to see if the barcode
# value is in 'images' dictionary. If so, grab image and display on Tkinter Canvas. If not, display Error message image.
def barcodeScanImage():
  barcode = str(sv.get())

  if barcode in images:
    image_file = images.get(barcode)
    scanImage = itk.PhotoImage(image_file)
    width, height = image_file.size
    canvas.create_image(0, 0, image = scanImage, anchor = tk.NW)

  else:
    image_file = images.get('error.gif')
    errorImage = itk.PhotoImage(image_file)
    width, height = image_file.size
    canvas.create_image(0, 0, image = errorImage, anchor = tk.NW)


# Create Dictionary 'images' to store image files in. #
images = {}


# Iterate through projectorImages folder in directory and store each image found there in the 'images' dictionary, with its Key as its filename. #
for filename in os.listdir('projectorImages\\'):
  image = Image.open(os.path.join('projectorImages\\', filename))
  images[filename] = image


# Create startImage variable. Use .size function to get its width and height, which will be plugged into the tk.Canvas width and height arguments.
# This ensures the displayed image will be displayed in its entirety.
startImage = images.get('start.gif')
width, height = startImage.size
canvas = tk.Canvas(master = window, width = width, height = height)


# Create startImageReady variable reference to the same image file, using the itk.PhotoImage function to convert it into a readable format for Tkinter.
# Then, use canvas.create_image to actually display the image in the Tkinter canvas.
startImageReady = itk.PhotoImage(images.get('start.gif'))
canvas.pack()
canvas.create_image(0, 0, image = startImageReady, anchor = tk.NW)

sv = tk.StringVar()
entry = tk.Entry(master = window, textvariable = sv)
sv.trace("w", callback = barcodeScanImage())
entry.pack()


window.mainloop()

Thanks much for your time. I've been trying to figure out what's causing this issue, but I'm at the end of my beginner's rope! Lol

Upvotes: 0

Views: 1048

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 386285

Consider this line of code:

sv.trace("w", callback = barcodeScanImage())

It is functionally identical to this code:

result = barcodeScanImage()
sv.trace("w", callback=result)

Since barcodeScanImage() returns None, it's the same as this:

sv.trace("w", callback=None)

When you call trace, you must give it a reference to a function (note the missing ()):

sv.trace("w", callback=barcodeScanImage)

However, when you set up a trace, tkinter will pass some additional arguments to the function which you need to be prepared to accept. Since you're not using them, you can just ignore them:

def barcodeScanImage(*args):
   ...

For more information on the arguments that are passed in, see this question: What are the arguments to Tkinter variable trace method callbacks?

Upvotes: 3

Related Questions