0

I have a Tkinter GUI with three buttons running a separate script each. Two of them load fine, however the third one throws up a NameError, saying one of my names isn't defined. However when I run the script not through the GUI, it runs fine.

This is the GUI code:

import sys
import os
import tkinter
import cv2
from tkinter.filedialog import askopenfilename
from tkinter import messagebox
import numpy as np 
import matplotlib.pyplot as plt 

top=tkinter.Tk()
top.geometry("300x350")
top.config(background='black')
top.title('Test')
top.resizable(height=False, width=False)

def thresholdCallBack():
    exec(open('motionindexthreshold.py').read())

def autoremoveCallBack():
    exec(open('motionindexgenerator.py').read())

def videoTaggingCallBack():
    exec(open('stepthrough.py').read())

def quitCallBack():
    top.destroy()

M = tkinter.Message(top, text='Test', width=280, background='black', foreground='white', font=('Courier', 28))
B = tkinter.Button(top,text="Define Motion Index Threshold",command= thresholdCallBack)
C = tkinter.Button(top,text="Autoremove Nonmovement Video Segments",command= autoremoveCallBack)
D = tkinter.Button(top,text="Tag Video Frames",command= videoTaggingCallBack)
E = tkinter.Button(top,text="Quit", command=quitCallBack)
B.config(height=5, width=80, background='red')
C.config(height=5, width=80, background='blue', foreground='white')
D.config(height=5, width=80, background='yellow')
E.config(height=5, width=80, background='green')
M.pack()
B.pack()
C.pack()
D.pack()
E.pack()
top.mainloop()

And this is the python script that crashes when a keypress is registered:

import cv2
import tkinter as tk
from tkinter.filedialog import askopenfilename
from tkinter import messagebox
import numpy as np 
import os
import matplotlib.pyplot as plt 
import sys


framevalues = []
count = 1

root = tk.Tk()
root.withdraw()

selectedvideo = askopenfilename()
selectedvideostring = str(selectedvideo)
cap = cv2.VideoCapture(selectedvideo)
length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))


def stanceTag():    
    framevalues.append('0' + ' ' + '|' + ' ' + str(int(cap.get(1))))
    print (str(int(cap.get(1))), '/', length) 
    print(framevalues)

def swingTag():
    framevalues.append('1' + ' ' + '|' + ' ' + str(int(cap.get(1))))
    print (str(int(cap.get(1))), '/', length)
    print(framevalues) 

def unsureTag():
    framevalues.append('-1' + ' ' + '|' + ' ' + str(int(cap.get(1))))
    print (str(int(cap.get(1))), '/', length) 
    print(framevalues)

def rewindFrames():
    cap.set(1,((int(cap.get(1)) - 2)))
    print (int(cap.get(1)), '/', length) 
    framevalues.pop()
    print(framevalues)  



while (cap.isOpened()): 
    ret, frame = cap.read()

    # check if read frame was successful
    if ret == False:
            break
    # show frame first
    cv2.imshow('frame',frame)

    # then waitKey
    frameclick = cv2.waitKey(0) & 0xFF

    if frameclick == ord('a'):
        swingTag()

    elif frameclick == ord('r'):
        rewindFrames()

    elif frameclick == ord('s'):
        stanceTag()

    elif frameclick == ord('d'):
        unsureTag()

    elif frameclick == ord('q'):
        with open((selectedvideostring + '.txt'), 'w') as textfile:
            for item in framevalues:
                textfile.write("{}\n".format(item))
        break

    else:
        continue

cap.release()
cv2.destroyAllWindows()

Does anyone have any ideas how to solve this issue?

Thanks

1
  • Something is being imported by your GUI script and not by the subscript. The NameError will tell you what name is missing. The fix would be to use a proper import to execute the scripts rather than exec. Commented Mar 17, 2017 at 19:27

1 Answer 1

2

If you need to run code from another python script, then you should use an import to get the other script and run a function in the other script. This will be a problem with your code since you have the core of your program outside of a function, so it would run as soon as it's imported. For the reason (and others) you should have all of your code in functions. You can detect if the code was imported or not by checking the __main__ attribute.

I restructured your code to move all the code into functions, then import it and call the appropriate function from the GUI buttons.

This is what your GUI code should look like:

import tkinter

import motionindexthreshold
import motionindexgenerator
import stepthrough

def main():
    top=tkinter.Tk()
    top.geometry("300x350")
    top.config(background='black')
    top.title('Test')
    top.resizable(height=False, width=False)

    M = tkinter.Message(top, text='Test', width=280, background='black', foreground='white', font=('Courier', 28))
    B = tkinter.Button(top,text="Define Motion Index Threshold",command=motionindexthreshold.main)
    C = tkinter.Button(top,text="Autoremove Nonmovement Video Segments",command=motionindexgenerator.main)
    D = tkinter.Button(top,text="Tag Video Frames",command=stepthrough.main)
    E = tkinter.Button(top,text="Quit", command=top.destroy)
    B.config(height=5, width=80, background='red')
    C.config(height=5, width=80, background='blue', foreground='white')
    D.config(height=5, width=80, background='yellow')
    E.config(height=5, width=80, background='green')
    M.pack()
    B.pack()
    C.pack()
    D.pack()
    E.pack()
    top.mainloop()

if __name__ == '__main__':
    main()

And this is what your module code should look like:

import cv2
import tkinter as tk
from tkinter.filedialog import askopenfilename

def stanceTag(cap, framevalues):    
    framevalues.append('0' + ' ' + '|' + ' ' + str(int(cap.get(1))))
    print (str(int(cap.get(1))), '/', length) 
    print(framevalues)

def swingTag(cap, framevalues):
    framevalues.append('1' + ' ' + '|' + ' ' + str(int(cap.get(1))))
    print (str(int(cap.get(1))), '/', length)
    print(framevalues) 

def unsureTag(cap, framevalues):
    framevalues.append('-1' + ' ' + '|' + ' ' + str(int(cap.get(1))))
    print (str(int(cap.get(1))), '/', length) 
    print(framevalues)

def rewindFrames(cap, framevalues):
    cap.set(1,((int(cap.get(1)) - 2)))
    print (int(cap.get(1)), '/', length) 
    framevalues.pop()
    print(framevalues)  


def main():
    framevalues = []
    count = 1

    selectedvideo = askopenfilename()
    selectedvideostring = str(selectedvideo)
    cap = cv2.VideoCapture(selectedvideo)
    length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    while (cap.isOpened()): 
        ret, frame = cap.read()

        # check if read frame was successful
        if ret == False:
                break
        # show frame first
        cv2.imshow('frame',frame)

        # then waitKey
        frameclick = cv2.waitKey(0) & 0xFF

        if frameclick == ord('a'):
            swingTag(cap, framevalues)

        elif frameclick == ord('r'):
            rewindFrames(cap, framevalues)

        elif frameclick == ord('s'):
            stanceTag(cap, framevalues)

        elif frameclick == ord('d'):
            unsureTag(cap, framevalues)

        elif frameclick == ord('q'):
            with open((selectedvideostring + '.txt'), 'w') as textfile:
                for item in framevalues:
                    textfile.write("{}\n".format(item))
            break

        else:
            continue

    cap.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
    # this is called if this code was not imported ... ie it was directly run
    # if this is called, that means there is no GUI already running, so we need to create a root
    root = tk.Tk()
    root.withdraw()
    main()

Obviously this is a guess; I have no way to test if this solves your issue, but I think it will.

Sign up to request clarification or add additional context in comments.

3 Comments

This answer would be much better if you could describe what you changed. Otherwise the OP has to compare your solution line-by-line to the original.
Thanks so much, this is really helpful, and has fixed most of my problems - just one issue, when I press any key to run one of my functions, I'm getting a name error that 'framevalues' isn't defined. How can I fix this? Cheers
Ah I overlooked that you need that in your function too. Just pass it in like 'cap'. I've updated my answer to show that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.