Skip to content

Instantly share code, notes, and snippets.

@LennyPhoenix
Last active September 1, 2022 01:57
Show Gist options
  • Save LennyPhoenix/57344a0e049c2371622e31095bde87ed to your computer and use it in GitHub Desktop.
Save LennyPhoenix/57344a0e049c2371622e31095bde87ed to your computer and use it in GitHub Desktop.
Using Tkinter and Pyglet in the same program
"""
This version uses `Window.set_visible` instead of deleting the window,
so the default event loop can be used and the `on_close` event does not
need to be overridden.
"""
import threading
import pyglet
import tkinter
class Application:
def __init__(self):
# Create a window and register event handlers
self.pyglet_win = Window()
self.pyglet_win.push_handlers(self)
def create_tkinter_menu(self):
# Create the tkinter menu and register event handlers
self.menu = TkinterMenu()
self.menu.push_handlers(self)
# Start the tkinter mainloop
self.menu.run()
# Event called when the pyglet window is ready to pass over to the tkinter window
def on_pyglet_done(self):
self.create_tkinter_menu()
# Event called when the tkinter window is ready to pass back over to the pyglet window
def on_tkinter_done(self):
self.pyglet_win.set_visible(True)
# Start the event loop
def run(self):
pyglet.app.run()
# Generic window subclass with some events to pass to the Application
class Window(pyglet.window.Window, pyglet.event.EventDispatcher):
def __init__(self):
super().__init__()
self.label = pyglet.text.Label("Press ENTER")
def on_draw(self):
self.clear()
self.label.draw()
def on_key_release(self, symbol, modifiers):
if symbol == pyglet.window.key.ENTER:
self.set_visible(False) # Hide the window
self.dispatch_event("on_pyglet_done")
Window.register_event_type("on_pyglet_done")
# Class to handle the Tkinter side of things
class TkinterMenu(pyglet.event.EventDispatcher):
def __init__(self):
self.tk = tkinter.Tk()
# Register close protocol handler
self.tk.protocol("WM_DELETE_WINDOW", self.close_callback)
# Make a button
self.button = tkinter.Button(text="Press me!", command=self.button_callback)
self.button.pack()
# Helper function to get the pyglet thread to call a function.
# Always do
# self.schedule(self.dispatch_event, "on_closed")
# Instead of
# self.dispatch_event("on_closed")
def schedule(self, func, *args):
pyglet.clock.schedule_once(lambda dt: func(*args), 0)
# Called when the user tries to close the window manually
def close_callback(self):
# Exit the event loop
self.schedule(pyglet.app.exit)
self.tk.destroy()
# Hand over to the pyglet window
def button_callback(self):
self.schedule(self.dispatch_event, "on_tkinter_done")
self.tk.destroy()
# Start the mainloop in *another thread*
def run(self):
self.thread = threading.Thread(target=self.tk.mainloop)
self.thread.run()
TkinterMenu.register_event_type("on_tkinter_done")
# Start the application
if __name__ == "__main__":
app = Application()
app.run()
import threading
import pyglet
import tkinter
class Application:
def __init__(self):
# Instance the event loop
self.event_loop = pyglet.app.EventLoop()
# Create our starter window, we could technically start with tkinter here
self.create_pyglet_window()
def create_pyglet_window(self):
# Create a window and register event handlers
self.pyglet_win = Window()
self.pyglet_win.push_handlers(self)
def create_tkinter_menu(self):
# Create the tkinter menu and register event handlers
self.menu = TkinterMenu()
self.menu.push_handlers(self)
# Start the tkinter mainloop
self.menu.run()
# Event called when the pyglet window is ready to pass over to the tkinter window
def on_pyglet_done(self):
self.create_tkinter_menu()
# Event called when the tkinter window is ready to pass back over to the pyglet window
def on_tkinter_done(self):
self.create_pyglet_window()
# Event called when either window is closed manually by the user
def on_closed(self):
self.stop()
# Start the event loop
def run(self):
self.event_loop.run()
def stop(self):
# Stop the event loop
self.event_loop.exit()
# Generic window subclass with some events to pass to the Application
class Window(pyglet.window.Window, pyglet.event.EventDispatcher):
def __init__(self):
super().__init__()
self.label = pyglet.text.Label("Press ENTER")
def on_draw(self):
self.clear()
self.label.draw()
# Called when the user tries to close the window manually
def on_close(self):
self.close()
self.dispatch_event("on_closed")
def on_key_press(self, symbol, modifiers):
if symbol == pyglet.window.key.ENTER:
self.close()
self.dispatch_event("on_pyglet_done")
Window.register_event_type("on_pyglet_done")
Window.register_event_type("on_closed")
# Class to handle the Tkinter side of things
class TkinterMenu(pyglet.event.EventDispatcher):
def __init__(self):
self.tk = tkinter.Tk()
# Register close protocol handler
self.tk.protocol("WM_DELETE_WINDOW", self.close_callback)
# Make a button
self.button = tkinter.Button(text="Press me!", command=self.button_callback)
self.button.pack()
# Helper function to get the pyglet thread to call a function.
# Always do
# self.schedule(self.dispatch_event, "on_closed")
# Instead of
# self.dispatch_event("on_closed")
def schedule(self, func, *args):
pyglet.clock.schedule_once(lambda dt: func(*args), 0)
# Called when the user tries to close the window manually
def close_callback(self):
self.schedule(self.dispatch_event, "on_closed")
self.tk.destroy()
def button_callback(self):
self.schedule(self.dispatch_event, "on_tkinter_done")
self.tk.destroy()
# Start the mainloop in *another thread*
def run(self):
self.thread = threading.Thread(target=self.tk.mainloop)
self.thread.run()
TkinterMenu.register_event_type("on_tkinter_done")
TkinterMenu.register_event_type("on_closed")
# Start the application
if __name__ == "__main__":
app = Application()
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment