Samuel May
Samuel May

Reputation: 75

Problems with detecting delay and removing it from .after() in python

I am developing a Tkinter python game - long story short it needs to be able to run at different FPS values. However, I am having trouble maintaining a consistent second length.

I have tried to make it detect lag and take it away from the .after() function:

def UpdatesEveryFrame():
    s = time.perf_counter()

    # Code here

    s = int((time.perf_counter()  - s)*1000)
    LabelProductionTime3.after(int(1000 / fps) - s, UpdatesEveryFrame)

However, this is unsuccessful. It seems to create an accurate value in milliseconds (usually around 15) but this does not make an accurate second delay. I have tried replacing perf_counter() with time() but this has the same effect.

Because of what the game is based upon, it is essential that there is an accurate second delay. Can you help? Thanks.

Upvotes: 1

Views: 107

Answers (1)

typedecker
typedecker

Reputation: 1390

If the goal here is precision, then perhaps you should try the time.perf_counter_ns method of the time module, it specifically is made to be more precise than time.perf_counter, and gives time in nanoseconds, further if the time has to be converted back into seconds, it can be done using unit conversion.

Further, the documentation of time.perf_counter method mentions this as well -:

Use perf_counter_ns() to avoid the precision loss caused by the float type.

def UpdatesEveryFrame():
    s = time.perf_counter_ns()/(10 ** 9) # used perf_counter_ns, divided by (10 ** 9) to convert to seconds.

    # Code here

    s = int((time.perf_counter_ns()/(10 ** 9) - s)*1000) # used perf_counter_ns, divided by (10 ** 9) to convert to seconds.
    LabelProductionTime3.after(int(1000 / fps) - s, UpdatesEveryFrame)

EDIT: There also exists time.monotonic method, designed specifically to measure the time elapsed between two consecutive calls, it returns time in fractional seconds similar to time.perf_counter, so no changes have to be made in the current code except the name of the function itself.

def UpdatesEveryFrame():
    s = time.monotonic() # Changed method name.

    # Code here

    s = int((time.monotonic()  - s)*1000)
    LabelProductionTime3.after(int(1000 / fps) - s, UpdatesEveryFrame) # Changed method name.

Further, similar to the method time.perf_counter_ns available as a more precise version of time.perf_counter, there also exists a more precise version of the time.monotonic method that returns time in nanoseconds and functions similar to time.monotonic, namely time.monotonic_ns.

Upvotes: 2

Related Questions