I’m trying to build a tkinter GUI to show frames from 2 captures side-by-side.
This code works.
import cv2, numpy as np
from tkinter import NW, Tk, Canvas, PhotoImage
def photo_image(img):
h, w = img.shape[:2]
data = f'P6 {w} {h} 255 '.encode() + img[..., ::-1].tobytes()
return PhotoImage(width=w, height=h, data=data, format='PPM')
def update():
ret1, img1 = cap1.read()
ret2, img2 = cap2.read()
if ret1:
photo = photo_image(np.hstack((img1, img2)))
canvas.create_image(0, 0, image=photo, anchor=NW)
canvas.image = photo
root.after(15, update)
root = Tk()
root.title("Video")
cap1 = cv2.VideoCapture('/tmp/fto-20241229_184824_Sports2D.mp4')
cap2 = cv2.VideoCapture('/tmp/dtl-20241229_184824_Sports2D.mp4')
canvas = Canvas(root, width=1650, height=700)
canvas.pack()
update()
root.mainloop()
cap1.release()
cap2.release()
But when I try to implement the same code in a class I get the error
when I slide through the frames. ie
pyimage2" doesn’t exist
pyimage3" doesn’t exist
pyimage4" doesn’t exist
_tkinter.TclError: image "pyimage2" doesn't exist
import tkinter as tk
import cv2
import numpy as np
from tkinter import *
from tkinter.ttk import *
class Application(tk.Tk):
def __init__(self, width, height, video1, video2):
super().__init__()
self.root = tk.Tk()
self.root.option_add("*tearOff", False)
self.root.title("Golf Swing Analysis")
self.photo = None
self.style = Style()
self.width = width
self.height = height
self.cap1 = cv2.VideoCapture(video1)
self.cap2 = cv2.VideoCapture(video2)
self.cap1.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
self.cap1.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
self.cap2.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
self.cap2.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
# Create toolbar
toolbar = tk.Frame(self.root, bd=1, height=50, relief=tk.RAISED)
toolbar.pack(side=tk.TOP, fill=tk.X)
self.paned = PanedWindow(self.root)
self.paned.pack()
self.pane1 = Frame(self.paned)
self.paned.add(self.pane1, weight=1)
self.pane1.pack()
self.notebook = Notebook(self.pane1)
self.notebook.pack()
self.tab1 = Frame(self.notebook)
self.notebook.add(self.tab1, text="Swing View")
self.tab2 = Frame(self.notebook)
self.label2 = Label(self.tab2, text="Reference", justify="center")
self.label2.pack()
self.notebook.add(self.tab2, text="Reference Swing")
# Add buttons to toolbar
backBtn = tk.Button(toolbar, text='Previous', command=self.previous_file)
backBtn.pack(side=tk.LEFT)
nextBtn = tk.Button(toolbar, text="Next", command=self.next_file)
nextBtn.pack(side=tk.LEFT)
self.sep = Separator(orient="vertical")
self.sep.pack(side=tk.LEFT)
label = Label(toolbar, text="Drawing Tools: ")
label.pack(side="left")
self.var = tk.IntVar()
drawOn = Radiobutton(toolbar, text="enable", variable=self.var, value=1, command=self.toggle_radio)
drawOn.pack(side="left")
drawOff = Radiobutton(toolbar, text="disable", variable=self.var, value=0, command=self.toggle_radio)
drawOff.pack(side="left")
plotBtn = tk.Button(toolbar, text="Show Plots", command=self.show_plot)
plotBtn.pack(side=tk.LEFT)
exportBtn = tk.Button(toolbar, text="Export", command=self.export_results)
exportBtn.pack(side=tk.LEFT)
quitBtn = tk.Button(toolbar, text="Quit", command=self.exit_app)
quitBtn.pack(side=tk.RIGHT)
label = Label(toolbar, text="Frame: ")
label.pack(side="left")
self.scale = tk.Scale(toolbar, from_=0, to=240, orient="horizontal", length=240, command=self.slider_changed)
self.scale.pack(side=tk.LEFT)
self.root.bind("<Left>", lambda e: self.scale.set(self.scale.get()-1))
self.root.bind("<Right>", lambda e: self.scale.set(self.scale.get()+1))
self.canvas = Canvas(self.tab1, width=1610, height=640, highlightbackground="red", highlightthickness=2)
self.canvas.pack()
self.notebook.pack(expand=True, fill="both", padx=5, pady=5)
self.root.after(0, self.update)
def next_file(self):
print("Open next swing")
def previous_file(self):
print("Open previous swing")
def export_results(self):
print("Exporting results")
def show_plot(self):
print("Plotting results")
def exit_app(self):
self.root.destroy()
def toggle_radio(self):
if self.var.get() == 1:
print("drawings enabled")
elif self.var.get() == 0:
print("drawings disabled")
def slider_changed(self,value):
self.cap1.set(cv2.CAP_PROP_POS_FRAMES, int(value))
self.cap2.set(cv2.CAP_PROP_POS_FRAMES, int(value))
self.update()
def photo_image(self,img):
h, w = img.shape[:2]
data = f'P6 {w} {h} 255 '.encode() + img[..., ::-1].tobytes()
return PhotoImage(width=w, height=h, data=data, format='PPM')
def update(self):
ret1, img1 = self.cap1.read()
ret2, img2 = self.cap2.read()
if ret1 and ret2:
photo = self.photo_image(np.hstack((img1, img2)))
self.canvas.create_image(0, 0, image=photo, anchor=NW)
self.canvas.image = photo
self.root.after(15, self.update)
def __del__(self):
self.cap1.release()
self.cap2.release()
if __name__ == "__main__":
video1='/tmp/fto-20241229_184824_Sports2D.mp4'
video2='/tmp/dtl-20241229_184824_Sports2D.mp4'
app = Application(800,600,video1,video2)
app.mainloop()