在 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()
这是非阻塞的,每秒执行一次。
这是代码,问题围绕底部的 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()
这是非阻塞的,每秒执行一次。