5

I'm using after() for a Tkinter animation loop:

from tkinter import Tk, Canvas
from time import time

root = Tk()
root.configure(width=1920, height=1080)
root.resizable(True, True)

main = Canvas(root, width=1600, height=900)

ball = main.create_oval(80, 80, 120, 120, fill="#FF0000")
main.pack()

frametarget = 17

lasttime = time()
frametimes = []


def move():
    global lasttime
    frametimes.append(time() - lasttime)
    lasttime = time()

    main.move(ball, 1, 1)
    root.after(frametarget, move)


root.after(0, move)
root.mainloop()

print(sum(frametimes[60:]) / len(frametimes[60:]) * 1000)

This generally seems to be updating in the region of ~30ms and looks visually slow when left unattended, but when the mouse is continuously moving over the window it hits 17ms consistently and runs smooth, but reverts back once this stops.

I'm using Python 3.7, Windows 10

Tests:

Average frame time where frametarget = 17 (for ~60fps)

Without mouse movement = 29.28ms
With mouse movement = 17.08ms

Average frame time where frametarget = 50 (for 20fps)

Without mouse movement = 62.50ms
With mouse movement = 50.04ms

Update: The same issue is not present on Linux.

IceBlue02
  • 71
  • 3
  • 1
    You'll need to show a [mcve] to get any specific help. Generally I can't think of any reason this should happen unless you are using Windows XP. Those 2 update lines are not needed - remove them to see if that helps. Note that by far the bottleneck will be the drawing to screen step, I usually don't count on anything faster than 25 ms, the logic rarely factors in. Besides, a standard monitor is 30 Hz, so anything faster than 33 ms will make no difference to the user. – Novel Dec 01 '20 at 17:40
  • 1
    @Novel I've updated with a better example, with some tests. Even when running at a lower framerate the same discrepancy between when the mouse is moving or not exists, and the issue is more the inconsistency from it causing the animation to appear slower/faster at different points rather than the frame rate itself. – IceBlue02 Dec 01 '20 at 18:35
  • When I run the (updated) code in your question and _without_ the mouse continuously moving over the window, I consistently get timings very close to `17`. – martineau Dec 01 '20 at 18:45
  • Interesting, thanks for the code. How are you running this? When I try your code it's very accurate, and sticks to the set framerate down to 1 ms. Moving my mouse makes no difference. What OS, python version, environment are you using? – Novel Dec 01 '20 at 18:46
  • @Novel Python 3.7, Windows 10. I've tried running via IDE and directly from cmd and got the same results, not that it should make a difference. I'll try testing on Linux and a different PC and see if the same result occurs, although it is going to be necessary to ensure it works for Windows nonetheless. – IceBlue02 Dec 01 '20 at 18:51
  • I see no difference on OSX; must be an issue with Windows. – Bryan Oakley Dec 01 '20 at 19:31

2 Answers2

2

Upgrading to Python 3.8.6 (from 3.7.4) solved the issue.

It appeared to be unique to my own environment as the problem did not reproduce on OSX, Linux or other Windows environments.

IceBlue02
  • 71
  • 3
  • Very interesting to note, thanks for the update. – Novel Dec 01 '20 at 21:12
  • Not that unique, I'm running Python 3.8.5, Windows 10, and I have the same issue except I don't need to move the mouse, just hover with it over the window. Maybe it's some way of it to save resources? – Yuval.R Jan 10 '22 at 08:30
0

I had almost the same problem except in my case I didn't have to move the mouse, just hover with it on the window, but I think this solution will work for both cases.
Add this at the start of your code:

from ctypes import windll

timeBeginPeriod = windll.winmm.timeBeginPeriod
timeBeginPeriod(1)
Yuval.R
  • 1,182
  • 4
  • 15