tkinter button function calls: multiple presses calling same function multiple times

362 Views Asked by At

I'm trying to make a Raspberry Pi Media Player, using omxplayer and tkinter.

I grab upto 16 video from a USB drive(or in local folder) and display them as thumbnails on a grid of buttons. When a button is pressed, it plays that video with omxplayer in fullscreen(at which point you cant access the tkinter window anymore until the video ends). Basically I want to enable selection of video only when there isn't a video playing already.

The problem I'm facing is with multiple presses on one button, or presses on other buttons before the video gets to play. This causes all the videos to queue up and immediately play one after another. I don't want subsequent presses to register until the first video (played from the first button press) is done playing. I try to set a boolean variable video_is_playing, and check its state in the function call, but it never enters the else case.

I tried to disable all buttons after you press any of them, then enabling them all after the video is done playing, I tried to use a variable to check if a video is done playing, and pretty much anything else I could think of doing to prevent multiple button presses.

Here is some parts of my code (sorry if it seems long, but I think everything included is relevant):

class TkinterGUI:
    def __init__(self):
        self.folder_name="videos"
        self.vid_path = f"{os.path.split(os.getcwd())[0]}/{os.path.split(os.getcwd())[1]}/{self.folder_name}/"
        self.videos = []
        self.video_is_playing = False
        self.vidbuttons = []

        for f in os.listdir(f"{self.vid_path}"):
            if '.mp4' in f:
                self.videos.append(f)
        self.videos.sort()
        self.videos_iterator = iter(self.videos)

    def pack_thumbnail(self, path, button):
        #putting video thumbnail in button with imageio
        pass

    def disable_buttons(self, window):
        for b in self.vidbuttons:
            b.config(state=tk.DISABLED)
        window.update()
        print(">>all buttons diabled")
    
    def enable_buttons(self, window):
        for b in self.vidbuttons:
            b.config(state=tk.NORMAL)
        window.update()
        print(">>all buttons enabled")

    def play_vid(self, i, j, window):
        try:
            self.disable_buttons(window)
            if self.video_is_playing == False:
                self.video_is_playing=True
                k = (i*4)+j
                video = self.videos[k]
                path = f"{self.vid_path}/{video}"
                print(f">>now playing: {video} of duration {self.vid_duration(path)}")
                omxp = Popen(['omxplayer', path])
                omxp.wait()
                print(f"video {video} is done playing!")
            else:
                print("a video seems to be playing already")
                return
        except Exception as e:
            print(e)
        finally:
            self.video_is_playing = False
            self.enable_buttons(window)

    def video_player_window(self):
        window = tk.Tk()
        window.attributes("-fullscreen", True)

        #left side frame(blank for now)
        frame1 = tk.Frame(master=window, width=200, height=100, bg="white")
        frame1.pack(fill=tk.Y, side=tk.LEFT)

        #main video player frame(contains 4x4 grid of video thumbnails)
        frame2 = tk.Frame()
        for i in range(4):
            frame2.columnconfigure(i, weight=1, minsize=75)
            frame2.rowconfigure(i, weight=1, minsize=50)
            
            for j in range(4):
                frame = tk.Frame(master=frame2, relief=tk.FLAT, borderwidth=1)
                frame.grid(row=i, column=j, padx=5, pady=5)

                vid=next(self.videos_iterator, "end")
                print(vid)

                if vid != "end":
                    button = tk.Button(master=frame, highlightcolor="black", text=f"Row {i}\nColumn {j}", command= partial(self.play_vid, i, j, window))
                    self.pack_thumbnail(self.vid_path+f"{vid}", button)
                    button.pack(padx=5, pady=5)
                    self.vidbuttons.append(button)
                else:
                    img = Image.open(f"vidnotfound.png")
                    img = img.resize((424, 224))
                    image = ImageTk.PhotoImage(img)
                    label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}", image=image)#, compound='center')
                    label.image = image
                    label.pack(padx=5, pady=5)
        frame2.pack()
        window.bind("<Escape>", lambda x: window.destroy())
        window.mainloop()


tkin = TkinterGUI()
tkin.video_player_window()

I used functools.partial() to pass i, j indices to the play_vid function, so I can use those indices to know which video from the list to play. Here is everything I imported:

import tkinter as tk
import imageio
from PIL import ImageTk, Image
from pathlib import Path
from functools import partial

import subprocess
from subprocess import Popen

on a side note: is there a better way to accomplish what I want to do with the button grid? I'd like each button to call the same function but play different videos, is there any attribute I could use or anything?

0

There are 0 best solutions below