4

I am creating GUI to play video files. The problem is that when I pause video, Play button cannot re-play that video, and I have to select video file again.

Note: Since I want to show video in the same tkinter window, I don't use OpenCV imshow command. Instead, I am using "window.after" method.

Following is my code:

I try to use "self.pause" variable to control pause status. When I click Pause button, this Boolean variable becomes True. However, I couldn't find suitable place to make it False when I click Play button again.

from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
import PIL.Image, PIL.ImageTk
import cv2


class videoGUI:

    def __init__(self, window, window_title):

        self.window = window
        self.window.title(window_title)

        top_frame = Frame(self.window)
        top_frame.pack(side=TOP, pady=5)

        bottom_frame = Frame(self.window)
        bottom_frame.pack(side=BOTTOM, pady=5)

        self.pause = False   # Parameter that controls pause button

        self.canvas = Canvas(top_frame)
        self.canvas.pack()

        # Select Button
        self.btn_select=Button(bottom_frame, text="Select video file", width=15, command=self.open_file)
        self.btn_select.grid(row=0, column=0)

        # Play Button
        self.btn_play=Button(bottom_frame, text="Play", width=15, command=self.play_video)
        self.btn_play.grid(row=0, column=1)

        # Pause Button
        self.btn_pause=Button(bottom_frame, text="Pause", width=15, command=self.pause_video)
        self.btn_pause.grid(row=0, column=2)

        self.delay = 15   # ms

        self.window.mainloop()


    def open_file(self):

        self.pause = False

        self.filename = filedialog.askopenfilename(title="Select file", filetypes=(("MP4 files", "*.mp4"),
                                                                                         ("WMV files", "*.wmv"), ("AVI files", "*.avi")))
        print(self.filename)

        # Open the video file
        self.cap = cv2.VideoCapture(self.filename)

        self.width = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)

        self.canvas.config(width = self.width, height = self.height)


    def get_frame(self):   # get only one frame

        try:

            if self.cap.isOpened():
                ret, frame = self.cap.read()
                return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

        except:
            messagebox.showerror(title='Video file not found', message='Please select a video file.')


    def play_video(self):

        # Get a frame from the video source, and go to the next frame automatically
        ret, frame = self.get_frame()

        if ret:
            self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
            self.canvas.create_image(0, 0, image = self.photo, anchor = NW)

        if not self.pause:
            self.window.after(self.delay, self.play_video)


    def pause_video(self):
        self.pause = True


    # Release the video source when the object is destroyed
    def __del__(self):
        if self.cap.isOpened():
            self.cap.release()

##### End Class #####


# Create a window and pass it to videoGUI Class
videoGUI(Tk(), "EnJapan")

If I write following code in "play_video" function:

self.pause = False

Pause button will not work. Because "window.after" method calls "play_video" function automatically and make the "self.pause" as False. Therefore, Pause button will have no effect.

Mohammad
  • 775
  • 1
  • 14
  • 37

5 Answers5

3

Question: Pause button will have no effect.

Reference:


To cancel the allready queued events for self.play_video change the following:

def play_video(self):
    ...

    if self.pause:
        self.window.after_cancel(self.after_id)
    else:
        self.after_id = self.window.after(self.delay, self.play_video)
Community
  • 1
  • 1
stovfl
  • 14,998
  • 7
  • 24
  • 51
  • @stovfi what does the 'self.after_id' stand for? – Ekin Feb 08 '20 at 01:06
  • 1
    @Ekin ***"what does the `self.after_id` stand for?"***: I have updated my answer with a reference link. `after_id` ist the identifier returned from `.after(...`. – stovfl Feb 08 '20 at 09:21
0

I would create another method for the play button callback. Something like this:

def play_start(self):
    self.pause = False
    self.play_video()

However, I would make sure you disable the play button if it's already playing. Otherwise, you could have multiple "instances" of play_video going if the play button is pressed multiple times.


An alternative is to combine your play and pause button, so it toggles the value of self.pause. Then you could have just one button with one callback function.

busybear
  • 10,194
  • 1
  • 25
  • 42
0

Simply add in the play_video(self) method:

if self.pause == True:
    self.pause = False
    return
 
Jop Knoppers
  • 676
  • 1
  • 10
  • 22
0
if not self.pause:
    self.window.after(self.delay, self.play_video)
 else:
    self.pause = False
Dharman
  • 30,962
  • 25
  • 85
  • 135
MHP
  • 352
  • 3
  • 8
-2
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
import PIL.Image, PIL.ImageTk
import cv2


class videoGUI:

    def __init__(self, window, window_title):

        self.window = window
        self.window.title(window_title)

        top_frame = Frame(self.window)
        top_frame.pack(side=TOP, pady=5)

        bottom_frame = Frame(self.window)
        bottom_frame.pack(side=BOTTOM, pady=5)

        self.pause = False   # Parameter that controls pause button

        self.canvas = Canvas(top_frame)
        self.canvas.pack()

        # Select Button
        self.btn_select=Button(bottom_frame, text="Select video file", width=15, command=self.open_file)
        self.btn_select.grid(row=0, column=0)

        # Play Button
        self.btn_play=Button(bottom_frame, text="Play", width=15, command=self.play_video)
        self.btn_play.grid(row=0, column=1)

        # Pause Button
        self.btn_pause=Button(bottom_frame, text="Pause", width=15, command=self.pause_video)
        self.btn_pause.grid(row=0, column=2)

        # Resume Button
        self.btn_resume=Button(bottom_frame, text="resume", width=15, command=self.resume_video)
        self.btn_resume.grid(row=0, column=3)

        self.delay = 15   # ms

        self.window.mainloop()


    def open_file(self):

        self.pause = False

        self.filename = filedialog.askopenfilename(title="Select file", filetypes=(("MP4 files", "*.mp4"),
                                                                                         ("WMV files", "*.wmv"), ("AVI files", "*.avi")))
        print(self.filename)

        # Open the video file
        self.cap = cv2.VideoCapture(self.filename)

        self.width = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)

        self.canvas.config(width = self.width, height = self.height)


    def get_frame(self):   # get only one frame

        try:

            if self.cap.isOpened():
                ret, frame = self.cap.read()
                return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

        except:
            messagebox.showerror(title='Video file not found', message='Please select a video file.')


    def play_video(self):

        # Get a frame from the video source, and go to the next frame automatically
        ret, frame = self.get_frame()

        if ret:
            self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
            self.canvas.create_image(0, 0, image = self.photo, anchor = NW)

        if not self.pause:
            self.window.after(self.delay, self.play_video)


    def pause_video(self):
        self.pause = True

#Addition
    def resume_video(self):
        self.pause=False
        self.play_video()


    # Release the video source when the object is destroyed
    def __del__(self):
        if self.cap.isOpened():
            self.cap.release()

##### End Class #####


# Create a window and pass it to videoGUI Class
videoGUI(Tk(), "EnJapan")
d_kennetz
  • 5,219
  • 5
  • 21
  • 44
Subhash
  • 1
  • 1