Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #80320 > unrolled thread

Moving a window on the screen

Started by"ast" <nomail@invalid.com>
First post2014-11-06 09:32 +0100
Last post2014-11-06 04:25 -0500
Articles 9 — 5 participants

Back to article view | Back to comp.lang.python


Contents

  Moving a window on the screen "ast" <nomail@invalid.com> - 2014-11-06 09:32 +0100
    Re: Moving a window on the screen Chris Angelico <rosuav@gmail.com> - 2014-11-06 19:57 +1100
      Re: Moving a window on the screen "ast" <nomail@invalid.com> - 2014-11-06 10:18 +0100
        Re: Moving a window on the screen Akira Li <4kir4.1i@gmail.com> - 2014-11-08 19:35 +0300
        Re: Moving a window on the screen Terry Reedy <tjreedy@udel.edu> - 2014-11-08 13:40 -0500
        Re: Moving a window on the screen Akira Li <4kir4.1i@gmail.com> - 2014-11-08 23:31 +0300
        Re: Moving a window on the screen Terry Reedy <tjreedy@udel.edu> - 2014-11-12 15:06 -0500
    Re: Moving a window on the screen Peter Otten <__peter__@web.de> - 2014-11-06 10:15 +0100
    Re: Moving a window on the screen Terry Reedy <tjreedy@udel.edu> - 2014-11-06 04:25 -0500

#80320 — Moving a window on the screen

From"ast" <nomail@invalid.com>
Date2014-11-06 09:32 +0100
SubjectMoving a window on the screen
Message-ID<545b321a$0$2396$426a34cc@news.free.fr>
Hi

Why the following program doesn't work ?

for x in range(0, 100, 10):
    fen.geometry("200x200+%d+10"   %   x)
    time.sleep(0.5)


where fen is a window (fen = Tk())

The fen window goes from it's initial location to the last one
but we dont see all the intermediate steps


thx

[toc] | [next] | [standalone]


#80322

FromChris Angelico <rosuav@gmail.com>
Date2014-11-06 19:57 +1100
Message-ID<mailman.15536.1415264262.18130.python-list@python.org>
In reply to#80320
On Thu, Nov 6, 2014 at 7:32 PM, ast <nomail@invalid.com> wrote:
> The fen window goes from it's initial location to the last one
> but we dont see all the intermediate steps

You usually don't want to use time.sleep() in a GUI program. Try doing
the same thing, but with an event loop delay call instead; often, the
display won't update until you go back to the main loop.

ChrisA

[toc] | [prev] | [next] | [standalone]


#80324

From"ast" <nomail@invalid.com>
Date2014-11-06 10:18 +0100
Message-ID<545b3cd8$0$2306$426a34cc@news.free.fr>
In reply to#80322
"Chris Angelico" <rosuav@gmail.com> a écrit dans le message de 
news:mailman.15536.1415264262.18130.python-list@python.org...

> You usually don't want to use time.sleep() in a GUI program. Try doing
> the same thing, but with an event loop delay call instead; often, the
> display won't update until you go back to the main loop.


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() 

[toc] | [prev] | [next] | [standalone]


#80399

FromAkira Li <4kir4.1i@gmail.com>
Date2014-11-08 19:35 +0300
Message-ID<mailman.15582.1415464608.18130.python-list@python.org>
In reply to#80324
"ast" <nomail@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], 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]:

  #!/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,
                 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,
             move, delta_x, root.winfo_screenwidth())
  root.mainloop()
  

[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

[toc] | [prev] | [next] | [standalone]


#80400

FromTerry Reedy <tjreedy@udel.edu>
Date2014-11-08 13:40 -0500
Message-ID<mailman.15583.1415472071.18130.python-list@python.org>
In reply to#80324
On 11/8/2014 11:35 AM, Akira Li wrote:
> "ast" <nomail@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

> 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

>    #!/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.

>                   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.

>               move, delta_x, root.winfo_screenwidth())
>    root.mainloop()
>
>
> [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
>


-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#80403

FromAkira Li <4kir4.1i@gmail.com>
Date2014-11-08 23:31 +0300
Message-ID<mailman.15585.1415478753.18130.python-list@python.org>
In reply to#80324
Terry Reedy <tjreedy@udel.edu> writes:

> On 11/8/2014 11:35 AM, Akira Li wrote:
>> "ast" <nomail@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

[toc] | [prev] | [next] | [standalone]


#80623

FromTerry Reedy <tjreedy@udel.edu>
Date2014-11-12 15:06 -0500
Message-ID<mailman.15738.1415822826.18130.python-list@python.org>
In reply to#80324
On 11/8/2014 3:31 PM, Akira Li wrote:
> Terry Reedy <tjreedy@udel.edu> writes:

>> On my Win7 machine, your complicated code is much worse as it causes
>> the window to jump about every half second

After cutting and pasting again, I do not see the jumps. I do not have 
the code I ran before to compare.  I do remember that I deleted, added 
back, and re-deleted the phase correction, and ran a couple of times 
each, to be sure that the aberrant behavior was reproducible in general 
effect.

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#80323

FromPeter Otten <__peter__@web.de>
Date2014-11-06 10:15 +0100
Message-ID<mailman.15537.1415265339.18130.python-list@python.org>
In reply to#80320
ast wrote:

> Why the following program doesn't work ?
> 
> for x in range(0, 100, 10):
>     fen.geometry("200x200+%d+10"   %   x)
>     time.sleep(0.5)
> 
> 
> where fen is a window (fen = Tk())
> 
> The fen window goes from it's initial location to the last one
> but we dont see all the intermediate steps

As Chris says, using sleep() with a gui program usually doesn't work. While 
the script is sleeping the event loop doesn't run and consequently the user 
will not see any updates of the program's state.

Instead of sleeping you have to schedule events that trigger a callback and 
then go back to the event loop. Such a callback may schedule another 
invocation of itself, thus giving you the desired effect:

import tkinter as tk

root = tk.Tk()
xpositions = iter(range(0, 100, 10))

def move_next():
    try:
        x = next(xpositions)
    except StopIteration:
        pass
    else:
        root.geometry("200x200+%d+10" %  x)
        root.after(500, move_next) # have tkinter call move_next again
                                   # in about 500 milliseconds

root.after_idle(move_next)
root.mainloop()

[toc] | [prev] | [next] | [standalone]


#80325

FromTerry Reedy <tjreedy@udel.edu>
Date2014-11-06 04:25 -0500
Message-ID<mailman.15538.1415265932.18130.python-list@python.org>
In reply to#80320
On 11/6/2014 3:57 AM, Chris Angelico wrote:
> On Thu, Nov 6, 2014 at 7:32 PM, ast <nomail@invalid.com> wrote:
>> The fen window goes from it's initial location to the last one
>> but we dont see all the intermediate steps
>
> You usually don't want to use time.sleep() in a GUI program. Try doing
> the same thing, but with an event loop delay call instead; often, the
> display won't update until you go back to the main loop.

import tkinter as tk
root = tk.Tk()

x = 0
def march():
     global x
     print(x)
     root.geometry("200x200+%d+%d" % (x,x))
     x += 10
     if x < 200:
         root.after(300, march)

march()
root.mainloop()

-- 
Terry Jan Reedy

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web