Using tkinter and pygame together [duplicate] - python

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.

(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'
# [...]

Here are some links.
For embedding in WxPython An Article on pygame.org
For Embedding in WxPython An Article on the WxPython wiki
For embedding in Tkinter see this SO question
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.

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.

Related

Python crash when using Tkinter and Pygame in one Script [duplicate]

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.
(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'
# [...]
Here are some links.
For embedding in WxPython An Article on pygame.org
For Embedding in WxPython An Article on the WxPython wiki
For embedding in Tkinter see this SO question
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.
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.

Tkinter frame not showing with pygame window [duplicate]

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.
(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'
# [...]
Here are some links.
For embedding in WxPython An Article on pygame.org
For Embedding in WxPython An Article on the WxPython wiki
For embedding in Tkinter see this SO question
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.
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.

Pygame & Tkinter [duplicate]

I'd like to use some features from pygame (sprite graphics) in my GUI I made in Tkinter. I know of OcempGUI, but I'd prefer to stick to Tkinter, just use some modules from pygame. This is similar but not quite the same. Is this possible at all? What are potential problems (event loop)?
This works on Linux. If you're lucky, it might work on other operating systems as well.
import Tkinter as tk
import os
w, h = 500, 200
# Add a couple widgets. We're going to put pygame in `embed`.
root = tk.Tk()
embed = tk.Frame(root, width=w, height=h)
embed.pack()
text = tk.Button(root, text='Blah.')
text.pack()
# Tell pygame's SDL window which window ID to use
os.environ['SDL_WINDOWID'] = str(embed.winfo_id())
# The wxPython wiki says you might need the following line on Windows
# (http://wiki.wxpython.org/IntegratingPyGame).
#os.environ['SDL_VIDEODRIVER'] = 'windib'
# Show the window so it's assigned an ID.
root.update()
# Usual pygame initialization
import pygame as pg
pg.display.init()
screen = pg.display.set_mode((w,h))
pos = 0
while 1:
# Do some pygame stuff
screen.fill(pg.Color(0,0,0))
pos = (pos + 1) % screen.get_width()
pg.draw.circle(screen, pg.Color(255,255,255), (pos,100), 30)
# Update the pygame display
pg.display.flip()
# Update the Tk display
root.update()

Python Screen Shot, only desktop background (MacBook Catalina)

I am trying to take a screen shot of a GUI window generated by tkinter, but when I take a screen shot I only get the desktop and not the Python window.
I read with the new o/s Catalina you have to give the terminal permissions, which I have done but still I cannot grab the python window. Even if I do a full screen shot I still cannot see the Python window.
https://github.com/BoboTiG/python-mss/issues/134
I have tried several different ways, but still cannot get to screen grab the Python window.
Does any one else have these problems with Catalina O/S?
from PIL import Image, ImageTk
from tkinter import Tk, BOTH, Canvas, BOTH, NW, W
from tkinter.ttk import Frame, Label, Style
import pyscreenshot
import io
import os
import subprocess
import sys
import mss
top_border_height = 50
bottom_border_height = 70
screen_width = 800
screen_height = 480
video_icon_640x480_x = (800-640)/2
video_icon_640x480_y = (480-480)/2
homeicon64x64_x = 8
homeicon64x64_y = 8
root = Tk('test Screen')
root.geometry("800x480")
w = Canvas(root, width=screen_width, height=screen_height)
back_ground = ImageTk.PhotoImage(Image.open("./icon/wireframe_mode_background.png"))
w.create_image(0, 0, image=back_ground, anchor='nw')
w.video_icon_640x480 = ImageTk.PhotoImage(Image.open("./icon/wireframe_640x480.png"))
w.create_image(video_icon_640x480_x, video_icon_640x480_y, image=w.video_icon_640x480, anchor="nw")
w.home_icon_640x480 = ImageTk.PhotoImage(Image.open("./icon/wireframe_64x64.png"))
w.create_image(homeicon64x64_x,homeicon64x64_y,image=w.home_icon_640x480, anchor="nw")
w.video_icon_640x480_1 = ImageTk.PhotoImage(Image.open("./icon/wireframe_64x64.png"))
w.create_image(728,8,image=w.video_icon_640x480_1, anchor="nw")
w.video_icon_640x480_2 = ImageTk.PhotoImage(Image.open("./icon/wireframe_64x64.png"))
w.create_image(728,80,image=w.video_icon_640x480_2, anchor="nw")
w.pack()
root.mainloop()
im = pyscreenshot.grab(bbox=(10, 10, 510, 510)) # X1,Y1,X2,Y2
im.save('screenshot.png')
with mss.mss() as sct:
filename = sct.shot(mon=-1, output='fullscreen.png')
print(filename)
The answer lies in the Python release (or maybe Pillow) setting up the needed resource when on MacOS so the permission is requested when not available. Until that happens, each user of the code / application will have to enable the Python Launcher to have the Screen Recording permission added since Catalina. I simply had to add this limitation / hiccup for MacOS users to my applications' user manual for now User Manual Screenshot. I have made a separate, specific question about this at Can you request the MacOS Screen Recording permission in Python
If this changes or you know a way to request an OS permission from within a Python application, please let us know. The comment above about answering a similar question is not valid. It is not addressing this very specific MacOS permission issue added since Catalina. (I do not have enough points to comment on the comment but could answer the question; go figure.)

How can I embed an SDL2 window in my Tkinter GUI application?

I'm trying to embed an SDL2 window into my Tkinter application, via pySDL2. How do I setup my pySDL2 window, renderer so that my rendering or drawing appears inside an embedded frame?
Other examples have shown for pygame, but I've discovered that my version of pygame currently does not properly work with SDL2. I understand that there are other implementations of pygame that are attempting to implement SDL2, but compatibility with SDL2 is of primary importance to me.
An example of this working correctly would be a frame in a Tkinter window having a screen which when a button is clicked, draws something to the frame via the pySDL2 API.
In attempting to use the pygame solution found elsewhere, I received a few different errors including BadWindow, BadDrawable (related to X server functions.)
Examples for this were difficult to find, so hopefully this answer will help others. In working with the pySDL2 wrapper API, you need to know about ctypes for different operations. In some cases, the API has been extended to avoid some of these seemingly arcane actions. An example code is provided and then explained below.
from sdl2 import *
import tkinter as tk
from tkinter import *
import random, ctypes
def draw():
global renderer
x1 = ctypes.c_int(random.randrange(0, 600))
y1 = ctypes.c_int(random.randrange(0, 500))
x2 = ctypes.c_int(random.randrange(0, 600))
y2 = ctypes.c_int(random.randrange(0, 500))
r = ctypes.c_ubyte(random.randrange(0, 255))
g = ctypes.c_ubyte(random.randrange(0, 255))
b = ctypes.c_ubyte(random.randrange(0, 255))
SDL_SetRenderDrawColor(renderer, r, g, b, ctypes.c_ubyte(255))
SDL_RenderDrawLine(renderer, x1, y1, x2, y2)
def sdl_update():
global window, event, renderer
SDL_RenderPresent(renderer);
if SDL_PollEvent(ctypes.byref(event)) != 0:
if event.type == SDL_QUIT:
SDL_DestroyRenderer(renderer)
SDL_DestroyWindow(window)
SDL_Quit()
# tkinter stuff #
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)
button1 = Button(buttonwin,text = 'Draw', command=draw)
button1.pack(side=LEFT)
root.update()
#################################
# SDL window stuff #
SDL_Init(SDL_INIT_VIDEO)
window = SDL_CreateWindowFrom(embed.winfo_id())
renderer = SDL_CreateRenderer(window, -1, 0)
SDL_SetRenderDrawColor(renderer, ctypes.c_ubyte(255), ctypes.c_ubyte(255),
ctypes.c_ubyte(255), ctypes.c_ubyte(255))
SDL_RenderClear(renderer)
event = SDL_Event()
draw()
while True:
sdl_update()
root.update()
The example above shows that you can create your tkinter GUI, and then initialize your pySDL2 code, creating a window from whichever frame or window you want, in this case, I've chosen to use a frame I've created, called embed. Using the winfo_id() function available (see the tkinter docs) we can get a handle to the window. The draw() function simply does the drawing using the render module. Notice that for the SDL2 functions that expect certain types, ctypes is used to format those parameters in a way that is expected by SDL2. In the main while loop, a call to the sdl_update() function checks for SDL events. That is followed by the root window (tkinter) update call.
The Button we created has its command linked to draw() and when you click this button, a randomly colored line appears in the frame the SDL2 window is linked to.
This code was adapted from This SO answer from PythonNut regarding pygame and tkinter. That code was originally by user Alex Sallons.

Categories