I want to be able to switch a VLC Instance between frames. The purpose is to toggle fullscreen mode. In fullscreen mode, the Instance should be embedded into a toplevel, otherwise it should be embedded into root.
I have code that does this, but it stops te video when switching between full screen and non-full screen. This would interrupt the playing. So I'm wondering if there's ways to do it without interrupting this playing, i.e. without the player.stop() command.
Here's my code:
from tkinter import (
Tk, Menu, Frame, Label, Button, Scale, Toplevel,
BOTH, Entry, DISABLED, END, HORIZONTAL, VERTICAL
)
from vlc import Instance
class Test:
def __init__(self):
self.Instance = Instance()
self.player = self.Instance.media_player_new()
self.root = Tk()
self.root.geometry("800x800")
self.frame = Frame(self.root, width=700, height=600)
self.frame.pack()
Button(
self.root, text="OK",
command=self.play).pack(side='left')
Button(
self.root, text="FS",
command=self.FS).pack(side='left')
self.display = Frame(self.frame, bd=5)
self.display.place(relwidth=1, relheight=1)
self.root.mainloop()
def play(self):
Media = self.Instance.media_new('test.webm')
self.player.stop()
self.player.set_xwindow(self.display.winfo_id())
self.player.set_media(Media)
self.player.play()
def FS(self):
self.top = Toplevel(self.root)
self.top.geometry("200x200")
self.player.stop()
Media = self.Instance.media_new('test.webm')
self.player.set_xwindow(self.top.winfo_id())
self.player.play()
self.player.set_media(Media)
self.player.play()
self.top.attributes("-fullscreen", True)
def main():
Test()
if __name__ == '__main__':
main()
Related
I created a GUI using Python, Tkinter. Everything seems to be fine. I have added several options to my Menu tab. In the File section I have added a feature to exit the GUI by clickinh onto the Exit button(not the button though, but a menu option). Here is the code that I have written.
from tkinter import *
from tkinter import filedialog, RIGHT
class Filedialog:
def directory(self):
content = filedialog.askdirectory()
return content
class Main(Frame):
def __init__(self):
self.opening_times = 0
self.current_image = 0
self.images = []
self.root = Tk()
self.root.title("Y")
self.root.configure(bg='pale turquoise')
self.root.geometry("800x800")
self.frame = Frame(self.root)
self.frame.pack(side=RIGHT)
self.frame = Frame.__init__(self, self.root)
self.widgets()
self.root.mainloop()
def widgets(self):
self.menu = Menu(self.root)
self.root.config(menu=self.menu)
self.filemenu = Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="File", menu=self.filemenu)
self.filemenu.add_separator()
self.filemenu.add_command(label="Save")
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", command=self.onExit)
#self.editmenu = Menu(self.menu, tearoff=0)
#self.menu.add_cascade(label='Edit', menu=self.editmenu)
#self.editmenu.add_command(label="Undo")
#self.editmenu.add_command(label="Redo")
def onExit(self):
self.root.quit()
if __name__ == "__main__":
Main()
I have almost succeeded. But the problem arises when I click on the exit. The function does what it has to. But the GUI window was not closing, also it freezes there. There was no response at all until I force the window to close externally.
I also tried doing:
self.quit()
/
self.exit()
But the response is the same as the GUI freezes and not letting me do any other activities. I had to close complete program to get the access again.
I am not getting the error at least, to know what is wrong.
Please help me find out what is wrong/solve this problem.
Thank you for your time.
Let me correct your code
from sys import exit
from tkinter import *
from tkinter import filedialog, RIGHT
class Filedialog:
def directory(self):
content = filedialog.askdirectory()
return content
class Main(Frame):
def __init__(self):
self.opening_times = 0
self.current_image = 0
self.images = []
self.root = Tk()
self.root.title("Y")
self.root.configure(bg='pale turquoise')
self.root.geometry("800x800")
self.frame = Frame(self.root)
self.frame.pack(side=RIGHT)
self.frame = Frame.__init__(self, self.root)
self.widgets()
self.root.mainloop()
def widgets(self):
self.menu = Menu(self.root)
self.root.config(menu=self.menu)
self.filemenu = Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="File", menu=self.filemenu)
self.filemenu.add_separator()
self.filemenu.add_command(label="Save")
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", command=self.onExit)
#self.editmenu = Menu(self.menu, tearoff=0)
#self.menu.add_cascade(label='Edit', menu=self.editmenu)
#self.editmenu.add_command(label="Undo")
#self.editmenu.add_command(label="Redo")
def onExit(self):
self.root.destroy()
exit()
if __name__ == "__main__":
Main()
You need to use destroy() instead of quit() and you should use exit to quit from the console
The method you have to use is destroy. For example:
def onExit(self):
self.root.destroy()
Use destroy() method instead of quit.
You can use the method tk.destroy() to close a window. The built-in functions exit() and quit() close the Python console.
Working example
from tkinter import *
tk = Tk()
tk.title("Closing window demonstration")
def close_window():
tk.destroy()
quit = Button(tk, text = "Quit", command = close_window)
quit.pack()
tk.mainloop()
I cannot figure out how to use the tag_bind method associated with a canvas. I created an oval and expected to be able to generate an event by clicking on it. When I do this, nothing happens. What is the correct use of tag_bind?
import tkinter as tk
class Window():
def __init__(self, master):
self.master = master
self.create_widgets()
def create_widgets(self):
self.c = tk.Canvas(self.master, bg='white', height=200, width=300)
self.c.pack()
b = tk.Button(self.master, text='Draw', command=self.draw)
b.pack()
def draw(self):
self.c.delete('all')
self.oval = self.c.create_oval([30,50], [130,80])
self.rect = self.c.create_rectangle([180,10], [280,80])
self.c.tag_bind(self.oval, '<Button-1>', self.oval_func)
def oval_func(self, event):
self.c.delete(self.rect)
self.c.create_text(150, 150, text='Hello, world!', anchor='w')
if __name__ == '__main__':
root = tk.Tk()
app = Window(root)
root.mainloop()
The code is working. However, when you bind to a canvas object, you have to click on part of the drawn object. Since you didn't fill the oval, that means you must click on its outline.
If you fill it with the same color as the background (or any other color) you can click anywhere in the oval.
self.oval = self.c.create_oval([30,50], [130,80], fill="white")
I have implemented a splash screen that is shown while my application loads the database from remote cloud storage on startup. The splash screen is kept alive (there's a progressbar on it) with calls to .update() and is destroyed once the separate loading process ends. After this, the mainloop is started and the app runs normally.
The code below used to work fine on my Mac with python 3.6 and tcl/tk 8.5.9. However, after the update to Sierra I was forced to update tk to ActiveTcl 8.5.18. Now, the splash screen is not displayed until the separate process finishes, but then appears and stays on screen together with the root window (even though its .destroy() method is called).
import tkinter as tk
import tkinter.ttk as ttk
import multiprocessing
import time
class SplashScreen(tk.Toplevel):
def __init__(self, root):
tk.Toplevel.__init__(self, root)
self.geometry('375x375')
self.overrideredirect(True)
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.label = ttk.Label(self, text='My Splashscreen', anchor='center')
self.label.grid(column=0, row=0, sticky='nswe')
self.center_splash_screen()
print('initialized splash')
def center_splash_screen(self):
w = self.winfo_screenwidth()
h = self.winfo_screenheight()
x = w / 2 - 375 / 2
y = h / 2 - 375 / 2
self.geometry("%dx%d+%d+%d" % ((375, 375) + (x, y)))
def destroy_splash_screen(self):
self.destroy()
print('destroyed splash')
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.start_up_app()
self.title("MyApp")
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.application_frame = ttk.Label(self, text='Rest of my app here', anchor='center')
self.application_frame.grid(column=0, row=0, sticky='nswe')
self.mainloop()
def start_up_app(self):
self.show_splash_screen()
# load db in separate process
process_startup = multiprocessing.Process(target=App.startup_process)
process_startup.start()
while process_startup.is_alive():
# print('updating')
self.splash.update()
self.remove_splash_screen()
def show_splash_screen(self):
self.withdraw()
self.splash = SplashScreen(self)
#staticmethod
def startup_process():
# simulate delay while implementation is loading db
time.sleep(5)
def remove_splash_screen(self):
self.splash.destroy_splash_screen()
del self.splash
self.deiconify()
if __name__ == '__main__':
App()
I do not understand why this is happening and how to solve it. Can anybody help? Thanks!
Update:
The splash screen is displayed correctly if you outcomment the line self.overrideredirect(True). However, I don't want window decorations and it still stays on screen at the end of the script. It is being destroyed internally though, any further method calls on self.splash (e.g. .winfo_...-methods) result in _tkinter.TclError: bad window path name ".!splashscreen".
Also, this code works fine under windows and tcl/tk 8.6. Is this a bug/problem with window management of tcl/tk 8.5.18 on Mac?
I came across this while looking for an example on how to make a tkinter splash screen that wasn't time dependent (as most other examples are). Sam's version worked for me as is. I decided to make it an extensible stand-alone class that handles all the logic so it can just be dropped into an existing program:
# Original Stackoverflow thread:
# https://stackoverflow.com/questions/44802456/tkinter-splash-screen-multiprocessing-outside-of-mainloop
import multiprocessing
import tkinter as tk
import functools
class SplashScreen(tk.Toplevel):
def __init__(self, root, **kwargs):
tk.Toplevel.__init__(self, root, **kwargs)
self.root = root
self.elements = {}
root.withdraw()
self.overrideredirect(True)
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
# Placeholder Vars that can be updated externally to change the status message
self.init_str = tk.StringVar()
self.init_str.set('Loading...')
self.init_int = tk.IntVar()
self.init_float = tk.DoubleVar()
self.init_bool = tk.BooleanVar()
def _position(self, x=.5,y=.5):
screen_w = self.winfo_screenwidth()
screen_h = self.winfo_screenheight()
splash_w = self.winfo_reqwidth()
splash_h = self.winfo_reqheight()
x_loc = (screen_w*x) - (splash_w/2)
y_loc = (screen_h*y) - (splash_h/2)
self.geometry("%dx%d+%d+%d" % ((splash_w, splash_h) + (x_loc, y_loc)))
def update(self, thread_queue=None):
super().update()
if thread_queue and not thread_queue.empty():
new_item = thread_queue.get_nowait()
if new_item and new_item != self.init_str.get():
self.init_str.set(new_item)
def _set_frame(self, frame_funct, slocx=.5, sloxy=.5, ):
"""
Args:
frame_funct: The function that generates the frame
slocx: loction on the screen of the Splash popup
sloxy:
init_status_var: The variable that is connected to the initialization function that can be updated with statuses etc
Returns:
"""
self._position(x=slocx,y=sloxy)
self.frame = frame_funct(self)
self.frame.grid(column=0, row=0, sticky='nswe')
def _start(self):
for e in self.elements:
if hasattr(self.elements[e],'start'):
self.elements[e].start()
#staticmethod
def show(root, frame_funct, function, callback=None, position=None, **kwargs):
"""
Args:
root: The main class that created this SplashScreen
frame_funct: The function used to define the elements in the SplashScreen
function: The function when returns, causes the SplashScreen to self-destruct
callback: (optional) A function that can be called after the SplashScreen self-destructs
position: (optional) The position on the screen as defined by percent of screen coordinates
(.5,.5) = Center of the screen (50%,50%) This is the default if not provided
**kwargs: (optional) options as defined here: https://www.tutorialspoint.com/python/tk_toplevel.htm
Returns:
If there is a callback function, it returns the result of that. Otherwise None
"""
manager = multiprocessing.Manager()
thread_queue = manager.Queue()
process_startup = multiprocessing.Process(target=functools.partial(function,thread_queue=thread_queue))
process_startup.start()
splash = SplashScreen(root=root, **kwargs)
splash._set_frame(frame_funct=frame_funct)
splash._start()
while process_startup.is_alive():
splash.update(thread_queue)
process_startup.terminate()
SplashScreen.remove_splash_screen(splash, root)
if callback: return callback()
return None
#staticmethod
def remove_splash_screen(splash, root):
splash.destroy()
del splash
root.deiconify()
class Screen(tk.Frame):
# Options screen constructor class
def __init__(self, parent):
tk.Frame.__init__(self, master=parent)
self.grid(column=0, row=0, sticky='nsew')
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
### Demo ###
import time
def splash_window_constructor(parent):
"""
Function that takes a parent and returns a frame
"""
screen = SplashScreen.Screen(parent)
label = tk.Label(screen, text='My Splashscreen', anchor='center')
label.grid(column=0, row=0, sticky='nswe')
# Connects to the tk.StringVar so we can updated while the startup process is running
label = tk.Label(screen, textvariable=parent.init_str, anchor='center')
label.grid(column=0, row=1, sticky='nswe')
return screen
def startup_process(thread_queue):
# Just a fun method to simulate loading processes
startup_messages = ["Reticulating Splines","Calculating Llama Trajectory","Setting Universal Physical Constants","Updating [Redacted]","Perturbing Matrices","Gathering Particle Sources"]
r = 10
for n in range(r):
time.sleep(.2)
thread_queue.put_nowait(f"Loading database.{'.'*n}".ljust(27))
time.sleep(1)
for n in startup_messages:
thread_queue.put_nowait(n)
time.sleep(.2)
for n in range(r):
time.sleep(.2)
thread_queue.put_nowait(f"Almost Done.{'.'*n}".ljust(27))
for n in range(r):
time.sleep(.5)
thread_queue.put_nowait("Almost Done..........".ljust(27))
time.sleep(.5)
thread_queue.put_nowait("Almost Done......... ".ljust(27))
def callback(text):
# To be run after the splash screen completes
print(text)
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.callback_return = SplashScreen.show(root=self,
frame_funct=splash_window_constructor,
function=startup_process,
callback=functools.partial(callback,"Callback Done"))
self.title("MyApp")
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.application_frame = tk.Label(self, text='Rest of my app here', anchor='center')
self.application_frame.grid(column=0, row=0, sticky='nswe')
self.mainloop()
if __name__ == "__main__":
App()
Apparently this is due to a problem with the window stacking order when windows are not decorated by the window manager after calling overrideredirect(True). It seems to have occurred on other platforms as well.
Running the following code on macOS 10.12.5 with Python 3.6.1 and tcl/tk 8.5.18, toplevel windows do not appear after the button 'open' is clicked:
import tkinter as tk
class TL(tk.Toplevel):
def __init__(self):
tk.Toplevel.__init__(self)
self.overrideredirect(True)
# self.after_idle(self.lift)
tl_label = tk.Label(self, text='this is a undecorated\ntoplevel window')
tl_label.grid(row=0)
b_close = tk.Button(self, text='close', command=self.close)
b_close.grid(row=1)
def close(self):
self.destroy()
def open():
TL()
root = tk.Tk()
label = tk.Label(root, text='This is the root')
label.grid(row=0)
b_open = tk.Button(root, text='open', command=open)
b_open.grid(row=1)
root.mainloop()
Uncommenting the line self.after_idle(self.lift) fixes the problem (simply calling self.lift() does too. But using after_idle()prevents the window from flashing up for a fraction of a second before it is moved to its position and resized, which is another problem I have experienced repeatedly with tkinter and keeps me wondering whether I should move on to learn PyQT or PySide2...).
As to the problem with closing an undecorated window in my original question: calling after_idle(window.destroy()) instead of window.destroy() seems to fix that too. I do not understand why.
In case other people reproduce this and somebody hints me towards where to report this as a bug, I am happy to do so.
Sorry for any stupidity or ignorance on my part but I am new to Python, and coding in general. I have been trying to get a UI working for a game I'm going to make as a way of teaching myself python I have a main window and a few button widgets in this.
The Play game button opens a second window (for the game itself) and hides the root window with the .withdraw method. This works perfectly. Inside the game window I have another button which I would like to destroy the game window and bring the user back to the root menu window that I have withdrawn. This seems to work except every time I call it,it creates an new, duplicate set of widgets in the window so I end up with multiple sets.
I'll post my full code at the bottom but here are what I believe are the relevant parts.
A tkinter Button calls this inside the parent class (the main window). This works fine.
def playGame(self): #Start the main game window
self.master.withdraw()
gameWindow()
I'm using the bellow method inside of the child class to destroy the game window and then call a method in the parent class to bring back the earlier withdrawn window
def exitMenu(self):
self.g.destroy()
UI(root).showMenu()
this works except it duplicates the widgets each time resulting in this being shown:
screen capture of result
Bellow is all my code, thank you so much for any help.
import tkinter as tk
import PIL
from Config import *
root = tk.Tk()
class UI(): #Main Menu
def __init__(self, master):
#Create Main Menu Window
self.master = master
self.master.title("Monopoly")
self.master.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.master.geometry((resolution))
#Menu Buttons
self.label = tk.Label(master, text= 'Welcome to Monopoly! PLACEHOLDER')
self.playButton = tk.Button(master, text= 'Play Game', command= self.playGame)
self.settingsButton = tk.Button(master, text= 'settings', command= self.settings)
self.exitButton = tk.Button(master, text= 'Exit', command= self.exitGame)
self.label.grid(columnspan=2)
self.playButton.grid(column=0)
self.settingsButton.grid(column=0)
self.exitButton.grid(column=0)
def settings(self): #Opens Settings Window
s = tk.Toplevel()
s.title('Settings')
s.wm_iconbitmap('icons\Monopoly-Icon.ico')
s.geometry((resolution))
self.master.withdraw()
resLabel = tk.Label(s, text= 'Resolution')
resOption = tk.OptionMenu(s, resolution, *resList)
resLabel.grid(column=0,row=0)
resOption.grid(column=0, row=4)
def showMenu(self): #Bring back menu windwow
self.master.deiconify()
def exitGame(self): #Exit Game Method
root.destroy()
def playGame(self): #Start the main game window
self.master.withdraw()
gameWindow()
class gameWindow(UI):
def __init__(self):
self.g = tk.Toplevel()
self.g.title('Monopoly')
self.g.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.g.geometry((resolution))
self.menuButton = tk.Button(self.g, text= 'Main Menu', command= self.exitMenu)
self.menuButton.grid(column=0,row=0)
def exitMenu(self):
self.g.destroy()
UI(root).showMenu()
mainMenu = UI(root)
root.mainloop()
You should pass the parent class, UI, to the child class gameWindow, then you can use the showMenu() method of the parent class in the child class.
Code for showing the main window from the child window will be:
self.mainUI.showMenu() instead of UI(root).showMenu()
Child class becomes:
class gameWindow(UI):
def __init__(self, mainUI): # Make the main window a parameter/attribute
self.mainUI = mainUI # Make the main window a parameter/attribute
self.g = tk.Toplevel()
self.g.title('Monopoly')
self.g.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.g.geometry((resolution))
self.menuButton = tk.Button(self.g, text= 'Main Menu', command= self.exitMenu)
self.menuButton.grid(column=0,row=0)
def exitMenu(self):
self.g.destroy()
self.mainUI.showMenu()
gameWindow now requires one argument, so you pass it the main UI as self
def playGame(self): #Start the main game window
self.master.withdraw()
gameWindow(self)
You can read more about hiding/showing window here
Here is the full code:
import tkinter as tk
import PIL
from Config import *
root = tk.Tk()
class UI(): #Main Menu
def __init__(self, master):
#Create Main Menu Window
self.master = master
self.master.title("Monopoly")
self.master.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.master.geometry((resolution))
#Menu Buttons
self.label = tk.Label(master, text= 'Welcome to Monopoly! PLACEHOLDER')
self.playButton = tk.Button(master, text= 'Play Game', command= self.playGame)
self.settingsButton = tk.Button(master, text= 'settings', command= self.settings)
self.exitButton = tk.Button(master, text= 'Exit', command= self.exitGame)
self.label.grid(columnspan=2)
self.playButton.grid(column=0)
self.settingsButton.grid(column=0)
self.exitButton.grid(column=0)
def settings(self): #Opens Settings Window
s = tk.Toplevel()
s.title('Settings')
s.wm_iconbitmap('icons\Monopoly-Icon.ico')
s.geometry((resolution))
self.master.withdraw()
resLabel = tk.Label(s, text= 'Resolution')
resOption = tk.OptionMenu(s, resolution, *resList)
resLabel.grid(column=0,row=0)
resOption.grid(column=0, row=4)
def showMenu(self): #Bring back menu windwow
self.master.deiconify()
def exitGame(self): #Exit Game Method
root.destroy()
def playGame(self): #Start the main game window
self.master.withdraw()
gameWindow(self)
class gameWindow(UI):
def __init__(self, mainUI): # Make the main window a parameter/attribute
self.mainUI = mainUI # Make the main window a parameter/attribute
self.g = tk.Toplevel()
self.g.title('Monopoly')
self.g.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.g.geometry((resolution))
self.menuButton = tk.Button(self.g, text= 'Main Menu', command= self.exitMenu)
self.menuButton.grid(column=0,row=0)
def exitMenu(self):
self.g.destroy()
self.mainUI.showMenu()
mainMenu = UI(root)
root.mainloop()
I am creating 2 window in my program and i am using two class, since the code is complex, i separate it in 2 different python file. After i imported the second window file, how can i make sure it open without having this error which show in this picture
The original result should look like this after the new window button clicked:
Coding for Main Window:
from tkinter import *
import classGUIProgram
class Window(Tk):
def __init__(self, parent):
Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.geometry("600x400+30+30")
self.wButton = Button(self, text='newWindow', command = self.OnButtonClick)
self.wButton.pack()
def OnButtonClick(classGUIProgram):
classGUIProgram.top = Toplevel()
master = Tk()
b = classGUIProgram.HappyButton(master)
master.mainloop()
if __name__ == "__main__":
window = Window(None)
window.title("title")
window.mainloop()
Coding for Second Window:
from tkinter import *
class HappyButton:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.printButton = Button(frame, text="Print message", command=self.printMessage)
self.printButton.pack(side=LEFT)
self.quitButton = Button(frame, text="Quit", command= quit)
self.quitButton.pack(side=LEFT)
self.downloadHistoryCB=Checkbutton(frame, text="Download History")
self.downloadHistoryCB.pack(side=LEFT)
def printMessage(self):
print("Wow this actually worked!")
master = Tk()
b = HappyButton(master)
master.mainloop()
You're creating extra Tk windows. Here is an example of using Toplevel widgets and another file.
mainWindow.py
import tkinter as tk
import secondWindow as sW
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title("Main Window")
self.geometry("600x400+30+30")
tk.Button(self, text = "New Window", command = self.new_window).pack()
tk.Button(self, text = "Close Window", command = self.close).pack()
self._second_window = None
def new_window(self):
# This prevents multiple clicks opening multiple windows
if self._second_window is not None:
return
self._second_window = sW.SubWindow(self)
def close(self):
# Destory the 2nd window and reset the value to None
if self._second_window is not None:
self._second_window.destroy()
self._second_window = None
if __name__ == '__main__':
window = MainWindow()
window.mainloop()
secondWindow.py
import tkinter as tk
class SubWindow(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.title("Sub Window")
self.geometry("400x300+30+30")
# Change what happens when you click the X button
# This is done so changes also reflect in the main window class
self.protocol('WM_DELETE_WINDOW', master.close)
tk.Button(self, text = "Print", command = self.printMessage).pack()
def printMessage(self):
print("Wow this actually worked!")
When using another file be sure to not have any global code you don't want running. Your classes don't have to inherit from Tk and Toplevel, this is just an example. But you need to ensure you only ever have one instance of Tk otherwise you get the behaviour you encountered