如何在我的 Tkinter GUI 应用程序中嵌入 SDL2 window?
How can I embed an SDL2 window in my Tkinter GUI application?
我正在尝试通过 pySDL2 将 SDL2 window 嵌入到我的 Tkinter 应用程序中。如何设置我的 pySDL2 window 渲染器,以便我的渲染或绘图出现在嵌入式框架内?
其他示例已针对 pygame 显示,但我发现我的 pygame 版本目前无法正确使用 SDL2。我知道 pygame 的其他实现正在尝试实现 SDL2,但与 SDL2 的兼容性对我来说是最重要的。
正确工作的一个例子是 Tkinter window 中的一个框架,它有一个屏幕,当点击一个按钮时,通过 pySDL2 API 在框架上绘制一些东西。
在尝试使用在别处找到的 pygame 解决方案时,我收到了一些不同的错误,包括 BadWindow、BadDrawable(与 X 服务器功能相关。)
这方面的例子很难找到,所以希望这个答案能对其他人有所帮助。在使用 pySDL2 包装器 API 时,您需要了解不同操作的 ctypes。在某些情况下,API 已被扩展以避免其中一些看似神秘的操作。提供了一个示例代码,然后在下面进行解释。
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()
上面的例子表明你可以创建你的 tkinter GUI,然后初始化你的 pySDL2 代码,从你想要的任何帧或 window 创建一个 window,在这种情况下,我已经选择使用我创建的框架,称为 embed
。使用可用的 winfo_id()
函数(参见 tkinter 文档)我们可以获得 window 的句柄。 draw()
函数只是使用渲染模块进行绘图。请注意,对于期望某些类型的 SDL2 函数,ctypes
用于以 SDL2 期望的方式格式化这些参数。在主 while 循环中,调用 sdl_update()
函数检查 SDL 事件。接下来是根 window (tkinter) 更新调用。
我们创建的 Button
将其命令链接到 draw()
,当您单击此按钮时,SDL2 window 链接到的帧中会出现一条随机颜色的线。
此代码改编自 This SO answer from PythonNut regarding pygame and tkinter. That code was originally by user Alex Sallons.
我正在尝试通过 pySDL2 将 SDL2 window 嵌入到我的 Tkinter 应用程序中。如何设置我的 pySDL2 window 渲染器,以便我的渲染或绘图出现在嵌入式框架内?
其他示例已针对 pygame 显示,但我发现我的 pygame 版本目前无法正确使用 SDL2。我知道 pygame 的其他实现正在尝试实现 SDL2,但与 SDL2 的兼容性对我来说是最重要的。
正确工作的一个例子是 Tkinter window 中的一个框架,它有一个屏幕,当点击一个按钮时,通过 pySDL2 API 在框架上绘制一些东西。 在尝试使用在别处找到的 pygame 解决方案时,我收到了一些不同的错误,包括 BadWindow、BadDrawable(与 X 服务器功能相关。)
这方面的例子很难找到,所以希望这个答案能对其他人有所帮助。在使用 pySDL2 包装器 API 时,您需要了解不同操作的 ctypes。在某些情况下,API 已被扩展以避免其中一些看似神秘的操作。提供了一个示例代码,然后在下面进行解释。
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()
上面的例子表明你可以创建你的 tkinter GUI,然后初始化你的 pySDL2 代码,从你想要的任何帧或 window 创建一个 window,在这种情况下,我已经选择使用我创建的框架,称为 embed
。使用可用的 winfo_id()
函数(参见 tkinter 文档)我们可以获得 window 的句柄。 draw()
函数只是使用渲染模块进行绘图。请注意,对于期望某些类型的 SDL2 函数,ctypes
用于以 SDL2 期望的方式格式化这些参数。在主 while 循环中,调用 sdl_update()
函数检查 SDL 事件。接下来是根 window (tkinter) 更新调用。
我们创建的 Button
将其命令链接到 draw()
,当您单击此按钮时,SDL2 window 链接到的帧中会出现一条随机颜色的线。
此代码改编自 This SO answer from PythonNut regarding pygame and tkinter. That code was originally by user Alex Sallons.