如何将简易Turtle游戏程序嵌入Tkinter窗口并生成可执行文件
将Turtle游戏嵌入Tkinter并打包的实现方案
核心思路
Turtle本身基于Tkinter开发,只需将Turtle画布直接嵌入Tkinter主窗口,即可替换原独立的Turtle窗口,后续就能用PyInstaller等工具打包成可执行文件。
修改后的完整代码
import tkinter as tk import turtle import time import random # 主Tkinter窗口设置 root = tk.Tk() root.title("Racing Game") # 创建Turtle画布并嵌入Tkinter窗口 canvas = tk.Canvas(root, width=1200, height=650) canvas.pack() # 初始化Turtle屏幕,绑定到Tkinter画布 wn = turtle.TurtleScreen(canvas) wn.bgcolor("black") wn.bgpic("beach_bg(3).gif") wn.tracer(0) delay = 0.15 # 游戏参数设置 xSetup = 1200 ySetup = 650 movementDelay = 0.001 p1_segments = [] p1 = turtle.RawTurtle(wn) p1.speed(0) p1.shape("square") p1.color("red") p1.up() p1.setpos(-565, 150) p1.direction = "stop" p2_segments = [] p2 = turtle.RawTurtle(wn) p2.speed(0) p2.shape("square") p2.color("blue") p2.up() p2.setpos(-565, -150) p2.direction = "stop" foodX = (xSetup / 2) - 30 foodY = (ySetup / 2) - 30 x = random.randint(foodX * -1, foodX) y = random.randint(foodY * -1, foodY) food1 = turtle.RawTurtle(wn) food1.shape("turtle") food1.speed(0) food1.up() food1.color("green yellow") food1.setpos(x, y) text = turtle.RawTurtle(wn) text.speed(0) text.up() text.shape("square") text.color("white") text.hideturtle() p1Score = 0 p2Score = 0 score = turtle.RawTurtle(wn) score.speed(0) score.up() score.color("white") score.shape("square") score.hideturtle() score.setpos(0, -300) score.write(f"Red: {p1Score} Blue: {p2Score}", align="center", font=("Verdana", 16, "bold")) # 键盘控制函数 def p1_go_up(): if p1.direction != "down": time.sleep(movementDelay) p1.direction = "up" def p2_go_up(): if p2.direction != "down": time.sleep(movementDelay) p2.direction = "up" def p1_go_down(): if p1.direction != "up": time.sleep(movementDelay) p1.direction = "down" def p2_go_down(): if p2.direction != "up": time.sleep(movementDelay) p2.direction = "down" def p1_go_right(): if p1.direction != "left": time.sleep(movementDelay) p1.direction = "right" def p2_go_right(): if p2.direction != "left": time.sleep(movementDelay) p2.direction = "right" def p1_go_left(): if p1.direction != "right": time.sleep(movementDelay) p1.direction = "left" def p2_go_left(): if p2.direction != "right": time.sleep(movementDelay) p2.direction = "left" def reset_game(): time.sleep(0.5) p1.setpos(-565, 150) p1.direction = "stop" p2.setpos(-565, -150) p2.direction = "stop" score.clear() score.write(f"Red: {p1Score} Blue: {p2Score}", align="center", font=("Verdana", 16, "bold")) for segment in p1_segments: segment.setpos(1000, 1000) p1_segments.clear() for segment in p2_segments: segment.setpos(1000, 1000) p2_segments.clear() # 移动逻辑 def move(): if p1.direction == "up": text.clear() p1y = p1.ycor() p1.sety(p1y + 20) if p1.direction == "down": text.clear() p1y = p1.ycor() p1.sety(p1y - 20) if p1.direction == "right": text.clear() p1x = p1.xcor() p1.setx(p1x + 20) if p1.direction == "left": text.clear() p1x = p1.xcor() p1.setx(p1x - 20) if p2.direction == "up": text.clear() p2y = p2.ycor() p2.sety(p2y + 20) if p2.direction == "down": text.clear() p2y = p2.ycor() p2.sety(p2y - 20) if p2.direction == "right": text.clear() p2x = p2.xcor() p2.setx(p2x + 20) if p2.direction == "left": text.clear() p2x = p2.xcor() p2.setx(p2x - 20) def buttonClick(x, y): print(f"Coordinate ({x}, {y})") # 绑定键盘事件 wn.listen() wn.onkeypress(p1_go_up, "w") wn.onkeypress(p2_go_up, "Up") wn.onkeypress(p1_go_down, "s") wn.onkeypress(p2_go_down, "Down") wn.onkeypress(p1_go_right, "d") wn.onkeypress(p2_go_right, "Right") wn.onkeypress(p1_go_left, "a") wn.onkeypress(p2_go_left, "Left") wn.onscreenclick(buttonClick, 1) # 游戏主循环(替换原while True,避免阻塞Tkinter事件循环) def game_loop(): global p1Score, p2Score wn.update() if p1.distance(food1) < 20: x = random.randint(foodX * -1, foodX) y = random.randint(foodY * -1, foodY) food1.setpos(x, y) p1Score += 1 score.clear() score.write(f"Red: {p1Score} Blue: {p2Score}", align="center", font=("Verdana", 16, "bold")) p1_newSegment = turtle.RawTurtle(wn) p1_newSegment.shape("square") p1_newSegment.color("dark red") p1_newSegment.up() p1_segments.append(p1_newSegment) for index in range(len(p1_segments) - 1, 0, -1): p1_xSegment = p1_segments[index - 1].xcor() p1_ySegment = p1_segments[index - 1].ycor() p1_segments[index].setpos(p1_xSegment, p1_ySegment) if len(p1_segments) > 0: p1_xhead = p1.xcor() p1_yhead = p1.ycor() p1_segments[0].setpos(p1_xhead, p1_yhead) if p2.distance(food1) < 20: x = random.randint(foodX * -1, foodX) y = random.randint(foodY * -1, foodY) food1.setpos(x, y) p2Score += 1 score.clear() score.write(f"Red: {p1Score} Blue: {p2Score}", align="center", font=("Verdana", 16, "bold")) p2_newSegment = turtle.RawTurtle(wn) p2_newSegment.shape("square") p2_newSegment.color("midnight blue") p2_newSegment.up() p2_segments.append(p2_newSegment) for index in range(len(p2_segments) - 1, 0, -1): p2_xSegment = p2_segments[index - 1].xcor() p2_ySegment = p2_segments[index - 1].ycor() p2_segments[index].setpos(p2_xSegment, p2_ySegment) if len(p2_segments) > 0: p2_xhead = p2.xcor() p2_yhead = p2.ycor() p2_segments[0].setpos(p2_xhead, p2_yhead) if p1.ycor() < -305 or p1.ycor() > 320 or p1.xcor() > 587 or p1.xcor() < -597: text.write("Red went over the line!", align="center", font=("Verdana", 25, "bold")) p2Score +=1 reset_game() if p2.ycor() < -305 or p2.ycor() > 320 or p2.xcor() > 587 or p2.xcor() < -597: text.write("Blue went over the line!", align="center", font=("Verdana", 25, "bold")) p1Score += 1 reset_game() move() # 碰撞检测 for p1_segment in p1_segments: if p1_segment.distance(p1) < 20: text.clear() text.write("Red ate his self!", align="center", font=("Verdana", 25, "bold")) p2Score += 1 reset_game() if p2.distance(p1_segment) < 20: text.clear() text.write("Game Over! Red Won!", align="center", font=("Verdana", 25, "bold")) time.sleep(0.5) p1Score = 0 p2Score = 0 reset_game() for p2_segment in p2_segments: if p2_segment.distance(p2) < 20: text.clear() text.write("Blue ate his self!", align="center", font=("Verdana", 25, "bold")) p1Score += 1 reset_game() if p1.distance(p2_segment) < 20: text.clear() text.write("Game Over! Blue Won!", align="center", font=("Verdana", 25, "bold")) time.sleep(0.5) p1Score = 0 p2Score = 0 reset_game() # 循环调用,维持游戏运行 root.after(int(delay * 1000), game_loop) # 启动游戏循环 game_loop() # 启动Tkinter主循环 root.mainloop()
关键修改点
- 用
tkinter.Tk()创建主窗口,替代Turtle默认的独立窗口 - 创建Tkinter画布
Canvas,通过turtle.TurtleScreen(canvas)将Turtle绑定到该画布 - 所有Turtle对象改用
RawTurtle(wn)创建,确保它们属于嵌入的画布 - 替换原
while True循环为Tkinter的after()方法,避免阻塞主事件循环,这是Tkinter应用的标准做法 - 保留原游戏的所有逻辑和交互,仅调整窗口载体
打包成可执行文件
使用PyInstaller打包时注意以下步骤:
- 安装PyInstaller:
pip install pyinstaller - 打包命令(假设脚本名为
racing_game.py):pyinstaller --onefile --windowed --add-data "beach_bg(3).gif;." racing_game.py--onefile:生成单个可执行文件--windowed:不显示控制台窗口(调试时可去掉)--add-data:将背景图片打包进去,Windows用;分隔路径,macOS/Linux用:
内容的提问来源于stack exchange,提问作者potaka
相关产品推荐
相关产品推荐

