I currently need a function that runs 128 times per second in python. Here is my code
import time, threading
run_counter = 0
def runner():
global run_counter
while True:
do_something()
run_counter += 1
time.sleep(1/128)
threading.Thread(target=runner).start()
Now, I have something to keep track of how many times do_something
has been executed every second:
while True:
print(run_counter)
run_counter = 0
time.sleep(1)
Now here is the issue. time.sleep(1/128)
should sleep 0.0078125 seconds and it is supposed to run 128 times every second. However, every time print(run_counter)
, it indicates it only has been run 64 times every second.
Additionally, I have a OpenGL context running on the background. I'm using PyOpenGlTk for this:
class WindowGLTkCanvas(OpenGLFrame):
def __init__(self, parent, root, size: Dimension = Dimension(100, 100), scale=1):
OpenGLFrame.__init__(self, root, width=size.x, height=size.y)
PynamicsObject.__init__(self, parent)
self.parent = parent
self.renderable = []
self.scale = scale
self.texture_handler = {}
self.width = size.x
self.height = size.y
self.frame = Routine(self, target=self.redraw)
def initgl(self):
Logger.info("Call: initgl")
glViewport(0, 0, self.width, self.height)
glClearColor(0, 0, 0, 0)
# setup projection matrix
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, self.width, self.height, 0, -1, 1)
# setup identity model view matrix
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glEnable(GL_TEXTURE_2D)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(GL_BLEND)
#glEnable(GL_LINE_SMOOTH)
def redraw(self):
Logger.info("Call: draw")
glClear(GL_COLOR_BUFFER_BIT)
glFlush()
#Logger.info("Call: drawend")
I also have a wrapper to pack this tkinter item on a tkinter root:
class WindowGLTk():
def __init__(self, parent):
super().__init__(parent)
self.root = tk.Tk()
self.gl_canvas = WindowGLTkCanvas(self, self.root, Dim(500, 500))
self.gl_canvas.pack(fill=tk.BOTH, expand=tk.YES)
self.gl_canvas.animate = True
self.parent._viewport = self
#self.frame = Routine(self, self._tick, delay=tps_to_seconds(200))
def _tick(self):
print("RENDER")
#self.gl_canvas.redraw()
def load(self):
self.root.mainloop()
Finally, another handler calls the load
function of this wrapper:
if self._viewport is None:
Logger.warn("No window created for this context. Skipping window startup.")
else:
#self._viewport.frame.start()
self._viewport.load()
I have tried implementing a loop-based time sleep that checks the system time continously:
def sleep(seconds: float):
a = time.time()
b = a + seconds
while time.time() < b:
pass
return
This code is fine and works perfectly. However, it is lagging the redraw()
function of a OpenGLFrame (from PyOpenGlTk), since I debugged the redraw
function and found out it is running lesser than the expected value of 144 times per second (144 FPS)
Are there any alternatives for sleeping in python?
EDIT: I solved this by time.sleeping for 1 second every loop, and used threading.Timer inside the loop to evenly space out each run for 1/128 seconds. Might be a dumb approach but it works.
def _loop(self):
while True:
for i in range(128):
threading.Timer(i / 128, self.target).start()
time.sleep(1)
Are there any alternatives for sleeping in python?
I suggest taking look at threading.Timer
where you specify after what time it is supposed to run function, simple example
import time
import threading
t0 = time.time()
def func():
print("Started at %.03f" % (time.time()-t0))
time.sleep(25) # simulate time-consumnig do_something
for i in range(10): # start func 10 times...
threading.Timer(i/10, func).start() # ...in 1/10 sec intervals
possible output
Started at 0.000
Started at 0.101
Started at 0.201
Started at 0.301
Started at 0.401
Started at 0.501
Started at 0.601
Started at 0.701
Started at 0.802
Started at 0.902
(tested in Python 3.10.12)