Display questions!

My last wxPython problem took a long time to fix and now that it is, I
don't even know why! I'm on wxPython 2402, Python 222, Linux.

When the program launches, the text is "messed up". See here:
http://chuckesterbrook.com/files/python/wx/problems/display-01/1.png

When I resize, or move the window off screen and back on, the text is
displayed correctly:
http://chuckesterbrook.com/files/python/wx/problems/display-01/2.png

The code is here (and inlined further down):
http://chuckesterbrook.com/files/python/wx/problems/display-01/AppFrame.py

My first four individual attempts all failed:
  self.Refresh()
  self.Update()
  self.Show()
  wxYield()

My fifth attempt was all four at the same time. That failed.

In my last attempt, I was just reaching:
  wxSafeYield()

It worked! But without any threads, long running loops, timers, or
what-have-you, I don't know the answer to any of these questions:

1. Why is any invocation needed at all? Shouldn't the creation of
   StaticText in the panel cause the affected region to be updated at
   the end of the event loop, or in response to some kind of generated
   refresh event?

2. Given that an invocation is needed, why doesn't a simple Refresh()
   and/or Update() work?

3. Given that wxSafeYield() works, why doesn't wxYield()? If I read the
   docs right, wxSafeYield() is like wxYield(), but it locks out user
   input. I don't see the relation to this code.

4. How come the problem also disappears if the second page of the
   notebook is not added???

I feel totally lost now, wondering where else I might have to sprinkle
in wxSafeYields(). I've read plenty of wxPython wikis, mails and docs,
but I can't relate the required wxSafeYield() in this program to its
simple set up (a notebook with two pages and some static text added
in). There's the activate event of course, but it *seems* harmless.

Any insights are greatly appreciated!

The code:
http://chuckesterbrook.com/files/python/wx/problems/display-01/AppFrame.py

And inlined:

from wxPython.wx import *

class AppFrame(wxFrame):

  def __init__(self):
    wxFrame.__init__(self, None, -1, "", size=(200, 200))

    self.firstTime = True
    self.nb = wxNotebook(self, -1)

    wrapper = wxPanel(self.nb, -1)
    self.myView = MyView(wrapper, -1)
    sizer = wxBoxSizer(wxHORIZONTAL)
    sizer.Add(self.myView, 1, wxEXPAND)
    wrapper.SetSizer(sizer)
    self.nb.AddPage(wrapper, "A")

    if 1:
      # btw, if no second notebook page is added, the problem goes away
      # (otherwise wx.SafeYield is required)
      v = wxPanel(self.nb, -1)
      self.nb.AddPage(v, "B")

    EVT_ACTIVATE(self, self.onActivate)

  def onActivate(self, e):
    if self.firstTime:
      self.myView.addLabels()
      self.firstTime = False
    e.Skip() # don't know if this is needed, but doesn't affect the \
problem either way

class MyView(wxPanel):

  def addLabels(self):
    # this method is called in response to our (ultimately) containing \
frame's on-activate event.
    self.addLabel('Hello, world.', 10, 10)

    # none of the following are sufficient, even together.
# self.Refresh()
# self.Update()
# self.Show()
# wxYield()

    # SafeYield is the *only* invocation here that fixes the display \
problem!
# wxSafeYield()

  def addLabel(self, msg, x, y):
    color = wxColor(255, 255, 127)
    offsets = [
      (+1, +1),
      (-1, -1),
      (+1, -1),
      (-1, +1),
    ]
    for dx, dy in offsets:
      text = wxStaticText(self, -1, msg, wxPoint(x+dx, y+dy))
      text.SetForegroundColour(color)
    wxStaticText(self, -1, msg, wxPoint(x, y))

if __name__=='__main__':
  wxApp = wxPySimpleApp()
  AppFrame().Show()
  wxApp.MainLoop()

Chuck Esterbrook wrote:
[...]

In my last attempt, I was just reaching:
  wxSafeYield()

It worked! But without any threads, long running loops, timers, or what-have-you, I don't know the answer to any of these questions:

1. Why is any invocation needed at all? Shouldn't the creation of
   StaticText in the panel cause the affected region to be updated at
   the end of the event loop, or in response to some kind of generated
   refresh event?

2. Given that an invocation is needed, why doesn't a simple Refresh()
   and/or Update() work?

It has something to do with the fact that the static texts are created during the EVT_ACTIVATE event, and that they overlap. If addLabels is called in the __init__ or even in the frame's first EVT_SIZE event then it works okay without any extra help.

Another part of the problem may be that on GTK wxStaticTexts are not true windows, but are pseudo-widgets that are just drawn on their parents. There may be some interaction with the parent that makes the pseudo-widgets work right that is missed by not creating the stattext until the EVT_ACTIVATE event...

3. Given that wxSafeYield() works, why doesn't wxYield()? If I read the
   docs right, wxSafeYield() is like wxYield(), but it locks out user
   input. I don't see the relation to this code.

Probably because wxSafeYield disables all windows and then enables them again, thus generating the right messages for the panel/pseudo-widgets interaction to happen.

4. How come the problem also disappears if the second page of the
   notebook is not added???

I don't know but I guess that it is probably related to the above.

I feel totally lost now, wondering where else I might have to sprinkle in wxSafeYields(). I've read plenty of wxPython wikis, mails and docs, but I can't relate the required wxSafeYield() in this program to its simple set up (a notebook with two pages and some static text added in). There's the activate event of course, but it *seems* harmless.

Any insights are greatly appreciated!

wxControls really are not designed to be overlapped and there could be other problems other than what you see here. For example, on Windows all you will see with your example is the static text with the black foreground, and none of the yellow ones. This is because on MSW the wxStaticText is a true window and so you can't see through it to the wxStaticTexts that are behind it.

For the effect you are trying to do (which looks very nice BTW) you'll be better off creating your own custom control and drawing the text to the control's DC. wxPython/lib/stattext.py would be a good starting point. You could probably derive a class from wxGenStaticText and just override DoGetBestSize and OnPaint...

···

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

[snip]

wxControls really are not designed to be overlapped and there could
be other problems other than what you see here. For example, on
Windows all you will see with your example is the static text with
the black foreground, and none of the yellow ones. This is because
on MSW the wxStaticText is a true window and so you can't see through
it to the wxStaticTexts that are behind it.

For the effect you are trying to do (which looks very nice BTW)
you'll be better off creating your own custom control and drawing the
text to the control's DC. wxPython/lib/stattext.py would be a good
starting point. You could probably derive a class from
wxGenStaticText and just override DoGetBestSize and OnPaint...

Thanks Robin! That's exactly the kind of information I was looking for.

···

On Wednesday 12 March 2003 09:40 am, Robin Dunn wrote:

--
Chuck
http://ChuckEsterbrook.com