Recording the previously selected control

62 views
Skip to first unread message

Iwbnwif Yiw

unread,
Sep 25, 2017, 10:53:03 AM9/25/17
to wxPython-users
I have been programming with wxWidgets for a few years using C++, but have only just started using Python and wxPython.

I am trying to write a very simple barcode reader / stock-taking app that runs on a Raspberry Pi. I am using Python 2.7 and wxPython from the Raspbian 'Stretch' repository.

My app has a single frame with 4 wx.TextCtrl and 12 buttons arranged as a 'number pad'. 1 of the wx.TextCtrl is read-only and the other 3 should have numbers entered by the user by pressing the buttons on the 'number pad' (using a mouse or touch panel).

My approach is to capture a wx.EVT_SET_FOCUS event in each of the wx.TextCtrl and I had planned to record its Python handle/label/pointer using event.GetWindow(). Then when a number button is pressed, I would append the corresponding number to the control using something like `focusCtrl.AppendText('1')`.

However, I am still a bit confused about the Python handle / unmutable object / mutable list concept. In particular, how I can record the whatever_it_is that is returned from event.GetWindow()?

This is what I have tried:

    # In def __init__
    self
.qtyField.Bind(wx.EVT_SET_FOCUS, self.OnFocus)

    # Then ...
    def OnFocus(self, event):
        print ("Got a focus event")
        self.focusWindow = event.GetWindow()
        event.Skip()

    # And ...
    def OnOne(self, event):  # wxGlade: MyFrame.<event_handler>
        self.focusWindow.AppendText('1')
        event.Skip()

However, self.focusWindow only gets local scope (I think), so the self.focusWindow in OnOne is not the same as in OnFocus. If I want to create an object with class scope (i.e. focusWindow = wx.TextCtrl()), then it actually instantiates an extra control (as is reasonably expected).

I realise that these are basic Python questions, but I thought to ask here because of the wxPython context.

Separately, but related, I am using wxGlade to create the frame. wxGlade only has some of the events available to a control, specifically the hierarchical wx.Window events are not included. What is the best way to work with this - should I subclass the wxGlade created frame in my own file or edit the wxGlade generated "mainwindow.py"?

Finally, I miss the CodeLite working environment with code completion, debugging etc. I know this is a personal choice but any hints / tips on an IDE are much appreciated. I started with Idle, but now moved to Eric because it promised code completion - which I have failed to get working so far.

Thanks for any help!



James Scholes

unread,
Sep 25, 2017, 11:13:05 AM9/25/17
to wxpytho...@googlegroups.com
Iwbnwif Yiw wrote:
> My approach is to capture a wx.EVT_SET_FOCUS event in each of the
> wx.TextCtrl and I had planned to record its Python handle/label/pointer
> using event.GetWindow(). Then when a number button is pressed, I would
> append the corresponding number to the control using something like
> `focusCtrl.AppendText('1')`.

This approach is really over-complicated. All you need to do is to
store the wx.TextCtrl as an attribute of your instance and reference it
later.

self.aTextCtrl = wx.TextCtrl(...)

And then later, in an event handler for your keypad buttons:

self.aTextCtrl.AppendText(...)

Regards,

James Scholes
https://twitter.com/JamesScholes

Iwbnwif Yiw

unread,
Sep 25, 2017, 11:56:14 AM9/25/17
to wxPython-users
Thank you for your help.

On Monday, September 25, 2017 at 4:13:05 PM UTC+1, James Scholes wrote:

This approach is really over-complicated.  All you need to do is to
store the wx.TextCtrl as an attribute of your instance and reference it
later.

self.aTextCtrl = wx.TextCtrl(...)


Sorry, but I am still missing something. Won't the above line create another instance of a wx.TextCtrl rather than something I can access an existing control through? 

I have 3 controls, each of which needs to receive additional numbers when it has the focus.

+--------------+
| TextCtrl1   |
+--------------+
| TextCtrl2   |
+--------------+
| TextCtrl3   |
+--------------+

If the user touches TextCtrl2 and then starts pressing number buttons, the .AppendText(number) should add a number to the middle wxTextCtrl.

If they then touch TextCtrl3, any further numbers should be appended to that control.

So how do I know which instance of wx.TextCtrl to call .AppendText on?



Tim Roberts

unread,
Sep 25, 2017, 12:26:27 PM9/25/17
to wxpytho...@googlegroups.com
Iwbnwif Yiw wrote:

However, I am still a bit confused about the Python handle / unmutable object / mutable list concept. In particular, how I can record the whatever_it_is that is returned from event.GetWindow()?

If you have any C# background, the object concept in Python is closer to C# than it is to C++.

Here's the way I think about it.  You have objects, and you have namespaces.  The world of objects is just a big cloud of "things" that have a type and a value, but they have no names.

Then, you have namespaces.  The names in the namespaces refer to "things', but they don't really have any content themselves.  So, when I  say:
    i = "abc"
    j = i
    k = j
I have created exactly one object in the big nameless cloud.  It is a string object with three characters.  I happen to have three local names that refer to that object.  When I say:
    k = 3
that releases one of the references, so there are only two remaining.  The "k" name now has a reference to the integer 3 object in the big nameless cloud.  If I happen to say:
    i = 1
    j = 2
then there are NO remaining references, although there is still a string object in the nameless cloud.  During the next garbage collection cycle, it will notice that the "abc" literal string has no references, and it will delete the object.

The names aren't so important.  What's important is the objects.  If you have a button on your window, there is only one button object, although you might have many names that refer to that object.  So"
    def __init__( ... ):
        self.okButton = wx.Button( self, "OK", ... )
        self.okButton.Bind( wx.EVT_CLICK, self.onClick )
    def onClick( self, evt ):
        self.what = evt.GetWindow()
        btn = evt.GetWindow()

There is only one wx.Button object in this example, but it is referred to by many names.  By the time I get through that onClick handler, self.okButton, self.what, and the local variable "btn" all refer to that same button object.  Any actions I take through any of those names affects the object, so the changes can be seen through all of those names.  In addition, the parent object also keeps a reference, to make sure it isn't deleted.




This is what I have tried:

    # In def __init__
    self
.qtyField.Bind(wx.EVT_SET_FOCUS, self.OnFocus)

    # Then ...
    def OnFocus(self, event):
        print ("Got a focus event")
        self.focusWindow = event.GetWindow()
        event.Skip()

    # And ...
    def OnOne(self, event):  # wxGlade: MyFrame.<event_handler>
        self.focusWindow.AppendText('1')
        event.Skip()

However, self.focusWindow only gets local scope (I think), so the self.focusWindow in OnOne is not the same as in OnFocus.

That's not true.  self.focusWindow is a member variable, just like member variables in C++.  All the events for this window will get passed the same "self" object (just like "this" in  C++), so self.focusWindow will refer to whatever object tucked in there last.

James's suggestion was to skip using the focusWindow hack, and instead just refer to self.qtyField in your OnOne handler.  If you know which window fired the event, then just use your local reference.  He just happened to use a different variable name in his description.
-- 
Tim Roberts, ti...@probo.com
Providenza & Boekelheide, Inc.

Chris Barker - NOAA Federal

unread,
Sep 25, 2017, 2:21:38 PM9/25/17
to wxpytho...@googlegroups.com

Iwbnwif Yiw

unread,
Sep 25, 2017, 3:30:27 PM9/25/17
to wxPython-users


On Monday, September 25, 2017 at 5:26:27 PM UTC+1, Tim Roberts wrote:
Here's the way I think about it.  You have objects, and you have namespaces.  The world of objects is just a big cloud of "things" that have a type and a value, but they have no names.

....

Thanks, this description was very useful. 
 
However, self.focusWindow only gets local scope (I think), so the self.focusWindow in OnOne is not the same as in OnFocus.

That's not true.  self.focusWindow is a member variable, just like member variables in C++.  All the events for this window will get passed the same "self" object (just like "this" in  C++), so self.focusWindow will refer to whatever object tucked in there last.


Sorry, yes I have got this now. I was getting a "None does not have a AppendText() attribute" type error. In fact this was caused by event.GetWindow() returning None, if I replace it with event.GetEventObject(), then it works as I wanted.
 
James's suggestion was to skip using the focusWindow hack, and instead just refer to self.qtyField in your OnOne handler.  If you know which window fired the event, then just use your local reference.  He just happened to use a different variable name in his description.

The thing is the OnOne button event will always be fired by the button, it is the window focus event that will have one of the 3 TextCtrl as an object (could be qtyField, costField or sellField) - but in the OnOne event I need to remember which window previously had focus so I know which of the 3 controls I need to append text to.
 
So now, I can do exactly what I want to do with the following code:

    def OnNumber(self, event):
        if self.focusWindow is not None:
            self.focusWindow.AppendText(event.GetEventObject().GetLabel())

        event.Skip()

    def OnClear(self, event):
        if self.focusWindow is not None:
            self.focusWindow.Clear()

        event.Skip()

    def OnFocus(self, event):
        print ("Got a focus event")

        if event.GetEventObject() == self.nameField:
            self.focusWindow = None
        else:
            self.focusWindow = event.GetEventObject()

        event.Skip()


Edit: I should probably change if self.focusWindow is not None: to self.focusWindow is wx.TextCtrl.

Iwbnwif Yiw

unread,
Sep 25, 2017, 3:36:01 PM9/25/17
to wxPython-users
On Monday, September 25, 2017 at 7:21:38 PM UTC+1, Chris Barker - NOAA Federal wrote:
Thank you, yes it is! I was planning to do something like this because the wxGlade generated code was so cumbersome to work with.

Rufus Smith

unread,
Sep 25, 2017, 3:52:19 PM9/25/17
to wxpytho...@googlegroups.com
Actually, I'd be inclined to use a list of valid focus objects, as I created them, such as:

focusableWindows = [self.xvalue,self.yvalue,self.zvalue]

Then later, you can say:

if event.GetEventObject() in focusableWindows:
self.focusWindow = event.GetEventObject()

So you can always be pointing to a valid focus window, unless you really want to de-focus the pointer.





Iwbnwif Yiw

unread,
Sep 25, 2017, 4:22:14 PM9/25/17
to wxPython-users
On Monday, September 25, 2017 at 8:52:19 PM UTC+1, Rufus wrote:

Actually, I'd be inclined to use a list of valid focus objects, as I created them, such as:

focusableWindows = [self.xvalue,self.yvalue,self.zvalue]

Then later, you can say:

if event.GetEventObject() in focusableWindows:
self.focusWindow = event.GetEventObject()

So you can always be pointing to a valid focus window, unless you really want to de-focus the pointer.

Yes, that is a good idea. I can extend it to have an 'else' clause that disables the keys if a non-input window receives the focus. 

Chris Barker - NOAA Federal

unread,
Sep 26, 2017, 11:46:34 AM9/26/17
to wxpytho...@googlegroups.com
I'm on a phone, so can't really play with this, but I think you are
making this too complicated by thinking about focus, rather than which
events are being fired from where.

Perhaps a high-level description of the UI you are trying to build
would help us guide you.

But in general you want to bind event handlers to the widget they are
coming from. If a user types into an textCtrl, then that's where you
handle that event.

Focus events are a fairly rare use case -- users can switch focus
around kind of arbitrarily-- if they need to say " I'm now going to
edit this particular data" -- better to have an explicit way for them
to select that object -- check box, or???

I'm also wary of keeping collections around and searching for which
item is relevant-- generally, you can have your widgets named:

self.data1_input = something....

Or, if you have a bunch of similar ones, put them in a dict with
meaningful names.

-CHB

Rufus Smith

unread,
Sep 26, 2017, 3:16:12 PM9/26/17
to wxpytho...@googlegroups.com
The GUI the OP has built has its own "Virtual Keyboard" (calculator style) and he wants to direct
the keyed input to selected fields. A different approach may be to simply have the virtual keyboard
act like a regular keyboard, so either can be used for the data input in any field, using the built in
mechanism, rather than the way he's doing it now.

Simulating standard keyboard input from GUI events could have many benefits.

(I too, questioned using focus to do this, but instead rather highlighting the changing field with color or
a check-box as you suggest, but there'd still be the selection process, which focus seems to already
to do.)


Iwbnwif Yiw

unread,
Sep 26, 2017, 4:05:31 PM9/26/17
to wxPython-users
On Tuesday, September 26, 2017 at 8:16:12 PM UTC+1, Rufus wrote:

The GUI the OP has built has its own "Virtual Keyboard" (calculator style)  and he wants to direct
the keyed input to selected fields.  

Yes, that is exactly it.
 
A different approach may be to simply have the virtual keyboard
act like a regular keyboard, so either can be used for the data input in any field, using the built in
mechanism, rather than the way he's doing it now.

Simulating standard keyboard input from GUI events could have many benefits. 

Hmm, yes that is certainly a neat way of tackling the problem. I haven't tried this yet, but in wxWidgets C++, events propagate upwards so I am not sure whether you can wx.PostEvent to a form and have the text appear in a child window. If not, I still need to know which wx.TextCtrl has the focus so I can post the event to it?

One other slight problem... I didn't mention this, but I also have a clear button which clears all the text in the currently selected field. Again, I need to know which wx.TextCtrl to call Clear() on.
 
(I too, questioned using focus to do this, but instead rather highlighting the changing field with color or
a check-box as you suggest, but there'd still be the selection process, which focus seems to already
to do.)

I am not sure what is wrong with using focus? When a wx.TextCtrl has focus it flashes a cursor at the edit point - which to me seems a reasonable indication that that control will receive user input. 

Chris Barker

unread,
Sep 26, 2017, 5:40:57 PM9/26/17
to wxpython-users
On Tue, Sep 26, 2017 at 1:05 PM, Iwbnwif Yiw <iwb...@gmail.com> wrote:
I am not sure what is wrong with using focus? When a wx.TextCtrl has focus it flashes a cursor at the edit point - which to me seems a reasonable indication that that control will receive user input. 

sure, but:

Can the user type instead?

either way, as soon as the user clicks on a button on the "number pad" --  that ctr will lose focus.

I suspect it will be a bit confusing for the user for the input to go to the last item that happened to have focus, if, in fact, it was one of the controls that can receive input.

On the other hand, I'm not seeing it in action, maybe it's more obvious than it sounds.

But if you keep "the last control with focus" approach, you might want to have a stronger visual indicator of what the active control is -- so once someone clicks on it, it receives focus, it also get highlighted or something, and then as they go and click on the number pad the highlight remains.

-CHB




--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R            (206) 526-6959   voice
7600 Sand Point Way NE   (206) 526-6329   fax
Seattle, WA  98115       (206) 526-6317   main reception

Chris....@noaa.gov

Robin Dunn

unread,
Sep 26, 2017, 7:12:47 PM9/26/17
to wxPython-users
On Tuesday, September 26, 2017 at 1:05:31 PM UTC-7, Iwbnwif Yiw wrote:
On Tuesday, September 26, 2017 at 8:16:12 PM UTC+1, Rufus wrote:

The GUI the OP has built has its own "Virtual Keyboard" (calculator style)  and he wants to direct
the keyed input to selected fields.  

Yes, that is exactly it.
 
A different approach may be to simply have the virtual keyboard
act like a regular keyboard, so either can be used for the data input in any field, using the built in
mechanism, rather than the way he's doing it now.

Simulating standard keyboard input from GUI events could have many benefits. 

Hmm, yes that is certainly a neat way of tackling the problem. I haven't tried this yet, but in wxWidgets C++, events propagate upwards so I am not sure whether you can wx.PostEvent to a form and have the text appear in a child window.

No. Only Command events propagate, and _KEY and _CHAR events are not command events.  Even if they did propagate it would not work for two reasons. First, sending the events to the parent will not forward the event to the child as events propagate upwards not downwards, and if the child doesn't get the event then it will not be able to do anything with it. Second, sending a wx event will not be the same thing as a native widget getting a native message that it acts upon by adding a character to the control or whatever. wx events are only sent to the wx layers of the software stack, it is only when wx is translating a native message to an event that it can affect things, and that is only to the point of telling the native control whether the event has been handled in the wx layers or not.

Something I haven't seen mentioned yet is the wx.UIActionSimulator.  It's not perfect but it might be enough for what you need. You will still need to ensure the focus is in the right place however before the simulator will be able to stuff keystrokes into the message queue, so you'll still need to keep track of the last focused editable widget, because as Chris said, clicking on the buttons for your simulated keyboard will move the focus to the button.

 -- 
Robin Dunn
Software Craftsman

Iwbnwif Yiw

unread,
Sep 27, 2017, 4:33:11 AM9/27/17
to wxPython-users
On Tuesday, September 26, 2017 at 10:40:57 PM UTC+1, Chris Barker wrote:

either way, as soon as the user clicks on a button on the "number pad" --  that ctr will lose focus.

I suspect it will be a bit confusing for the user for the input to go to the last item that happened to have focus, if, in fact, it was one of the controls that can receive input.

On the other hand, I'm not seeing it in action, maybe it's more obvious than it sounds.

But if you keep "the last control with focus" approach, you might want to have a stronger visual indicator of what the active control is -- so once someone clicks on it, it receives focus, it also get highlighted or something, and then as they go and click on the number pad the highlight remains.

Fair point, although in practice it is not as bad as it sounds. I am using it on a 7" touch screen (the Raspberry Pi official one) and it works quite nicely - I gave it to my wife to try and she had no problems without any instruction!  I could perhaps change the border colour to make it even more obvious.
 

Iwbnwif Yiw

unread,
Sep 27, 2017, 4:42:52 AM9/27/17
to wxPython-users


On Wednesday, September 27, 2017 at 12:12:47 AM UTC+1, Robin Dunn wrote:

Second, sending a wx event will not be the same thing as a native widget getting a native message that it acts upon by adding a character to the control or whatever. wx events are only sent to the wx layers of the software stack, it is only when wx is translating a native message to an event that it can affect things, and that is only to the point of telling the native control whether the event has been handled in the wx layers or not.

Ah, yes I should have known that.
 
Something I haven't seen mentioned yet is the wx.UIActionSimulator.  It's not perfect but it might be enough for what you need.

Interesting idea, I always considered that to be part of the test framework, but actually it could be perfect for this task too.
 
You will still need to ensure the focus is in the right place however before the simulator will be able to stuff keystrokes into the message queue, so you'll still need to keep track of the last focused editable widget, because as Chris said, clicking on the buttons for your simulated keyboard will move the focus to the button.

It might be better if I can find a way of preventing the focus changing to the virtual keypad button. I can't test this just now, but isn't it possible to override wx.Button and sink the focus event, or perhaps return false from  wx.Window.AcceptsFocus()?

Rufus Smith

unread,
Sep 27, 2017, 12:15:28 PM9/27/17
to wxpytho...@googlegroups.com
Another approach, which I am using for a touch screen application, is to have a calculator keyboard with
its own display/text area.  All keypresses affect that display area, and I added the buttons  [set x] [set y], etc
to copy that data into the selected field positions, after the data entry was complete.  

Just a thought.




Iwbnwif Yiw

unread,
Sep 28, 2017, 1:53:31 AM9/28/17
to wxPython-users


On Wednesday, September 27, 2017 at 5:15:28 PM UTC+1, Rufus wrote:

Another approach, which I am using for a touch screen application, is to have a calculator keyboard with
its own display/text area.  All keypresses affect that display area, and I added the buttons  [set x] [set y], etc
to copy that data into the selected field positions, after the data entry was complete.  

Thanks, yes I think that would be a good way, but unfortunately the screen space is rather small on this device so the display would have to float somewhere when it was active. 
Reply all
Reply to author
Forward
0 new messages