Embedding a Pygame window into a Tkinter or WxPython frame

31,192

Solution 1

(Note this solution does not work on Windows systems with Pygame 2. See Using 'SDL_WINDOWID' does not embed pygame display correctly into another application #1574. You can currently download older versions of Pygame here.)

According to this SO question and the accepted answer, the simplest way to do this would be to use an SDL drawing frame.

This code is the work of SO user Alex Sallons.

import os
import pygame
import Tkinter as tk
from Tkinter import *

root = tk.Tk()
embed = tk.Frame(root, width = 500, height = 500) #creates embed frame for pygame window
embed.grid(columnspan = (600), rowspan = 500) # Adds grid
embed.pack(side = LEFT) #packs window to the left
buttonwin = tk.Frame(root, width = 75, height = 500)
buttonwin.pack(side = LEFT)
os.environ['SDL_WINDOWID'] = str(embed.winfo_id())
os.environ['SDL_VIDEODRIVER'] = 'windib'
screen = pygame.display.set_mode((500,500))
screen.fill(pygame.Color(255,255,255))
pygame.display.init()
pygame.display.update()

def draw():
    pygame.draw.circle(screen, (0,0,0), (250,250), 125)
    pygame.display.update()

button1 = Button(buttonwin,text = 'Draw',  command=draw)
button1.pack(side=LEFT)
root.update()

while True:
    pygame.display.update()
    root.update()

This code is cross-platform, as long as the windib SDL_VIDEODRIVER line is omitted on non Windows systems. I would suggest

# [...]
import platform
if platform.system == "Windows":
    os.environ['SDL_VIDEODRIVER'] = 'windib'
# [...]

Solution 2

Here are some links.

Basically, there are many approaches.

  • On Linux, you can easily embed any application in a frame inside another. Simple.
  • Direct Pygame output to a WkPython Canvas

Some research will provide the relevant code.

Solution 3

According to the tracebacks, the program crashes due to TclErrors. These are caused by attempting to access the same file, socket, or similar resource in two different threads at the same time. In this case, I believe it is a conflict of screen resources within threads. However, this is not, in fact, due to an internal issue that arises with two gui programs that are meant to function autonomously. The errors are a product of a separate thread calling root.update() when it doesn't need to because the main thread has taken over. This is stopped simply by making the thread call root.update() only when the main thread is not doing so.

Share:
31,192

Related videos on Youtube

AHuman
Author by

AHuman

I vaguely remember python and now use R.

Updated on January 14, 2022

Comments

  • AHuman
    AHuman over 2 years

    A friend and I are making a game in pygame. We would like to have a pygame window embedded into a tkinter or WxPython frame, so that we can include text input, buttons, and dropdown menus that are supported by WX or Tkinter. I have scoured the internet for an answer, but all I have found are people asking the same question, none of these have been well answered.

    What would be the best way implement a pygame display embedded into a tkinter or WX frame? (TKinter is preferable)

    Any other way in which these features can be included alongside a pygame display would also work.

  • AHuman
    AHuman about 10 years
    I have looked at all of these if you are wondering. However, none answer the simple question of how to make a very basic window with a frame with pygame in it.
  • PythonNut
    PythonNut about 10 years
    I'm going to assume you use Windows, because you don't say. Does the code example (for windows) on the WxPython page work? If not, what does happen?
  • AHuman
    AHuman about 10 years
    FYI: The guy offering the bounty won't reward it to anyone unless the answer is REALLY good.
  • PythonNut
    PythonNut about 10 years
    Yea, I kinda guessed that. In fact, I think it would be wrong for me to get a lot of rep for a pathetic answer. This is however, the only answer ATM. I'm trying to help anyway.
  • PythonNut
    PythonNut about 10 years
    Also, AFAIK, someone-or-other has already found this which pretty much does what you want.
  • AHuman
    AHuman about 10 years
    Still, if you just post that code you will get 150 rep.
  • trevorKirkby
    trevorKirkby about 10 years
    The code in the second answer (and on a rather informative other stack overflow question) actually has raised some strange errors. While this works for some time, after a while the program either stops responding with no exceptions raised, or closes with either "Fatal Python error: (pygame parachute) Segmentation Fault " or various TclErrors associated with calling root.update()
  • PythonNut
    PythonNut about 10 years
    Hm... I was warned that pygame does not like running in any thread other than the main thread, which could very well be what's going on. I'll look into it, but it may be an inherent flaw in the pygame code. p.s. You are using Windows, right? I never really got info on your current setup.
  • PythonNut
    PythonNut about 10 years
    Try replacing the loop at the end with root.after(0,pygame.display.update).
  • trevorKirkby
    trevorKirkby about 10 years
    Would I then use root.mainloop()? I may have some other things I need to do in the mainloop, but I could use root.after on them too. I'll try this, though I am not sure how root.update would cause errors...
  • trevorKirkby
    trevorKirkby about 10 years
    Yes the platform is windows
  • trevorKirkby
    trevorKirkby about 10 years
    Also, unless some of the problems pertain to event handling, which I changed, there shouldn't be any issues with the code, as the same code works fine outside of tkinter...
  • PythonNut
    PythonNut about 10 years
    No, it should replace it. You can create a function that calls pygame.display.update and pass that to root.after instead. I'm not clear how root.update is causing errors either. It's strange...
  • PythonNut
    PythonNut about 10 years
    Ah, no I meant code in the pygame core... aka. out of your control.
  • trevorKirkby
    trevorKirkby about 10 years
    I understand that this will call update (along with any other functions I may want to call) however, will that really function to update root as well as call these things? I admit to having more experience with setting timers like this in wx, but in wx you set up the timer and start root.mainloop(). I'll try a few ways ofdoing it though. It is strange that root.update() causes problems, but one or two other pages hint at this.
  • trevorKirkby
    trevorKirkby about 10 years
    So it seems that root.after will schedule a timer for the function. This won't really replace root.update, however, unless there is a way to make it repeatedly reset the timer and run periodically, making it able to use root.mainloop()
  • trevorKirkby
    trevorKirkby about 10 years
    I have the a function called by after that calls itself in after. I am getting an error, "TclStackFree: incorrect freePtr. Call out of sequence?" I notice that this happened immediately when I had no delay on the timer, but when I set it to run at the rate I want the everything to update, it took a few seconds while it seemed to work. Is this error something like a maximum recursion error then?
  • PythonNut
    PythonNut about 10 years
    If you suspect recursion, just print the recursion depth at every invocation.
  • trevorKirkby
    trevorKirkby about 10 years
    It crashed at recursion #309. The recursion limit of python in general is 1000, and I don't think tkinter has a special recursion limit of exactly 309 times.
  • PythonNut
    PythonNut about 10 years
    According to this, it's a threading problem. For us, it's probably somewhere in the pygame or tkinter cores, so we can't get at it. Also see this.
  • trevorKirkby
    trevorKirkby about 10 years
    I do, in fact, have a second thread running, though it is not what would cause the TclError. I believe what is happening is that the functions are being called close enough together back to back that they are both trying to update the screen at the same time.
  • trevorKirkby
    trevorKirkby about 10 years
    By the way why would the use of after() and root.mainloop() be better than calling root.update()?
  • trevorKirkby
    trevorKirkby about 10 years
    I tried doubling the wait between running the function again. The error still happened. It actually happened after fewer recursions.
  • trevorKirkby
    trevorKirkby about 10 years
    I am, in fact, also working with a great many sockets, however, I know for a fact that they are not causing the problem, as this particular error only arose from "root.after()"
  • PythonNut
    PythonNut about 10 years
    1. It was a suggestion from the original SO thread. The treads may be internal Tcl/pygame etc. threads that you do not control. Call the functions every other update, then just for laughs and see if that fixes anything. At think point, I'm not going to be very helpful... :(
  • trevorKirkby
    trevorKirkby about 10 years
    The program stopped responding on the time when I did not call my display.updates and event.pumps. This is only one of the errors that can happen, however, it appears that it is caused no matter what in the combination in pygame. This seems strange, because I would have thought that the person who used the code posted on the above answer would not have had this problem, or they would have mentioned it or perhaps fixed it, as they were the one who had also answered their question.
  • PythonNut
    PythonNut about 10 years
    Is the issue specific to your code? I take it the posted code verbatim also has issues.
  • PythonNut
    PythonNut about 10 years
    Have you seen this? It takes a different approach and may be worth a look.