Moving a window on the screen

Akira Li 4kir4.1i at gmail.com
Sat Nov 8 15:31:20 EST 2014


Terry Reedy <tjreedy at udel.edu> writes:

> On 11/8/2014 11:35 AM, Akira Li wrote:
>> "ast" <nomail at invalid.com> writes:
>>
>>> Ok, thx, it works now with:
>>>
>>> import tkinter
>>> fen = tkinter.Tk()
>>>
>>> x=0
>>>
>>> def moveW():
>>>     global x
>>>     fen.geometry("200x200+%d+10"  %  x)
>>>     x = x + 10
>>>     if (x < 1200):
>>>         fen.after(50, moveW)
>>>
>>> moveW()
>>
>> In general, to avoid the start time "drift" [1],
>
> which is hardly noticeable

Do you mean hardly noticeable for these particular *period*, delta_x and
(small) fixed number of repeatitions?

That is why I said, "in general".

The link [1] that I've provided contains an example where it *is*
noticable.

>> you could lock the
>> execution with a timer e.g., to move the window from left to right
>> *delta_x* pixels at a time every *period* ms [2]:
>
> On my Win7 machine, your complicated code is much worse as it causes
> the window to jump about every half second

Could you add print(timer()) inside move(), to see the values? What
happens if you increase the period to 100 ms?

>>    #!/usr/bin/env python3
>>    from time import monotonic
>>    from tkinter import Tk
>>
>>    def timer():
>>        return int(monotonic() * 1000) # milliseconds
>>
>>    def call_repeatedly(period, function, *args):
>>        root.after(period - timer() % period, call_repeatedly, period,
>
> The '- timer() % period' 'correction' is wrong when not 0 as it causes
> jumps.

The formula is correct. It is obvious if you make the period one second
(1000ms) and print the timer() values (in milliseconds):

  52002
  53000
  54000
  55000
  56001
  57000
  58000
  59001
  60001
  61000
  62000
  ...

As you can see the start time is locked with the timer: the function is
called at whole seconds. Individual calls may happens slightly sooner or
later but the timing doesn't drift, the difference
between the expected start time and the actual start time is 1ms:

  >>> M[0], M[-1], M[0] + 1000*(len(M)-1)
  (52002, 224001, 224002)

Here's the same thing if I remove the locking `-timer() % period` and
use just `root.after(period, call..)`:

  34235
  35236
  36236
  37236
  38236
  39237
  40237
  41237
  42237
  43238
  44238
  45238
  46238
  47239
  48239
  ...

The start time drifts:

  >>> L[0], L[-1], L[0] + 1000*(len(L)-1)
  (34235, 206279, 206235)

the difference between the expected start time and the actual start time
is 44ms (4400% worse than the previous method).

I agree, for the purpose of moving a window on the screen, the
difference doesn't matter though if it is a GUI clock then you should
not ignore it otherwise it will be wrong by a minute in a couple of
days.

>>                   function, *args) # schedule the next call
>>        function(*args)
>>
>>    def move(delta_x, max_x, width=200, x=[0]):
>>        root.geometry("%dx50+%d+100"  %  (width, x[0]))
>>        x[0] += delta_x # poor man's object
>>        if x[0] > (max_x - width):
>>            root.destroy() # exit
>>
>>    root = Tk()
>>    period = 20 # call every *period* milliseconds
>>    delta_x = 2 # how many pixels to move at a time
>>    root.after(period - period % timer(), call_repeatedly, period,
>
> 'period % timer()' is nonsensical as timer() is arbitrary. It will
> typically be 0 anyway.  'after(0, ...)'  works fine.
>

It is a bug (the terms are swapped by mistake). Thank you for
noticing. It should be `timer() % period` instead. The same expression
as used in the call_repeatedly() and in the link [2] that I've provided.

`period - timer() % period` is used here to make the first start time at
the exact boundary so there is the same interval between function(*args)  calls.

[1]: http://stackoverflow.com/questions/8600161/executing-periodic-actions-in-python#comment26637231_8600301
[2]: http://stackoverflow.com/questions/24174924/how-to-run-a-function-periodically-in-python


Akira




More information about the Python-list mailing list