You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将简易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打包时注意以下步骤:

  1. 安装PyInstaller:pip install pyinstaller
  2. 打包命令(假设脚本名为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 13:08:09