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

如何让使用Tkinter按钮的Python Turtle程序持续运行?

问题描述

我用Python Turtle开发了一个战争统计计算器,通过Tkinter按钮构建UI,但脱离IDLE打开后程序无法持续运行。尝试在按钮代码后添加循环或time.sleep(),结果程序直接无响应。使用Windows 11系统、Python 3.12.3版本,网上查找的解决方案均无效。

原代码如下:

import time
import turtle
from turtle import Screen
from tkinter import *

screen = turtle.Screen()
screen.setup(width=1000, height=400)
screen.title("WBT war stat calculator")
screen.bgcolor("light blue")
canvas = screen.getcanvas()

save_mil = ""
save_diplo = ""
save_eco = ""
save_land = ""

def update_total():
    if not save_mil == "":
        if not save_diplo == "":
            if not save_eco == "":
                if not save_land == "":
                    write_total.clear()
                    stat1 = (((((save_eco / 3) * 2) + (save_diplo / 3) + save_mil) / 2) * save_land)
                    write_total.write(stat1, font=("Arial",30))

def make_text(x, y):
    writer = turtle.Turtle()
    writer.penup()
    writer.speed(0)
    writer.hideturtle()
    writer.goto(x, y)
    return writer

def make_textbox(title1, des1, title2, des2, limit, type1):
    answer = screen.textinput(title1, des1)
    global save_mil
    global save_diplo
    global save_eco
    global save_land
    if answer == None:
        pass
    else:
        try:
            stat = float(answer)
            if stat > limit:
                pass
            else:
                if type1 == 1:
                    save_mil = stat
                    write_mil.clear()
                    write_mil.write(stat, font=("Arial",30))
                if type1 == 2:
                    save_diplo = stat
                    write_diplo.clear()
                    write_diplo.write(stat, font=("Arial",30))
                if type1 == 3:
                    save_eco = stat
                    write_eco.clear()
                    write_eco.write(stat, font=("Arial",30))
                if type1 == 4:
                    save_land = stat
                    write_land.clear()
                    write_land.write(stat, font=("Arial",30))
                update_total()
        except:
            stat = (limit + 1)

        while stat > limit:
                if answer == None:
                    stat = 1

                else:
                    try:
                        answer = screen.textinput(title2, des2)
                        stat = float(answer)
                        if stat > limit:
                            pass
                        else:
                            if type1 == 1:
                                save_mil = stat
                                write_mil.clear()
                                write_mil.write(stat, font=("Arial",30))
                            if type1 == 2:
                                save_diplo = stat
                                write_diplo.clear()
                                write_diplo.write(stat, font=("Arial",30))
                            if type1 == 3:
                                save_eco = stat
                                write_eco.clear()
                                write_eco.write(stat, font=("Arial",30))
                            if type1 == 4:
                                save_land = stat
                                write_land.clear()
                                write_land.write(stat, font=("Arial",30))
                            update_total()
                    except:
                        pass


def textbox_mil():
    make_textbox(" ", "What are the military stats? 1 - 10", " ", "What are the military stats again? 1 - 10", 10, 1)

def textbox_diplo():
    make_textbox(" ", "Great, what are the diplomatic stats now? 1 - 10", " ", "Sorry, what are the diplomatic stats now? 1 - 10", 10, 2)
            
def textbox_eco():
    make_textbox(" ", "Eco stats? 1 - 10", " ", "Eco stats again? 1 - 10", 10, 3)
    
def textbox_land():
    make_textbox(" ", "How much of the earth do you have by percentage? (as decimal)", " ", "Repeat how much of the earth do you have by percentage? (as decimal)", 1, 4)


def make_button(x_pos, y_pos, text_t, command_d):
    button = Button(canvas.master, text = text_t, command = command_d)
    button.pack()
    button.place(x = x_pos, y = y_pos)



mil_button = make_button(100, 70, "Military", textbox_mil)
diplo_button = make_button(100, 140, "Diplomatic", textbox_diplo)
eco_button = make_button(100, 210, "Economic", textbox_eco)
land_button = make_button(100, 280, "Land%", textbox_land)

write_mil = make_text(-300, 100)
write_diplo = make_text(-300, 30)
write_eco = make_text(-300, -40)
write_land = make_text(-300, -110)
write_total = make_text(-90, -40)
write_extra = make_text(-100, 200)
write_extra.pendown()
write_extra.goto(-100,-200)
write_extra.penup()
write_extra.goto(-90,100)
write_extra.write("TOTAL:", font=("Arial",30))
解决方案

问题核心是Turtle基于Tkinter运行,必须依赖Tkinter的事件循环才能维持窗口存活和UI交互。手动添加time.sleep()或自定义循环会阻塞事件循环,导致UI无响应;而未手动启动事件循环时,程序会直接退出。

修复步骤

  1. 移除所有手动添加的time.sleep()或自定义循环,避免打断Tkinter的事件处理流程。
  2. 在代码末尾添加Turtle事件循环启动语句:
    screen.mainloop()
    
    也可以用turtle.done(),两者效果一致,都会启动Tkinter主事件循环,保持窗口运行并响应用户操作。

优化后完整代码(关键修改已标注)

import time
import turtle
from turtle import Screen
from tkinter import *

screen = turtle.Screen()
screen.setup(width=1000, height=400)
screen.title("WBT war stat calculator")
screen.bgcolor("light blue")
canvas = screen.getcanvas()

save_mil = ""
save_diplo = ""
save_eco = ""
save_land = ""

def update_total():
    if not save_mil == "":
        if not save_diplo == "":
            if not save_eco == "":
                if not save_land == "":
                    write_total.clear()
                    stat1 = (((((save_eco / 3) * 2) + (save_diplo / 3) + save_mil) / 2) * save_land)
                    write_total.write(stat1, font=("Arial",30))

def make_text(x, y):
    writer = turtle.Turtle()
    writer.penup()
    writer.speed(0)
    writer.hideturtle()
    writer.goto(x, y)
    return writer

def make_textbox(title1, des1, title2, des2, limit, type1):
    answer = screen.textinput(title1, des1)
    global save_mil
    global save_diplo
    global save_eco
    global save_land
    if answer == None:
        pass
    else:
        try:
            stat = float(answer)
            if stat > limit:
                pass
            else:
                if type1 == 1:
                    save_mil = stat
                    write_mil.clear()
                    write_mil.write(stat, font=("Arial",30))
                if type1 == 2:
                    save_diplo = stat
                    write_diplo.clear()
                    write_diplo.write(stat, font=("Arial",30))
                if type1 == 3:
                    save_eco = stat
                    write_eco.clear()
                    write_eco.write(stat, font=("Arial",30))
                if type1 == 4:
                    save_land = stat
                    write_land.clear()
                    write_land.write(stat, font=("Arial",30))
                update_total()
        except:
            stat = (limit + 1)

        while stat > limit:
                if answer == None:
                    stat = 1

                else:
                    try:
                        answer = screen.textinput(title2, des2)
                        stat = float(answer)
                        if stat > limit:
                            pass
                        else:
                            if type1 == 1:
                                save_mil = stat
                                write_mil.clear()
                                write_mil.write(stat, font=("Arial",30))
                            if type1 == 2:
                                save_diplo = stat
                                write_diplo.clear()
                                write_diplo.write(stat, font=("Arial",30))
                            if type1 == 3:
                                save_eco = stat
                                write_eco.clear()
                                write_eco.write(stat, font=("Arial",30))
                            if type1 == 4:
                                save_land = stat
                                write_land.clear()
                                write_land.write(stat, font=("Arial",30))
                            update_total()
                    except:
                        pass


def textbox_mil():
    make_textbox(" ", "What are the military stats? 1 - 10", " ", "What are the military stats again? 1 - 10", 10, 1)

def textbox_diplo():
    make_textbox(" ", "Great, what are the diplomatic stats now? 1 - 10", " ", "Sorry, what are the diplomatic stats now? 1 - 10", 10, 2)
            
def textbox_eco():
    make_textbox(" ", "Eco stats? 1 - 10", " ", "Eco stats again? 1 - 10", 10, 3)
    
def textbox_land():
    make_textbox(" ", "How much of the earth do you have by percentage? (as decimal)", " ", "Repeat how much of the earth do you have by percentage? (as decimal)", 1, 4)


def make_button(x_pos, y_pos, text_t, command_d):
    button = Button(canvas.master, text = text_t, command = command_d)
    button.pack()
    button.place(x = x_pos, y = y_pos)



mil_button = make_button(100, 70, "Military", textbox_mil)
diplo_button = make_button(100, 140, "Diplomatic", textbox_diplo)
eco_button = make_button(100, 210, "Economic", textbox_eco)
land_button = make_button(100, 280, "Land%", textbox_land)

write_mil = make_text(-300, 100)
write_diplo = make_text(-300, 30)
write_eco = make_text(-300, -40)
write_land = make_text(-300, -110)
write_total = make_text(-90, -40)
write_extra = make_text(-100, 200)
write_extra.pendown()
write_extra.goto(-100,-200)
write_extra.penup()
write_extra.goto(-90,100)
write_extra.write("TOTAL:", font=("Arial",30))

# 关键:启动Turtle事件循环,维持窗口运行
screen.mainloop()

无效原因说明

  • time.sleep()会阻塞主线程,Tkinter无法处理按钮点击、窗口刷新等事件,导致程序无响应。
  • 自定义循环会占用主线程,打断Tkinter的事件处理流程,造成UI冻结。
  • IDLE环境下程序能运行是因为IDLE本身会启动Tkinter事件循环,脱离IDLE后必须手动启动。

内容的提问来源于stack exchange,提问作者Charles250211

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 05:39:54