SetValue on spinctrl doesn't send update event?

4 views
Skip to first unread message

Jim Ursetto

unread,
Feb 18, 2003, 4:35:30 AM2/18/03
to wxpytho...@lists.wxwindows.org
Hi,

I'd like to display text in a text box based on the value of a spin
control. Using EVT_SPINCTRL, I hooked up a method to update the
text box. This works fine when the user clicks on the spin arrows
or enters text directly. However, the event does not fire when
I call SetValue on the spin control within the program.

What's the normal way to handle this? Am I missing something? I don't
want to update the text box manually after a SetValue, because that
would result in duplicated code. I'd rather let the event handler
handle all updates; that's how I typically do it in GTK.

I thought about subclassing the spin control, and overriding the
SetValue method and having it send out a signal, but this seems like
overkill. I'm hoping there's a better solution.

Jim

--
j...@3e8.org / 0x43340710 / 517B C658 D2CB 260D 3E1F 5ED1 6DB3 FBB9 4334 0710

Jeff Shannon

unread,
Feb 18, 2003, 12:58:10 PM2/18/03
to wxPytho...@lists.wxwindows.org

Jim Ursetto wrote:

>Hi,
>
>I'd like to display text in a text box based on the value of a spin
>control. Using EVT_SPINCTRL, I hooked up a method to update the
>text box. This works fine when the user clicks on the spin arrows
>or enters text directly. However, the event does not fire when
>I call SetValue on the spin control within the program.
>
>

The simplest way that I see doing this, is to put all of your
text-box-update code in a single parameter-less function/method, and
have EVT_SPINCTRL call that method. This may be the only thing that the
event handler does. The update method can draw all the information that
it needs directly from the spin control, so it's really just a way of
nudging the text box to say "do your thing". Now, when you call
SetValue() on your spin control, you immediately follow that with a call
to this update method.

The next option would be to manually construct an EVT_SPINCTRL event
object and pass it to the appropriate event handler, but this would be a
lot more work (and slower, and not as clear).

A third option would be to create a custom composite control containing
both the spin control and the text box. Most of the composite control's
events would be passed directly to the contained controls, but (for
example) SetValue() would update both controls. This would probably be
easiest if built on top of the first option, actually -- so that
composite.SetValue() simply calls spinctrl.SetValue() and then
text.Update(). (This is similar to your idea of subclassing SpinCtrl,
but is a bit more versatile.)

Option three is probably worth the extra effort if you're going to be
using this control setup extensively (or widely), especially if it's
going to be used by multiple developers. If it's a single-use item,
though, then you're probably best off going with option one.

Jeff Shannon
Technician/Programmer
Credit International

Robin Dunn

unread,
Feb 18, 2003, 7:40:41 PM2/18/03
to wxPytho...@lists.wxwindows.org
Jim Ursetto wrote:
> Hi,
>
> I'd like to display text in a text box based on the value of a spin
> control. Using EVT_SPINCTRL, I hooked up a method to update the
> text box. This works fine when the user clicks on the spin arrows
> or enters text directly. However, the event does not fire when
> I call SetValue on the spin control within the program.
>
> What's the normal way to handle this? Am I missing something? I don't
> want to update the text box manually after a SetValue, because that
> would result in duplicated code. I'd rather let the event handler
> handle all updates; that's how I typically do it in GTK.

It's not always successful because of platform differences, but the
general policy is that changes to a control's value made by the user
generates events and changes made programatically do not. So you should
plan on SetValue not sending the event, (but also be aware that
sometimes it may and act accordingly.)

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Jim Ursetto

unread,
Feb 19, 2003, 10:46:17 AM2/19/03
to wxpytho...@lists.wxwindows.org
Jeff Shannon did write:

> Jim Ursetto wrote:
> >However, the event does not fire when I call SetValue on the spin
> >control within the program.

> The simplest way that I see doing this, is to put all of your
> text-box-update code in a single parameter-less function/method, and
> have EVT_SPINCTRL call that method.

This occurred to me as well, and is probably how I'll do it.
But see below.

> A third option would be to create a custom composite control containing
> both the spin control and the text box.

This is very interesting, and I hadn't thought of it. The problem is
that the spin/text box thing is just one example. For example, if I
have two windows, and a spin button in one needs to update a control in
the other, I don't think method #3 (composite control) will work.
Method #1--calling the text box update manually after a SetValue--would
still work in this case. But I want access to the spin button to be
transparent. This is especially important when another class has
programmatic access; it shouldn't have to change its behavior when I
connect a widget to the spin.

Thus, my suggestion to subclass the spin control, or at the least
having the frame or window class that contains it provide a method
to set its value. Outsiders would only deal with this method rather
than with the control itself. Of course, the problem with -this-
approach is that I have to do it to every widget I want this
behavior from.

In GTK, setting a value on a widget does send an event, consistently, so
I don't have to worry about this at all. So, I figured I was taking the
wrong approach in wxWindows, instead of the standard, idiomatic way.

I'd like to know what others have done in this situation. Do you guys
think I should ask on the wx-users list? Is there any code I can look
at? So far the closest thing I've found is in the wxPython demo.py; but
it was with the wxIntCtrl and wxTimeCtrl widgets, which actually do
manually generate and send their own update events after you set their
values programmatically. Just as in your suggested method #2.

Apologies for the long post, but I'm very curious.

--
"But if food is so good for you, how come the body
keeps trying to get rid of it?" -- breatharian.com

Jeff Shannon

unread,
Feb 19, 2003, 4:52:42 PM2/19/03
to wxPytho...@lists.wxwindows.org

Jim Ursetto wrote:

>[...] The problem is


>that the spin/text box thing is just one example. For example, if I
>have two windows, and a spin button in one needs to update a control in
>the other, I don't think method #3 (composite control) will work.
>Method #1--calling the text box update manually after a SetValue--would
>still work in this case. But I want access to the spin button to be
>transparent. This is especially important when another class has
>programmatic access; it shouldn't have to change its behavior when I
>connect a widget to the spin.
>

Another possibility -- you might be able to set up idle processing which
checks the status of your "watched" controls (the spincontrol) and then
performs any needed updates on the slave control (textbox). Any time
your app's event queue is empty, it would then ensure that the two
controls were in sync. This could result in some minor sluggishness of
response, since the textbox won't be updated immediately, but the lag
time should be very small under most circumstances. There's a variety
of sub-options here. You can unconditionally update your textbox every
time the idle handler kicks in -- this is simplest, but results in a lot
of unnecessary updates, which could become painful if updating is a bit
slow, or if there's a lot of these control pairs. You could, then,
track your "watched" controls' contents, and only update the textbox
when those contents change. This will be more efficient at runtime, but
requires a bit more coding. And if you're *really* motivated, you can
do this as a fairly generic publish/subscribe (or Observer-like) pattern
-- create a system that will allow you to register a callback that will
be triggered when the value of a specified control changes. Then you
could, say, call Observer.Register(spinctrl.Id(), textbox.Update()),
which would add spinctrl to a list of watched controls and then call
textbox.Update() when it changes. This Observer would check each
control in its list at every idle event. You'd probably also want a way
to Unregister controls, and there's all sorts of other features that
could be added... This would be more work than just tracking one or two
controls, but would probably be simpler if the number of watched
controls is likely to be more than that (or if you'll need to do similar
things in multiple applications).

Jim Ursetto

unread,
Feb 19, 2003, 7:25:40 PM2/19/03
to wxpytho...@lists.wxwindows.org
Jeff Shannon did write:

> Another possibility -- you might be able to set up idle processing

Yeah, I monkeyed around with this stuff for a while, not satisfied.
Then I remembered Robin's reply and found that TextCtrl sends an
update event when its SetValue method is called. Why not SpinCtrl,
then? I'm sure someone knows, somewhere. So I broke down:

class jSpinCtrl(wxSpinCtrl):
def SetValue(self, val):
old = self.GetValue()
wxSpinCtrl.SetValue(self, val)
new = self.GetValue()
if new != old:
event = wxPyCommandEvent(wxEVT_COMMAND_SPINCTRL_UPDATED, self.GetId())
event.SetEventObject(self)
event.SetInt(new)
self.GetEventHandler().ProcessEvent(event)

Now my spin control sends an update event when SetValue is called (and
the old and new values differ), and the rest of the code is much cleaner.
Seems reasonable, though I only tried it on Linux so far.

Thanks for all the replies, they were enlightening.

--

Reply all
Reply to author
Forward
0 new messages