AW: Strange bug in wxTextCtrl.SetValue

Dear Robin,
This is the workaround i was already trying to use. The main problem is that
the behavior when the callback is retriggered by a SetValue depends on the
number of characters in the text control. I cannot reset the flag when the
callback isn't called twice (in the case the number of characters is less
than 1024). The best thing would be to get an event with any call to
SetValue as this is what the reference permits!
If we knew where this 1024-behavior originates (i.e. Python or C++
framework?) it would be a great step for the solution.
Best regards
Oliver

···

-----Ursprüngliche Nachricht-----
Von: Robin Dunn [mailto:robin@alldunn.com]
Gesendet: Mittwoch, 7. Januar 2004 19:43
An: wxPython-users@lists.wxwindows.org
Betreff: Re: [wxPython-users] Strange bug in wxTextCtrl.SetValue

Oliver Walczak wrote:

Dear list,
For security purposes i need to check the data that i put into the text
controls of my dialog. Therefore i use handlers like EVT_TEXT(self,
ID_MY_TEXT_CTRL, self.OnChangeText) in my class that is derived from
wxFrame. OnChangeText looks similary to this:

def OnChangeText(self, event):
  ctrl = self.FindWindowById(ID_MY_TEXT_CTRL)
  myString = event.GetString()
  <do something with myString>
  ctrl.SetValue(myString)

This call shows different behaviors on different machines: if myString

takes

more than 1024 characters the program hangs up with a stack overflow. I
noticed that with more than 1024 characters in the string SetValue() seems
to call the callback consistently. I tested on two machines, both
identically installed with WindowsXP, Python 2.2.3, wxPython 2.4.2.4 and
win32all 1.6.2. On the first PC it worked regardless of the string's

length.

The callback gets called about 6 or 7 times but then it calms again. On

the

second one it crashes badly (i.e. if you don't feed a print statement or
something like that to OnChangeText you will not even get a traceback!)
after a series of calls.

One possibility could be that the SetValue is causing another recursive
EVT_TEXT on the one machine (or perhaps some other difference such as
different order of the events.) This can be caused by different
versions of the native controls as MS is sometimes highly inconsistent
at the low level things. The best thing to do would be to set a flag
before the SetValue and then check it at the begining of OnChangeText
and do nothing if it is set.

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

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

Hi all,

The attached program shows a frame, with a panel, with a red border around
the panel.

It works with MSW, it works with GTK2 on RH9, but it does not work with GTK1
on RH9. Platforms are all wxPython 2.4.2.4 on Python 2.3.3.

I battled to reproduce this in a sample program. MSW was easy to get
working, but GTK2 proved surprisingly sensitive about the position of the
routine to resize the panel, and the position of the command to change the
frame's background colour. Eventually I got it to work as shown, but it
seems to indicate that what I am doing is not very robust under GTK.

Does anyone know how to make this work correctly on MSW, GTK1, and GTK2? Any
advice will be much appreciated.

Frank Millman

fm21.py (1.03 KB)

Frank Millman wrote:

The attached program shows a frame, with a panel, with a red border around
the panel.

It works with MSW, it works with GTK2 on RH9, but it does not work with GTK1
on RH9. Platforms are all wxPython 2.4.2.4 on Python 2.3.3.

I'm not sure what's accounting for the platfrom differences, but I think your problem is due to the behaviour of a wxFrame sizing a sinlge child to fill itself. The re-size is probably happening after the MyPanel.__init__

As rule, you are treading on tricky ground when you count on sizing, etc. happening in a certain order. You are asking for platfrom differences, as you ahve discovered. You may be able to fix it by putting the appropriate code in the OnSize handler, but that didn't work for me at first try. Perhaps something with SetSizeHints could help.

However, it seems your architecture is problematic anyway. Your panel re-sets the background color of it's parent. This is what you want now, but if you were to put that Panel in something else, you could get a mess. I'd be inclinded to just have the Panel draw a border around itself. I've enclosed a version that does this.

If that doesn't get the look you want, or you have problems with placing things on top of the border, you might put a panel on your panel in one class: (untested)

class MyPanel(wx.Panel):
     def __init__(self,window):
         wx.Panel.__init__(self,window,-1)
         self.InnerPanel = wx.Panel(self, -1)
  self.SetBackgroundColour(wx.RED)

  wx.EVT_SIZE(self, self.OnSize)

     def OnSize(self, Event)
         size = self.GetSize()
         self.InnerPanel.SetSize((size[0]-4,size[1]-4))
         self.InnerPanel.SetPosition((2,2))
         self.Refresh() # this may not be required

This would mean that putting things on it would be wierd:

  button = wx.Button(self.InnerPanel, ...

But there may be a way around that

-Chris

test.py (1.16 KB)

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Oliver Walczak wrote:

Dear Robin,
This is the workaround i was already trying to use. The main problem is that
the behavior when the callback is retriggered by a SetValue depends on the
number of characters in the text control. I cannot reset the flag when the
callback isn't called twice (in the case the number of characters is less
than 1024). The best thing would be to get an event with any call to
SetValue as this is what the reference permits!
If we knew where this 1024-behavior originates (i.e. Python or C++
framework?) it would be a great step for the solution.

Does it make a difference if you use wxTE_RICH or wxTE_RICH2 styles? Please send a runnable sample that demonstrates the problem.

···

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

Frank Millman wrote:
> The attached program shows a frame, with a panel, with a red border

around

> the panel.
>
> It works with MSW, it works with GTK2 on RH9, but it does not work with

GTK1

> on RH9. Platforms are all wxPython 2.4.2.4 on Python 2.3.3.

Chris Barker wrote:

I'm not sure what's accounting for the platfrom differences, but I think
  your problem is due to the behaviour of a wxFrame sizing a sinlge
child to fill itself. The re-size is probably happening after the
MyPanel.__init__

I think you are right. I cannot prove it, but it looks as if the panel
always fills the entire area of the frame, even if I reduce the size of the
panel.

However, it seems your architecture is problematic anyway. Your panel
re-sets the background color of it's parent. This is what you want now,
but if you were to put that Panel in something else, you could get a
mess. I'd be inclinded to just have the Panel draw a border around
itself. I've enclosed a version that does this.

Thanks for the enclosed code. I tried it, and it worked in test mode, but
then blew up in my live app. Let me explain what I am using this for.

I create the border on init(), but I leave it the same colour as the
background colour, so it is normally invisible. When a user logs in to my
application, they are authenticated, and read/write permissions on various
database tables are set up. If they need to change a protected field, they
can call a supervisor, who can enter their own user-id and password, and the
app changes all permissions to their profile. If the supervisor forgets to
change back to the original user, they will leave the app in a dangerous
state. I need to find some way to highlight this situation. After trying a
few things, I came up with the idea of creating this invisible border, and
then using a wxTimer to change the border colour to red and back again once
per second. I am quite pleased with the 'warning' effect that this creates.

When I tried your code, drawing the red border caused the existing controls
on the panel to disappear! Back to the drawing board.

If that doesn't get the look you want, or you have problems with placing
things on top of the border, you might put a panel on your panel in one
class: (untested)

class MyPanel(wx.Panel):
     def __init__(self,window):
         wx.Panel.__init__(self,window,-1)
         self.InnerPanel = wx.Panel(self, -1)
self.SetBackgroundColour(wx.RED)

wx.EVT_SIZE(self, self.OnSize)

     def OnSize(self, Event)
         size = self.GetSize()
         self.InnerPanel.SetSize((size[0]-4,size[1]-4))
         self.InnerPanel.SetPosition((2,2))
         self.Refresh() # this may not be required

This would mean that putting things on it would be wierd:

button = wx.Button(self.InnerPanel, ...

But there may be a way around that

I tried this approach, and although it feels a bit odd, it works just fine,
on all platforms. I minimized the weirdness like this.

class MyPanel(wx.Panel):
    def __init__(self,window):
        wx.Panel.__init__(self,window,-1)
        self.panel = InnerPanel(self)

class InnerPanel(wx.Panel):
    def __init__(self,panel):
        wx.Panel.__init__(self,panel,-1)

All attributes and methods that were on MyPanel are now on InnerPanel.

I used to invoke MyPanel like so -
    self.panel = MyPanel(window)

I now invoke it like so -
    self.panel = MyPanel(window).panel

Nothing else needed changing, and it works fine. Many thanks, Chris, for
your valuable input.

Frank

Frank Millman wrote:

Thanks for the enclosed code. I tried it, and it worked in test mode, but
then blew up in my live app.

It sounds like you've found a fine solution, but I tire dto replicate what you want, and it works fine for me. Could this be a platform difference? I'm using wxGTK1. I only have one button on it, perhaps with more complicated controls on there, there is a problem, but IO's thin it could be solved with a Refresh() call in the right place. See the attached code.

-Chris

test.py (1.72 KB)

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Frank Millman wrote:

> Thanks for the enclosed code. I tried it, and it worked in test mode,

but

> then blew up in my live app.

Chris Barker wrote:

It sounds like you've found a fine solution, but I tire dto replicate
what you want, and it works fine for me. Could this be a platform
difference? I'm using wxGTK1. I only have one button on it, perhaps with
more complicated controls on there, there is a problem, but IO's thin it
could be solved with a Refresh() call in the right place. See the
attached code.

I find that under MSW, all controls disappear, while under GTK, some
controls remain, but all StaticText disappears. Refresh() does help, but GTK
seems to require an OnPaint handler, while MSW works without it, and goes
wrong if I include it. I am sure that with some effort a compromise can be
found, but as your other suggestion of the InnerPanel works so well, I will
stick with that for now.

Many thanks again for your assistance.

Frank

Hi all,

This is a question that came up from a post by Frank Millman. I think he's found a satisfactory work around, but I'm still not saatisfied.

The broad question is how to make a custom control that involves drawing to a panel with a DC, but having other controls on that panel as well. In particular, Frank wanted a Panel one could put other controls on, and optionally draw a border around the panel that would flash, drawing the users atention to an error or whatever. It seemed simple enough to jsut draw the border with a wxCliewnt DC, and be done with it. Unfortunately some controls, on some platforms, would get destroyed in teh process. I put a self.Refresh() call after the drawing, which seemed to work (on GTK1, at least), but it would flasha fair bit. Now I have it call Reresh() in all the Panels children after re-drawing the border, but this jsut seems too kludgy for my taste. Two questions:

1) Is there a more elegant solution to this problem?

2) Does this even work on other platforms (Windows, in particular)?

Sample code enclosed

thanks,

-Chris

DrawOnControl.py (2.02 KB)

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Chris Barker wrote:

Hi all,

This is a question that came up from a post by Frank Millman. I think he's found a satisfactory work around, but I'm still not saatisfied.

The broad question is how to make a custom control that involves drawing to a panel with a DC, but having other controls on that panel as well. In particular, Frank wanted a Panel one could put other controls on, and optionally draw a border around the panel that would flash, drawing the users atention to an error or whatever. It seemed simple enough to jsut draw the border with a wxCliewnt DC, and be done with it. Unfortunately some controls, on some platforms, would get destroyed in teh process. I put a self.Refresh() call after the drawing, which seemed to work (on GTK1, at least), but it would flasha fair bit. Now I have it call Reresh() in all the Panels children after re-drawing the border, but this jsut seems too kludgy for my taste. Two questions:

1) Is there a more elegant solution to this problem?

In general, you can use the wx.CLIP_CHILDREN style on the panel and it should then not draw over the child controls, although I seem to remember that it didn't work in certain scenarios on wxGTK for me in the past, but setting the appropriate clipping region myself took care of it.

In this specific case you can set the dc's brush to wx.TRANSPARENT_BRUSH so the background of the rectangle is not drawn.

2) Does this even work on other platforms (Windows, in particular)?

Yep.

···

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

Chris Barker wrote:
> Hi all,
>
> This is a question that came up from a post by Frank Millman. I think
> he's found a satisfactory work around, but I'm still not saatisfied.
>
> The broad question is how to make a custom control that involves drawing
> to a panel with a DC, but having other controls on that panel as well.
>

Robin Dunn wrote:

In this specific case you can set the dc's brush to wx.TRANSPARENT_BRUSH
so the background of the rectangle is not drawn.

So simple, and so effective. I have changed my flashing border to use this
technique, and it works perfectly.

Thanks, Robin.

Frank