updating on spinctrl actions: how to avoid unnecessary updates

Hi,
I have a lot of spinctrls on a page and when the value of one of the
spinctrls changes, the code must do some calculations on a large
datastructure, and present the result on the same page. This works and
all spinctrl changes a bound to a method:

def OnSpinCtrl(self,event):
        ...do calculation on a lot of data and present the result..

The code works, but the problem is that the calculations take some
time. So when you e.g. change the value of a spinCtrl from 1 to 1000
by pressing the arrow on the spinctrls, 1000 times a recalculation is
done, and this slows the program unnecessary down. When I change 1 to
1000 in a spinCtrl by typing off course everything works fine.

My question: how to smartly decide when to recalculate the result?
I think I need to implement something like a timer, but I don't have a
clue how to do this?

More specific: the result of the intermediate calculations when
changing from 1 to 1000 can go into the trash bin, as long as the
interface feels responsive.

Tia Samuel

When you get the change event start the timer. When you get the next event start the timer again. This resets it to the full timeout value again, so in effect each time the spin value changes you are delaying the timer event. When there are no more spin events for N milliseconds then the timer event will happen and you can do your calculations then.

···

On 4/2/10 3:44 AM, samuel en wrote:

Hi,
I have a lot of spinctrls on a page and when the value of one of the
spinctrls changes, the code must do some calculations on a large
datastructure, and present the result on the same page. This works and
all spinctrl changes a bound to a method:

def OnSpinCtrl(self,event):
         ...do calculation on a lot of data and present the result..

The code works, but the problem is that the calculations take some
time. So when you e.g. change the value of a spinCtrl from 1 to 1000
by pressing the arrow on the spinctrls, 1000 times a recalculation is
done, and this slows the program unnecessary down. When I change 1 to
1000 in a spinCtrl by typing off course everything works fine.

My question: how to smartly decide when to recalculate the result?
I think I need to implement something like a timer, but I don't have a
clue how to do this?

--
Robin Dunn
Software Craftsman

Hi Robin,
I guess oversimplified my question unknowingly.
The calculations are done in another class (A) who creates the panel
with all the spinctrls (SPIN).
So previously the code was in SPIN:
def OnSpinCtrl(self,event):
          self.updateAenSItems()
          event.Skip()
def GetSpinCtrlID(self): return ID_SpinCtrl
In A there was a binding like:
self.Bind(wx.EVT_SPINCTRL,
self.DoTheCalculations,id=self.SPIN.GetSpinCtrlID())
This worked. De methods in A did the calculations.

Now if have in SPIN:
self.timer = wx.Timer(self, id = ID_Timer)
self.Bind(wx.EVT_TIMER, self.OnTimer, id=ID_Timer)
and:
def OnTimer(self,event):
        event.Skip()
        self.timer.Stop()
and I start the timer in OnSpinCtrl with
self.timer.Start(10,oneShot=False)

In A I now have a binding like:
self.Bind(wx.EVT_TIMER, self.OnTimer)
and in self.OnTimer I call self.DoTheCalculation.

However the code nevers propagates to self.OnTimer in class A.
Self.OnTimer in SPIN is indeed called, but the event.Skip() there
doesn't trigger anything as far as I can see.

Samuel

···

When you get the change event start the timer. When you get the next
event start the timer again. This resets it to the full timeout value
again, so in effect each time the spin value changes you are delaying
the timer event. When there are no more spin events for N milliseconds
then the timer event will happen and you can do your calculations then.

--
Robin Dunn
Software Craftsmanhttp://wxPython.org

I'm not really following this at all, but I think you said you had
separated out the logic such that you need inter-class communication.
If so, then try pubsub out. I use pubsub all the time to tell one
frame or panel to do something from another panel or frame class.

···

On Apr 4, 4:04 am, samuel en <samuel110...@gmail.com> wrote:

Hi Robin,
I guess oversimplified my question unknowingly.
The calculations are done in another class (A) who creates the panel
with all the spinctrls (SPIN).
So previously the code was in SPIN:
def OnSpinCtrl(self,event):
self.updateAenSItems()
event.Skip()
def GetSpinCtrlID(self): return ID_SpinCtrl
In A there was a binding like:
self.Bind(wx.EVT_SPINCTRL,
self.DoTheCalculations,id=self.SPIN.GetSpinCtrlID())
This worked. De methods in A did the calculations.

Now if have in SPIN:
self.timer = wx.Timer(self, id = ID_Timer)
self.Bind(wx.EVT_TIMER, self.OnTimer, id=ID_Timer)
and:
def OnTimer(self,event):
event.Skip()
self.timer.Stop()
and I start the timer in OnSpinCtrl with
self.timer.Start(10,oneShot=False)

In A I now have a binding like:
self.Bind(wx.EVT_TIMER, self.OnTimer)
and in self.OnTimer I call self.DoTheCalculation.

However the code nevers propagates to self.OnTimer in class A.
Self.OnTimer in SPIN is indeed called, but the event.Skip() there
doesn't trigger anything as far as I can see.

Samuel

-------------------
Mike Driscoll

Blog: http://blog.pythonlibrary.org

wx.TimerEvent is not a command event, so it does not propagate up the containment hierarchy. (See self.Bind vs. self.button.Bind - wxPyWiki)

So you'll either need to do your work from the first method that catches the event, or at least do something there that communicates the need to do the work to the object that will be doing it. (Directly call a method of that object, pass it a message using pubsub like Mike suggested, make your own custom event and send that, make your binding with a method of that object, etc.)

···

On 4/4/10 2:04 AM, samuel en wrote:

However the code nevers propagates to self.OnTimer in class A.
Self.OnTimer in SPIN is indeed called, but the event.Skip() there
doesn't trigger anything as far as I can see.

--
Robin Dunn
Software Craftsman

Thanks Mike and Robin for your replies.

I have looked into pubsub, and it feels/sounds like a sort of
intermodule/program dynamic data exchange.
I am pretty sure that this would solve my (above) question, but I have
some concerns about using it, that is as a newbie.
I am trying to write my Python code in an object orientated style, and
pubsub feels like "cheating",
in the sense that a mal written application in the OOP sense still
would be able to function perfectly, surpassing all the class stuff by
exchanging pubsub messages.
Am I completely missing the point (no need to say: I am new at Python)
or is using pubsub, not only pragmatic, but also a good OOP
programming practise (or just a convenient way around OOP).

Additional question: how can I trigger something like a push button
event, such that this will be regarded as a command event which
propagated (e.g. I have a button bind to OnButton and there I do a
event.Skip(), now I would like the code to simulate the button
press... and therefore propagate this commandevent to the
parentclass).

Tia Samuel

e doing it. (Directly call a

···

method of that object, pass it a message using pubsub like Mike
suggested, make your own custom event and send that, make your binding
with a method of that object, etc.)

--
Robin Dunn
Software Craftsmanhttp://wxPython.org

Thanks Mike and Robin for your replies.

I have looked into pubsub, and it feels/sounds like a sort of
intermodule/program dynamic data exchange.
I am pretty sure that this would solve my (above) question, but I have
some concerns about using it, that is as a newbie.
I am trying to write my Python code in an object orientated style, and
pubsub feels like "cheating",
in the sense that a mal written application in the OOP sense still
would be able to function perfectly, surpassing all the class stuff by
exchanging pubsub messages.
Am I completely missing the point (no need to say: I am new at Python)
or is using pubsub, not only pragmatic, but also a good OOP
programming practise (or just a convenient way around OOP).

Publish/Subscibe is a well known standard OOP pattern. It's sometimes called the Observer pattern (there are slight differences, but they're basically the same thing.)

Additional question: how can I trigger something like a push button
event, such that this will be regarded as a command event which
propagated (e.g. I have a button bind to OnButton and there I do a
event.Skip(), now I would like the code to simulate the button
press... and therefore propagate this commandevent to the
parentclass).

The event object delivered for a EVT_BUTTON event is a wx.CommandEvent, so calling Skip() should already allow it to propagate. If you want to send a new button event, or send it to a different window hierarchy, you can do it like this:

  event = wx.CommandEvent(wx.EVT_BUTTON.typeId, source.GetId())
  event.SetEventObject(source)
  source.GetEventHandler().ProcessEvent(event)

Where source is the button that the event is coming from. (You can use a different window object in the last line if you want to send the event someplace else besides the hierarchy that the button is in.)

···

On 4/7/10 11:29 AM, samuel en wrote:

--
Robin Dunn
Software Craftsman

--
To unsubscribe, send email to wxPython-users+unsubscribe@googlegroups.com
or visit http://groups.google.com/group/wxPython-users?hl=en

This message is just to say thanks to Mike and Robin and to inform
other newbies with the same problem the proposed solution (using a
timer and using pubsub) works absolutely perfectly.
Samuel

Hi, happy as I am with the pubsub solution, I also want to be able to
simulate a mouse click on a toolbar button:

If you want to send a new button event, or send it to a different window hierarchy, you

can do it like this:

       event = wx.CommandEvent(wx.EVT_BUTTON.typeId, source.GetId())
       event.SetEventObject(source)
       source.GetEventHandler().ProcessEvent(event)

Where source is the button that the event is coming from. (You can use
a different window object in the last line if you want to send the event
someplace else besides the hierarchy that the button is in.)

I am having trouble understanding this code and I spend some time
googling to get it working without any luck.

This is the situation:
I have a class : class GenericCtrl(wx.Panel):
In this class I create a toolbar like bar = wx.ToolBar(self, -1,
wx.DefaultPosition, wx.DefaultSize,...
and I add some buttons with:
bar.AddLabelTool(ID_ResultCtrl_01,"&Right", symbol).

Now I want to be able to call a method PressButton(self) in
GenericCtrl that will simulate the user clicking on the tool
ID_ResultCtrl.

Not stopped by lack of knowledge, I optimistically wrote:
    def clickButton(self):
        event = wx.CommandEvent(wx.EVT_BUTTON.typeId,
ID_ResultCtrl_01)
        event.SetEventObject(self)
        self.GetEventHandler().ProcessEvent(event)
and still gave no errors, but also no visible results.
I tried numerous variants.....-> all giving errors.

So when the correct syntax is:
        event = wx.CommandEvent(wx.EVT_BUTTON.typeId, source.GetId())
        event.SetEventObject(source)
        source.GetEventHandler().ProcessEvent(event)
What should be the source then? The panel (self) containing the
toolbar, the toolbar?

Please give some help.
Tia Samuel

I recently saw mention of a wx package that simulates window clicks in wx (there are also generic GUI stimulation tools like autoclick and the like, for regression testing, but this one was specific to wxPython). Anyone have more info?

Oliver

For toolbar button events it would be wx.EVT_TOOL.typeId, and you should use the toolbar in place of self in the last two lines.

···

On 4/14/10 4:40 AM, samuel en wrote:

Hi, happy as I am with the pubsub solution, I also want to be able to
simulate a mouse click on a toolbar button:

If you want to send a new button event, or send it to a different window hierarchy, you

can do it like this:

        event = wx.CommandEvent(wx.EVT_BUTTON.typeId, source.GetId())
        event.SetEventObject(source)
        source.GetEventHandler().ProcessEvent(event)

Where source is the button that the event is coming from. (You can use
a different window object in the last line if you want to send the event
someplace else besides the hierarchy that the button is in.)

I am having trouble understanding this code and I spend some time
googling to get it working without any luck.

This is the situation:
I have a class : class GenericCtrl(wx.Panel):
In this class I create a toolbar like bar = wx.ToolBar(self, -1,
wx.DefaultPosition, wx.DefaultSize,...
and I add some buttons with:
bar.AddLabelTool(ID_ResultCtrl_01,"&Right", symbol).

Now I want to be able to call a method PressButton(self) in
GenericCtrl that will simulate the user clicking on the tool
ID_ResultCtrl.

Not stopped by lack of knowledge, I optimistically wrote:
     def clickButton(self):
         event = wx.CommandEvent(wx.EVT_BUTTON.typeId,
ID_ResultCtrl_01)
         event.SetEventObject(self)
         self.GetEventHandler().ProcessEvent(event)
and still gave no errors, but also no visible results.
I tried numerous variants.....-> all giving errors.

--
Robin Dunn
Software Craftsman

It will be in 2.9.1, and looks like this:

http://trac.wxwidgets.org/browser/wxWidgets/trunk/include/wx/uiaction.h

Here is the beginnings of a demo for wxPython:

http://trac.wxwidgets.org/browser/wxPython/trunk/demo/UIActionSimulator.py

There is currently a problem in that it is expecting native keycodes instead of what wx would normally give you in a wx.KeyEvent, but other that that it seems to be working well.

···

On 4/14/10 5:03 AM, oliver wrote:

I recently saw mention of a wx package that simulates window clicks in
wx (there are also generic GUI stimulation tools like autoclick and the
like, for regression testing, but this one was specific to wxPython).
Anyone have more info?

--
Robin Dunn
Software Craftsman

Thanks again Robin,

For toolbar button events it would be wx.EVT_TOOL.typeId, and you should
use the toolbar in place of self in the last two lines.

In case other newbies are going to the same struggle, the following
codes works:
class GenericCtrl(wx.Panel):
      self.bar = wx.ToolBar(self, -1, wx.DefaultPosition,
wx.DefaultSize,wx.TB_NODIVIDER )
      self.bar.AddLabelTool(ID_ResultCtrl_01, et cetera..........
[please fill in the details].....

    def clickButton(self, id):
        event = wx.CommandEvent(wx.EVT_TOOL.typeId, ID_ResultCtrl_01)
        event.SetEventObject(self.bar)
        self.bar.GetEventHandler().ProcessEvent(event)