Search code examples
pythonuser-interfacetkintergifmp4

Tkinter play video and next window commands from one button


I'm having trouble playing a video and moving to the next frame by clicking on one button. The problem is that the video starts playing at the beginning of the GUI, meaning that it has ended once you get to the right frame. I've tried to solve this by defining a function that only actually plays the video once the button is clicked. I can get it to move to the next frame and even print that the function is working but it won't play the video. To complicate things a bit, I've been working from different files and in classes. I've tried to slim down as much as possible with the following MWE:

page 1 as an overview page

page 2 as a first frame with the button

page 3 as a second frame with the MP4

page1 :

import tkinter as tk
from tkinter import *

from page2 import *

######Ensuring that the frames don't load as seperate windows but as frames over one another#######

class GUI(Tk):
    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)

        # create a container for all the widgets (buttons, etc)
        window = Frame(self)
        window.pack(side="top", fill="both", expand=True)
        window.grid_rowconfigure(0, weight=1)
        window.grid_columnconfigure(0, weight=1)

        self.frames = {}

        # for loops for switching between pages
        for F in (Page2, Page3):
            frame = F(window, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(Page2)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

if __name__ == "__main__":

    window = GUI()
    window.geometry("400x400")
    window.mainloop()

page2 :

import tkinter as tk
from tkinter import *

from page3 import *

class Page2(Frame):
          
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        
        DF = Listbox(self, bg='#f8f8f8',height=30, width=70, highlightthickness=0, borderwidth=0)
        DF.place(x=0, y=0) 

        nextpage = Page3(parent, controller)
        play = nextpage.play_video
         
        button = Button(
            DF, 
            text="press",
            bg='#ffffff', 
            height=4, 
            width=30,
            command=lambda: [controller.show_frame(Page3),play.play_video()],
              )
        button.place(relx=0.5, rely=0.5, anchor=CENTER)

page3 :

import tkinter as tk
from tkinter import *
from tkVideoPlayer import TkinterVideo

class Page3(Frame):
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        
        DF2 = Listbox(self, bg='green',height=30, width=70, highlightthickness=0, borderwidth=0)
        DF2.place(x=0, y=0) 

        self.vid_player = TkinterVideo(DF2, scaled = True)
        self.vid_player.place(x=0, y=0)
        self.vid_player.load("Location_of_MP4.mp4")
        
        ###add this to play the MP4###
        #self.vid_player.play()


        
    def play_video(self):
        print("working")
        self.vid_player.play()

If you add the self.vidplayer.play() part you can tell that the MP4 isn't having problems. Just opening it from the function play_video seems to be not working.

I'm a bit stuck as I've tried a number of work-arounds to fix the issue. I'm flexible in the solution, e.g. it doesn't have to be a function to play the video. If possible it could also be played directly from the command=lambda or any other way. I've also tried to look for code that would reset the video to the start but haven't found that either.


Solution

  • After some time I've found a solution. In file two change the button command to a single function command

    command=lambda:self.Function(controller),

    Within that Function create a thread to simultaneously redirect to the 3rd page while also running a simulation. After the simulation redirect to the other frame

    def FuncD(self, cont):
        cont.show_frame(page3)
        
        def thread_function(name):
    
            self.runcase()
            time.sleep(5)
            cont.show_frame(UC_Power2Plug_Results)
    
        thread = threading.Thread(target=thread_function, args=(1,))
        thread.start()
    

    This should solve the one click two command issue. Add the following line to keep the video going

    self.vid_player.bind("<<Ended>>",self.video_ended)
    

    With this function behind the line of command

    def video_ended(self, event=None):
        """ handle video ended """
    
        videoplayer = self.vid_player
        videoplayer.play()