Frame events are triggered multiple times

Hi all,

when I bound my own methods to the wx.EVT_SIZE and wx.EVT_MOVE events
of a frame, I noticed that these events are triggered multiple times
for a single change. How can I avoid this, so that my method is
executed only once each time a user resizes (moves) a window? Below is
a minimal exmaple.

Thanks for your help,

Johann

MInimal example:

···

-------------------------------------------------------------
import wx

class MyApp(wx.App):
    def OnInit(self):
        Frame = wx.Frame(None)
        Frame.Bind(wx.EVT_SIZE, self.OnSize)
        Frame.Bind(wx.EVT_MOVE, self.OnMove)
        Frame.Show()
        return True

    def OnSize(self, event):
      print 'Size event, new size:' + str(event.GetSize())
      event.Skip()

    def OnMove(self, event):
      print 'Move event, new pos:' + str(event.Position)
      event.Skip()

def main():
    app = MyApp(0)
    app.MainLoop()

if __name__ == '__main__':
    main()

-------------------------------------------------------------
Sample output on my computer (Ubuntu 11.04, latest stable wxpython
2.8.12 from the wx APT repository). When the frame was generated, the
first series of events was triggered. I then resized the window on the
desktop oncem which triggered the last five events.

Size event, new size:(400, 250)
Size event, new size:(400, 250)
Move event, new pos:(1197, 779)
Move event, new pos:(1197, 779)
Move event, new pos:(1197, 779)
Move event, new pos:(1197, 779)
Move event, new pos:(1197, 779)
Move event, new pos:(1197, 779)
Move event, new pos:(1197, 779)
Move event, new pos:(1197, 779)
Move event, new pos:(1152, 727)
Size event, new size:(445, 302)
Size event, new size:(445, 302)
Move event, new pos:(1152, 727)
Move event, new pos:(1152, 727)
Move event, new pos:(1152, 727)
Move event, new pos:(1152, 727)

If the goal is to keep them for a further reopening same place same
size the best is not to use these and overload the Close method.

···

On Fri, 9 Dec 2011 12:14:04 -0800 (PST) Johann Bauer <jbauer-news@web.de> wrote:

when I bound my own methods to the wx.EVT_SIZE and wx.EVT_MOVE events
of a frame, I noticed that these events are triggered multiple times
for a single change. How can I avoid this, so that my method is
executed only once each time a user resizes (moves) a window?

--
Q: How do you make an elephant float?
A: You get two scoops of elephant and some rootbeer...

Hi Jean-Yves,

If the goal is to keep them for a further reopening same place same
size the best is not to use these and overload the Close method.

My goal is to display a Matplotlib figure whose axes must be adapted
in a nontrivial way when the aspect ratio of the window changes.
Hence, I really need to process the size events, and I want to do this
with as little overhead as possible. I could of course store the old
size and check whether the size has changed. This seems a hack,
however, and I feel that I am doing something wrong here. Is there a
way to bind to a SIZE event so that it is triggered only once for each
size change?

Best, Johann

Nope.

A test shows that both events are called twice @ start, but after
that the MOVE event is only called once and SIZE's called twice,
so perhaps you can cope with the double start.

About the SIZE event run twice, I'm not a super programmer but may be
you could use a static var as a counter to only process SIZE events
once; but I'm not sure this is faster than comparing tuples.

···

On Fri, 9 Dec 2011 12:58:41 -0800 (PST) Johann Bauer <jbauer-news@web.de> wrote:

My goal is to display a Matplotlib figure whose axes must be adapted
in a nontrivial way when the aspect ratio of the window changes.
Hence, I really need to process the size events, and I want to do this
with as little overhead as possible. I could of course store the old
size and check whether the size has changed. This seems a hack,
however, and I feel that I am doing something wrong here. Is there a
way to bind to a SIZE event so that it is triggered only once for each
size change?

--
Check here if tax deductible.

I ran the code on:
Windows XP SP3 32Bit
Python 2.7 (r27:82525, Jul 4 2010, 09:01:59) [MSC v.1500 32 bit
(Intel)] on win32
wx.version() '2.8.11.0 (msw-unicode)'

No duplicates were printed for my test of your code example. So
obviously it is a version/platform issue. You may have already known
that. I wanted to see if it was some strange artifact of using Bind()
in an wxApp object or some varible name strangeness. Nope.

You can try a couple of things. Whenever I get multiple events or
events sent multiple times it is because I have the
"widget.Bind(EVT_sometype, OnHandler)" code in mulitple places. Your
use of the variable "Frame" instead of "frame" makes me think you're
using a class-factory function. If so... (left hanging intentionally).

Also, in your event handlers you can test if the event object, id,
source objects are the same identity, using keywork "is" perhaps. I'm
curious about the nix response to that. Could you run your modified
code and let us know if the event object, event id and event source
reports to be different. It should report same for source and id both
when you move the window.

It's almost like you have created two(or more) top level windows from
your "Frame" class and they are both/all bound to the same instance
event handler - meaning the position attribute in the event would be
moving both/all windows of the same size to the same spot on the
screen, effectively making it look like one window, but each one
executing the stdout print() statements.

# [wxPython-users] Frame events are triggered multiple times
# https://mail.google.com/mail/?tab=wm#inbox/1342479e51932d4b

import wx

class MyApp(wx.App):
   def OnInit(self):
       self.Frame = wx.Frame(None)
       self.Frame.Bind(wx.EVT_SIZE, self.OnSize)
       self.Frame.Bind(wx.EVT_MOVE, self.OnMove)
       self.Frame.lastmoveevent = None
       self.Frame.lastmoveevent_obj = None
       self.Frame.lastmoveevent_id = None
       self.Frame.Show()
       return True

   def OnSize(self, event):
     print 'Size event, new size:' + str(event.GetSize())
     event.Skip()

   def OnMove(self, event):
     print 'Move event, new pos:' + str(event.Position)
     if self.Frame.lastmoveevent is not None:
        if event is self.Frame.lastmoveevent:
           print 'Same event object sent twice.'
        else:
           print 'event is a non-copy of previously handled OnMove call.'

        if event.GetEventObject() == self.Frame.lastmoveevent_obj:
           print 'Source of Duplicate-Event is from same as widget object.'
        else:
           print 'Source objects of Multiple calls to OnMove() are different.'

        if event.GetId() == self.Frame.lastmoveevent_id:
           print 'Source of Duplicate-Event is from same as ID.'
        else:
           print 'Source Event IDs of Multiple calls to OnMove() are different.'

     # remember
     self.Frame.lastmoveevent = event
     self.Frame.lastmoveevent_id = event.GetId()
     self.Frame.lastmoveevent_obj = event.GetEventObject()

     # allows event to be propogated up to parent and eventually app object
     event.Skip()

def main():
   app = MyApp(0)
   app.MainLoop()

if __name__ == '__main__':
   main()

Debian Linux, Python V. 2.7.2+, wx V. 2.8.12.1-5

Your code don't work here: it throw an exception as:
Traceback (most recent call last):
  File "test_sèpukoi.py", line 5, in <module>
    class MyApp(wx.App):
  File "test_sèpukoi.py", line 39, in MyApp
    self.Frame.lastmoveevent = event
NameError: name 'event' is not defined

···

On Wed, 14 Dec 2011 22:10:00 -0500 Dev Player <devplayer@gmail.com> wrote:

I ran the code on:
Windows XP SP3 32Bit
Python 2.7 (r27:82525, Jul 4 2010, 09:01:59) [MSC v.1500 32 bit
(Intel)] on win32
wx.version() '2.8.11.0 (msw-unicode)'

--
BOFH excuse #433:
error: one bad user found in front of screen

# Issue caused by copy-and-paste from from web post which lost proper
indentation:

def OnMove(self, event):
    # last 4 lines of method should have same indent
    # as line 23
    if self.Frame.lastmoveevent is not None:

    # ...
    # OP's code had non-standard but legal indents;
    # i used his indents
    # remember
    self.Frame.lastmoveevent = event # line 39
    self.Frame.lastmoveevent_id = event.GetId()
    self.Frame.lastmoveevent_obj = event.GetEventObject()
    event.Skip()

May be some' like that?

JY

test_display_only_one_resize_one_move.py (2.37 KB)

···

On Thu, 15 Dec 2011 13:36:48 -0500 Dev Player <devplayer@gmail.com> wrote:

--
Shhh... be vewy, vewy, quiet! I'm hunting wabbits...

I suspect you should flatten your conditionals (the if statements).

Remember the OnMove and OnSize handlers are being called repeatedly.
The GetSize() may return the show the same size for each call to
OnSize(), but the source object that is calling the handler may be
different.

For example if you resize your frame and bind your buttons, panels,
etc. to receive the frame's resize event then the buttons and panels
and all could very well propogate the event back up to the frame. And
you'll see a bunch of the same events. It's just that the source
object OR the source ID could be different.

But with your example code you only test for source object or source
ID if the SIZE (or the position) has changed. We don't care about size
or position in this debug test. We care to find the source objects and
source IDs. AND if you have the same BIND(EVT_SOME_EVENT, MyHandler)
in several places it could create several different event objects with
the same source object and source ID. So I wanted to test for identity
(id()) uniqueness there too.

I would recommend that you do not put your "ifs" in a nested fastion
for better test.

The only one-deep nesting my test app does was on the initial run
where all those "remembering" attributes where initially set to
"None". I didn't want to confuse the output with initial settings, as
they are "None".

edit...

... So I wanted to test for identity (id()) uniqueness there too

+++ by checking if the event is the same event as before. I am not saying
if the event was the same type of event, I am saying if the event object is the
same. (although that really shouldn't work, not withstanding
event.Clone() either)

if id(old_event) == id(event):
   # bla bla
# OR PUT another way:
if old_event is event:
   # bla bla
#...
old_event = event

···

On Sat, Dec 17, 2011 at 9:52 PM, Dev Player <devplayer@gmail.com> wrote:

Oh forgot to mention:

The "else: print '...'" clauses in the debug sample where very
important. You did not have these. You were looking for sameness or
equivelence. I was looking for not-sameness and non-equivelence. I am
suspecting and hoping you will see the not-sameness and
non-equivelance with properly constructed "IF"s. It is the "else"
print outs I'm hoping to hear from you about. If the "else" blocks
that you see in my debugsample.py do not execute then I don't know
where to look for your issue.

btw you could just change:

if self.Frame.newSize != self.Frame.lastResizeEvent_size:
to
if self.Frame.newSize == self.Frame.lastResizeEvent_size and
self.Frame.lastResizeEvent_size is not None:

and
if self.Frame.newPos != self.Frame.lastMoveEvent_pos and
self.Frame.lastMoveEvent_pos is not None:
to:
if self.Frame.newPos == self.Frame.lastMoveEvent_pos and
self.Frame.lastMoveEvent_pos is not None:

would be a good start. Note the != and ==. This would be better:

if self.Frame.lastResizeEvent_size is not None and self.Frame.newSize
== self.Frame.lastResizeEvent_size:
and
if self.Frame.lastMoveEvent_pos is not None and self.Frame.newPos ==
self.Frame.lastMoveEvent_pos:

Personally I'd replace "self.Frame" with "self.frame" module wide.

Old habits: I try to never use composed statements as they take
more cycles to process.

···

On Sat, 17 Dec 2011 22:11:08 -0500 Dev Player <devplayer@gmail.com> wrote:

if self.Frame.newSize != self.Frame.lastResizeEvent_size:
to
if self.Frame.newSize == self.Frame.lastResizeEvent_size and
self.Frame.lastResizeEvent_size is not None:

--
"Plaese porrf raed."
    -- Prof. Michael O'Longhlin, S.U.N.Y. Purchase

Jean-Yves F. Barbier wrote:

···

Dev Player <devplayer@gmail.com> wrote:

if self.Frame.newSize != self.Frame.lastResizeEvent_size:
to
if self.Frame.newSize == self.Frame.lastResizeEvent_size and
self.Frame.lastResizeEvent_size is not None:

Old habits: I try to never use composed statements as they take
more cycles to process.

I can't let that statement go unchallenged. This seems like a classic
example of misguided micro-optimization. What do you mean by "composed
statements", and "more cycles" than what? In Python, in virtually every
case, you should write code for readability, and worry about
optimization only when you have a provable problem.

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Not only Python, the advice is applied much more generally. There are
so many stories of people trying to reduce their code's execution time
by a few cycles here and there, while elsewhere they have code that
takes actual noticeable (by a human) time to run. I'd like to see a
benchmark comparing two consecutive "if" statements with a single
statement containing both conditions. I bet the difference is non-
existent (either practically or literally).

···

On Dec 19, 6:17 pm, Tim Roberts <t...@probo.com> wrote:

I can't let that statement go unchallenged. This seems like a classic
example of misguided micro-optimization. What do you mean by "composed
statements", and "more cycles" than what? In Python, in virtually every
case, you should write code for readability, and worry about
optimization only when you have a provable problem.