在 python 中,有没有办法设置非阻塞的 tkinter 更新计时器,同时保持 window 打开?

In python, is there a way to set a tkinter update timer that is non blocking, while also keeping the window open?

这是代码,问题围绕底部的 mainLoop() 函数。我想知道是否有一种方法可以在基于 'fps' 变量的时间间隔内以非阻塞方式调用此函数。

我尝试了一些技术上应该可行的方法,我导入了 'threading' 并在 mainLoop() 函数中设置了一个 threading.Timer 以在 1/fps 秒后再次调用 mainLoop() 函数。唯一的问题是 window 在打开后立即关闭,我知道如果我调用 tkinter 的 root.mainloop() 函数,虽然 window 会保持打开状态,但它会阻塞并且停止其余代码。

(我也尝试了 while True 循环并在最后有 time.sleep(1/fps),但这需要在再次调用代码之前完成所有操作(它是阻塞的))

是否有更好的方法来做到这一点,也许使用 asyncio 或类似的东西?在 JavaScript 中执行此操作只是 'setInterval(mainLoop, 1000/fps)'.

的问题
#! /usr/bin/python
from tkinter import *
from threading import Timer
import math

# setup window + canvas
root = Tk()
root.title("Pendulum Simulation")
root.resizable(0,0)
canvas = Canvas(root, width = 600, height = 600, bg="grey", bd=0)
canvas.pack()
root.update()

# world variables
fps = 60
gravity = -1250

# Pendulum variables
length = 240
radius = 15
pinX = canvas.winfo_width()/2
pinY = canvas.winfo_height()/2
a = 179.9 # angle to vertical
a *= math.pi/180 # conversion to radians, needed for math
aV = 0 # angular velocity



# two wrapper functions to draw shapes
def drawLine(x1,y1,x2,y2):
    id = canvas.create_line(x1,canvas.winfo_height()-y1,x2,canvas.winfo_height()-y2, width = 2)
def drawCircle(x,y,r,c):
    id = canvas.create_oval(x - r,canvas.winfo_height()-y -r,x+r,canvas.winfo_height()-y+r, fill = c, width = 2)



def drawObjects():
    x = pinX + length*math.sin(a)
    y = pinY - length*math.cos(a)
    drawLine(pinX,pinY, pinX + length*math.sin(a), pinY - length*math.cos(a))
    drawCircle(pinX,pinY,2,"black")
    drawCircle(x,y,radius, "crimson")
    
def moveObjects():
    global aV
    global a
    
    aA = gravity/length * math.sin(a)
    aV += aA/fps
    a += aV/fps


def mainLoop():
    canvas.delete(ALL)
    drawObjects()
    root.update()
    moveObjects()
    Timer(round(1/fps,2), mainLoop).start()

mainLoop()

Tkinter 有自己的主循环。实际上,您可以使用根 window 的 after 方法将您自己的代码挂钩到该循环中。首先,我相信您还没有完成,启动主循环。在你的循环函数中,你可以在调用之后使用 after 方法。

def mainLoop():
     root.after(1000, mainLoop) #Calls mainLoop every 1 second.
     (...)
mainLoop()
root.mainloop()

这是非阻塞的,每秒执行一次。