我将pygame窗口嵌入到Tkinter中,如何操作pygame窗口? [英] I'm embedding a pygame window into Tkinter, how do I manipulate the pygame window?
问题描述
所以我正在pygame中制作游戏,我也想使用tkinter.我将pygame窗口嵌入到tkinter窗口中,但似乎无法执行任何操作.
So I'm making a game in pygame, and I want to use tkinter as well. I embedded a pygame window into a tkinter window, but I can't seem to do anything with it.
对于上下文,这是完整的代码:
For context, here is the full code:
import Tkinter as tk
import os
import platform
import pygame
class window(object):
def __init__(self):
self.root = tk.Tk() # Main window
self.root.title("SquareScape")
self.root.iconbitmap(r'C:\Users\17_es\PycharmProjects\square_puzzle\images\icon.ico')
self.root.configure(background='#9b9b9b')
# Large Frame
self.win_frame = tk.Frame(self.root, width=670, height=520, highlightbackground='#595959', highlightthickness=2)
# menu (left side)
self.menu = tk.Frame(self.win_frame, width=150, height=516, highlightbackground='#595959', highlightthickness=2)
self.menu_label = tk.Label(self.menu, text="Settings", bg='#8a8a8a', font=("Courier", "16", "bold roman"))
self.mute = tk.Button(self.menu, text="XXXX", font="Courier", bg='#bcbcbc', activebackground='#cdcdcd')
# pygame
self.pygame_frame = tk.Frame(self.win_frame, width=514, height=514, highlightbackground='#595959', highlightthickness=2)
self.embed = tk.Frame(self.pygame_frame, width=512, height=512,)
# Packing
self.win_frame.pack(expand=True)
self.win_frame.pack_propagate(0)
self.menu.pack(side="left")
self.menu.pack_propagate(0)
self.menu_label.pack(ipadx=60, ipady=2)
self.mute.pack(ipadx=40, ipady=2, pady=5)
self.pygame_frame.pack(side="left")
self.embed.pack()
#This embeds the pygame window
os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
if platform.system == "Windows":
os.environ['SDL_VIDEODRIVER'] = 'windib'
#Start pygame
pygame.init()
self.win = pygame.display.set_mode((512, 512))
self.win.fill(pygame.Color(255, 255, 255))
pygame.display.init()
self.root.mainloop()
screen = window()
#Here is sample code that I want to run
pygame.draw.rect(screen.win, (0, 0, 255), (200, 200, 100, 100))
当我使用 pygame.draw.rect(screen.win,(0,0,255),(200,200,100,100))
时,什么也没发生.在类内部使用pygame可以正常工作,但是在我更复杂的游戏中,对所有变量使用self.variable似乎是不必要的.
When I use pygame.draw.rect(screen.win, (0, 0, 255), (200, 200, 100, 100))
, nothing happens. Using pygame inside the class worked, but in my more complicated game, using self.variable for all my variables seems unnecessary.
如何在窗口类之外的pygame窗口中运行代码?
How can I run my code in the pygame window outside of the window class?
推荐答案
所以-除了明显缺少对 pygame.display.flip
的调用-我想这是您对 pygame.display.init
( pygame.init
已经调用了该代码)-我发现tkinter需要在打包框架完全可用之前初始化其窗口和小部件.由Pygame使用.
So - besides the obvious missing call to pygame.display.flip
- which I suppose is what you intended with the call to pygame.display.init
(pygame.init
already calls that one) - what I found out is that tkinter needs to initialize its windows and widgets before the packed frame is fully available to be used by Pygame.
为此,我在调用 pygame.init
之前,先添加了对 self.root.update_idletasks()
的调用,然后为我的平台明确设置了视频驱动程序(您已经在Windows中执行过此操作),使一切正常.
I did that by adding a call to self.root.update_idletasks()
before calling pygame.init
-- that, and explicitly setting the video driver for my platform (which you already does for Windows), made things work.
无论如何,无论如何,您都没有在代码中显示是否要调用Pygamedrawing函数-照原样,很可能一切正确,但是 screen.window()之后的代码
永远不会运行(或者只是在程序退出时运行)-因为您在应用程序类的 __ init __
方法内调用了tkinter.mainloop.
Anyway, also, in your code you did not show were you wanted to make the calls to Pygamedrawing functions - as it is, it is well possible that everything is correct, but the code after screen.window()
is just never run (or rather, just run at program exit) - because you call tkinter.mainloop inside the __init__
method of your application class.
将调用移到 __ init __
之外是一个好习惯,因此您也可以初始化其他对象和资源-实际上,您确实拥有屏幕
对象以对其进行操作.通过在 __ init __
内部进行该调用,就好像您的整个程序都在初始化内部"运行一样.
Moving the call to mainloop
outside the __init__
is a good practice, so you can initialize other objects and resources as well - and you actualy do have the screen
object to operate things on. By making that call inside __init__
is like your whole program was running "inside the initialization".
简而言之:
- 在初始化pygame之前先呼叫
tkinter.update_iddletasks()
- 记住在使用Pygame绘制任何内容后调用
pygame.display.flip
- 安排代码,以便您的绘图调用得以实际执行,并且在进入tkinter循环的调用之后不会被阻塞
- 您应该认真考虑使用Python 3.7或更高版本-(唯一的"python 2"代码中有
import Tkinter
变成了import tkinter
在Python 3中).Python 2在行的最后确实,并且上面没有针对pygame之类的项目的更新.
- call
tkinter.update_iddletasks()
before initializing pygame - remember to call
pygame.display.flip
after you draw anything with Pygame - arrange your code so that your drawing calls are actually executed, and not blocked after the call to enter tkinter's loop
- You should seriously consider using Python 3.7 or later - (the only "python 2" code there is
import Tkinter
which becomesimport tkinter
in Python 3). Python 2 is really at the end of line, and there are no updates for projects like pygame on it. .
也就是说,这是您的代码,经过修改后可以在Linux + Python 3上运行(仍应在Windows上运行),并使用嵌入式pygame框架实际执行一些操作.
That said, here is your code, modified to run on Linux + Python 3 (should still work on Windows), and to actually perform some actions using the embedded pygame frame.
import tkinter as tk
import os
import platform
import pygame
import time
class window(object):
def __init__(self):
self.root = tk.Tk() # Main window
self.root.title("SquareScape")
# self.root.iconbitmap(r'C:\Users\17_es\PycharmProjects\square_puzzle\images\icon.ico')
self.root.configure(background='#9b9b9b')
# Large Frame
self.win_frame = tk.Frame(self.root, width=670, height=520, highlightbackground='#595959', highlightthickness=2)
# menu (left side)
self.menu = tk.Frame(self.win_frame, width=150, height=516, highlightbackground='#595959', highlightthickness=2)
self.menu_label = tk.Label(self.menu, text="Settings", bg='#8a8a8a', font=("Courier", "16", "bold roman"))
self.mute = tk.Button(self.menu, text="XXXX", font="Courier", bg='#bcbcbc', activebackground='#cdcdcd')
tk.Button(self.menu, text="<->", command=lambda: setattr(self, "direction", (-self.direction[0], self.direction[1]))).pack()
tk.Button(self.menu, text="^", command=lambda: setattr(self, "direction", (self.direction[0], -self.direction[1]))).pack()
# pygame
self.pygame_frame = tk.Frame(self.win_frame, width=514, height=514, highlightbackground='#595959', highlightthickness=2)
self.embed = tk.Frame(self.pygame_frame, width=512, height=512,)
# Packing
self.win_frame.pack(expand=True)
self.win_frame.pack_propagate(0)
self.menu.pack(side="left")
self.menu.pack_propagate(0)
self.menu_label.pack(ipadx=60, ipady=2)
self.mute.pack(ipadx=40, ipady=2, pady=5)
self.pygame_frame.pack(side="left")
self.embed.pack()
#This embeds the pygame window
os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
system = platform.system()
if system == "Windows":
os.environ['SDL_VIDEODRIVER'] = 'windib'
elif system == "Linux":
os.environ['SDL_VIDEODRIVER'] = 'x11'
self.root.update_idletasks()
#Start pygame
pygame.init()
self.win = pygame.display.set_mode((512, 512))
self.bg_color = (255, 255, 255)
self.win.fill(self.bg_color)
self.pos = 0, 0
self.direction = 10, 10
self.size = 40
self.color = (0, 255, 0)
self.root.after(30, self.update)
self.root.mainloop()
def update(self):
first_move = True
pygame.draw.rect(self.win, self.bg_color, self.pos + (self.size, self.size))
self.pos = self.pos[0] + self.direction[0], self.pos[1] + self.direction[1]
if self.pos[0] < 0 or self.pos[0] > 512 - self.size:
self.direction = -self.direction[0], self.direction[1]
self.pos = self.pos[0] + 2 * self.direction[0], self.pos[1] + self.direction[1]
if self.pos[1] < 0 or self.pos[1] > 512 - self.size:
self.direction = self.direction[0], -self.direction[1]
self.pos = self.pos[0] + self.direction[0], self.pos[1] + 2 * self.direction[1]
pygame.draw.rect(self.win, self.color, self.pos + (self.size, self.size))
pygame.display.flip()
self.root.after(30, self.update)
screen = window()
tk.mainloop()
这篇关于我将pygame窗口嵌入到Tkinter中,如何操作pygame窗口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!