0
votes

I am making a funktion that calls itself, but i'm getting error: RecursionError: maximum recursion depth exceeded while calling a Python object, Is there a way around this. More specifically i am getting it for line 35, ok, frame = cap.read() I cant put a while loop because of the .after funktion in my program.

import cv2
from tkinter import *
import PIL
from PIL import Image, ImageTk

root = Tk()
root.bind('<Escape>', lambda e: root.quit())
lmain = Label(root)
lmain.pack()

print("[INFO] Making variables")
ImageSource = 0
window_name = "AutoCam"
width = 600
height = 800
cap = cv2.VideoCapture(ImageSource)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
print("[INFO] Made variables ")


def ShowFrame(frame):
    print("[INFO] making image.")
    cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
    img = PIL.Image.fromarray(cv2image)
    imgtk = ImageTk.PhotoImage(image=img)
    lmain.imgtk = imgtk
    lmain.configure(image=imgtk)
    print("[INFO] After 10 initializing")
    lmain.after(10, CheckSource)
    print("[INFO] Showed image")


def CheckSource():
    ok, frame = cap.read()

    if ok:
        print("[INFO] Ok is triggered")

        if cv2.waitKey(1) & 0xFF == ord('q'):
            cv2.destroyAllWindows()
            cv2.waitKey(0)
            print("[INFO] Exiting app after command")

        ShowFrame(frame)

    else:

        lmain.after(10, CheckSource())


CheckSource()
root.mainloop()

Any and all help would be very appreciated. Could someone also explain how to avoid this for future use?

[EDIT]

the error message is:
  Traceback (most recent call last):
   File "C:/Users/Gotta/Documents/AutoCamPy.py", line 52, in <module>
   CheckSource()
File "C:/Users/Gotta/Documents/AutoCamPy.py", line 49, in CheckSource
  lmain.after(10, CheckSource())
File "C:/Users/Gotta/Documents/AutoCamPy.py", line 49, in CheckSource
  lmain.after(10, CheckSource())
File "C:/Users/Gotta/Documents/AutoCamPy.py", line 49, in CheckSource
  lmain.after(10, CheckSource())
 [Previous line repeated 995 more times]
File "C:/Users/Gotta/Documents/AutoCamPy.py", line 35, in CheckSource
  ok, frame = cap.read()
RecursionError: maximum recursion depth exceeded while calling a Python 
object
2
dont get any error when i run your code. - AD WAN
what? i get a RecursionError: maximum recursion depth exceeded while calling a Python object - GottaAimHigherPal
you have to run it while not having the camera plugged in... - GottaAimHigherPal
because the app is suppose to automatically show camera feed once connected - GottaAimHigherPal
You have extra parens: .after(..., CheckSource()) - change to .after(..., CheckSource) - Mulan

2 Answers

1
votes

With the stack trace you can identify that the recursion is happening in line 49 (it is the line that is being executed many times)

File "C:/Users/Gotta/Documents/AutoCamPy.py", line 49, in CheckSource
  lmain.after(10, CheckSource())
File "C:/Users/Gotta/Documents/AutoCamPy.py", line 49, in CheckSource
  lmain.after(10, CheckSource())
File "C:/Users/Gotta/Documents/AutoCamPy.py", line 49, in CheckSource
  lmain.after(10, CheckSource())
 [Previous line repeated 995 more times]

The reason why it is reaching the recursion limit is because the .after function (https://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.after-method) expects a callback function as the second argument, but you are passing the result of calling CheckSource instead. You should pass CheckSource instead of CheckSource() as the second argument:

lmain.after(10, CheckSource)

1
votes

To handle this in a simple way you can write a try/except statement to catch this error. Until you know exactly what the error is you can use a catch all but I would recommend you hand the specific error once you know what it is.

That said I changed a few things in your code to clean it up a bit and to follow the PEP8 standard more closely.

You really should do import tkinter as tk instead of using *. This will help prevent overwriting of methods that have been imported.

Next your lambda was over kill just do root.quit instead. We want to save a reference to the command instead of executing it and we do this by removing the parenthesis. The same problem existed with your 2nd after statement.

Lastly you import PIL and then specifically import from PIL. You do not need to do both. If you just need Image and ImageTk then just do from PIL import Image, ImageTk and if you need many things from PIL then you can simple do import PIL and use the PIL. prefix from there.

Here is your cleaned up code with a try/except statement. Let me know if you have any questions.

import tkinter as tk
from PIL import Image, ImageTk
import cv2


root = tk.Tk()
root.bind('<Escape>', root.quit)
lmain = tk.Label(root)
lmain.pack()
ImageSource = 0
window_name = "AutoCam"
width = 600
height = 800
cap = cv2.VideoCapture(ImageSource)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)


def show_frame(frame):
    cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
    img = Image.fromarray(cv2image)
    imgtk = ImageTk.PhotoImage(image=img)
    lmain.imgtk = imgtk
    lmain.configure(image=imgtk)
    lmain.after(10, check_source)


def check_source():
    try:
        ok, frame = cap.read()
        if ok:
            if cv2.waitKey(1) & 0xFF == ord('q'):
                cv2.destroyAllWindows()
                cv2.waitKey(0)
            show_frame(frame)
        else:
            lmain.after(10, check_source)
    except:
        print('Connection failed for some reason!')


check_source()
root.mainloop()