Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
535 views
in Technique[技术] by (71.8m points)

python - wxPython problems with wrapping staticText

A simplified version of the code is posted below (white space, comments, etc. removed to reduce size - but the general format to my program is kept roughly the same).

When I run the script, the static text correctly wraps as it should, but the other items in the panel do not move down (they act as if the statictext is only one line and thus not everything is visible).

If I manually resize the window/frame, even just a tiny amount, everything gets corrected, and displays as it is should.

Why doesn't it display correctly to begin with? I've tried all sorts of combination's of GetParent().Refresh() or Update() and GetTopLevelParent().Update() or Refresh(). I've also tried everything I can think of but cannot get it to display correctly without manually resizing the frame/window. Once re-sized, it works exactly as I want it to.

Information:

  • Windows XP
  • Python 2.5.2
  • wxPython 2.8.11.0 (msw-unicode)

My Code:

#! /usr/bin/python

import wx

class StaticWrapText(wx.PyControl):
   def __init__(self, parent, id=wx.ID_ANY, label='', pos=wx.DefaultPosition,
                size=wx.DefaultSize, style=wx.NO_BORDER,
                validator=wx.DefaultValidator, name='StaticWrapText'):
      wx.PyControl.__init__(self, parent, id, pos, size, style, validator, name)
      self.statictext = wx.StaticText(self, wx.ID_ANY, label, style=style)
      self.wraplabel = label
      #self.wrap()
   def wrap(self):
      self.Freeze()
      self.statictext.SetLabel(self.wraplabel)
      self.statictext.Wrap(self.GetSize().width)
      self.Thaw()
   def DoGetBestSize(self):
      self.wrap()
      #print self.statictext.GetSize()
      self.SetSize(self.statictext.GetSize())
      return self.GetSize()

class TestPanel(wx.Panel):
   def __init__(self, *args, **kwargs):
      # Init the base class
      wx.Panel.__init__(self, *args, **kwargs)
      self.createControls()
   def createControls(self):
      # --- Panel2 -------------------------------------------------------------
      self.Panel2 = wx.Panel(self, -1)
      msg1 =  'Below is a List of Files to be Processed'
      staticBox      = wx.StaticBox(self.Panel2, label=msg1)
      Panel2_box1_v1 = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
      Panel2_box2_h1 = wx.BoxSizer(wx.HORIZONTAL)
      Panel2_box3_v1 = wx.BoxSizer(wx.VERTICAL)

      self.wxL_Inputs = wx.ListBox(self.Panel2, wx.ID_ANY, style=wx.LB_EXTENDED)

      sz = dict(size=(120,-1))
      wxB_AddFile    = wx.Button(self.Panel2, label='Add File',        **sz)
      wxB_DeleteFile = wx.Button(self.Panel2, label='Delete Selected', **sz)
      wxB_ClearFiles = wx.Button(self.Panel2, label='Clear All',       **sz)
      Panel2_box3_v1.Add(wxB_AddFile,    0, wx.TOP, 0)
      Panel2_box3_v1.Add(wxB_DeleteFile, 0, wx.TOP, 0)
      Panel2_box3_v1.Add(wxB_ClearFiles, 0, wx.TOP, 0)

      Panel2_box2_h1.Add(self.wxL_Inputs, 1, wx.ALL|wx.EXPAND, 2)
      Panel2_box2_h1.Add(Panel2_box3_v1,  0, wx.ALL|wx.EXPAND, 2)

      msg =  'This is a long line of text used to test the autowrapping '
      msg += 'static text message.  '
      msg += 'This is a long line of text used to test the autowrapping '
      msg += 'static text message.  '
      msg += 'This is a long line of text used to test the autowrapping '
      msg += 'static text message.  '
      msg += 'This is a long line of text used to test the autowrapping '
      msg += 'static text message.  '
      staticMsg = StaticWrapText(self.Panel2, label=msg)

      Panel2_box1_v1.Add(staticMsg,      0, wx.ALL|wx.EXPAND, 2)
      Panel2_box1_v1.Add(Panel2_box2_h1, 1, wx.ALL|wx.EXPAND, 0)
      self.Panel2.SetSizer(Panel2_box1_v1)

      # --- Combine Everything -------------------------------------------------
      final_vbox = wx.BoxSizer(wx.VERTICAL)
      final_vbox.Add(self.Panel2, 1, wx.ALL|wx.EXPAND, 2)
      self.SetSizerAndFit(final_vbox)

class TestFrame(wx.Frame):
   def __init__(self, *args, **kwargs):
      # Init the base class
      wx.Frame.__init__(self, *args, **kwargs)
      panel = TestPanel(self)
      self.SetClientSize(wx.Size(500,500))
      self.Center()

class wxFileCleanupApp(wx.App):
   def __init__(self, *args, **kwargs):
      # Init the base class
      wx.App.__init__(self, *args, **kwargs)
   def OnInit(self):
      # Create the frame, center it, and show it
      frame = TestFrame(None, title='Test Frame')
      frame.Show()
      return True

if __name__ == '__main__':
   app = wxFileCleanupApp()
   app.MainLoop()
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Using Mike Driscoll's code as a baseline, I hope this demonstrates my issue. There are two different versions of using "txt". Here are three things I want you to try:

  1. Run it as-is. With my StaticWrapText. It displays wrong at first, but re-size the window and it works EXACTLY as I want. There is no blank/wasted space below the text before the "button"

  2. Change these two lines (change the comments):
    txt = wx.StaticText(panel, label=text)
    #txt = StaticWrapText(panel, label=text)
    Now you will see there is no wrapping and the text is always on only one line. Definitely not what we want. This is because of "sizer.Add(txt, 0, wx.EXPAND, 5) "...so going on to Part 3...

  3. Keep the change from Part 2 and also change:
    sizer.Add(txt, 0, wx.EXPAND, 5)
    to:
    sizer.Add(txt, 1, wx.EXPAND, 5)
    So now the statictext will expand. This is CLOSE to working...BUT I don't want all that wasted space between the text and the button. If you make the window large, there is a lot of wasted space. See Part 1 after the window is re-sized to see the difference.

Code:

import wx

class StaticWrapText(wx.PyControl):
   def __init__(self, parent, id=wx.ID_ANY, label='', pos=wx.DefaultPosition,
                size=wx.DefaultSize, style=wx.NO_BORDER,
                validator=wx.DefaultValidator, name='StaticWrapText'):
      wx.PyControl.__init__(self, parent, id, pos, size, style, validator, name)
      self.statictext = wx.StaticText(self, wx.ID_ANY, label, style=style)
      self.wraplabel = label
      #self.wrap()
   def wrap(self):
      self.Freeze()
      self.statictext.SetLabel(self.wraplabel)
      self.statictext.Wrap(self.GetSize().width)
      self.Thaw()
   def DoGetBestSize(self):
      self.wrap()
      #print self.statictext.GetSize()
      self.SetSize(self.statictext.GetSize())
      return self.GetSize()

class MyForm(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")

        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)

        text = "I'm subclasses the statictext because I want it to act exactly like a static text, but correctly wordwrap as needed. I've found several examples of it on the web, but none that worked how I wanted. The wordwrap makes it look much nicer when the user may decide to re-size the window, so I would definitely like to have it be wordwrapped. I know about the wx.lib.wordwrap, but chose to use the built in Wrap function of the statictext control instead. It basically does the same thing from what I understand."
        #txt = wx.StaticText(panel, label=text)
        txt = StaticWrapText(panel, label=text)
        wxbutton = wx.Button(panel, label='Button', size=wx.Size(120,50))
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(txt,      0, wx.EXPAND, 5)
        sizer.Add(wxbutton, 1, wx.EXPAND, 5)
        panel.SetSizer(sizer)

# Run the program
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = MyForm().Show()
    app.MainLoop()

EDIT:

AHHH...finally! I tried using the Layout() method on virtually every level of the program, but I actually needed to use Layout() on the SIZER which is found with the method GetSizer() - or you can send SendSizeEvent() to the panel (commented in code below). Thus, the following now does EXACTLY what I want! Thanks for the help. The only other change was to store the panel with self.panel in the frame class. As a note, I had to put this statement AFTER the frame.Show() or it didn't work correctly.

Code:

import wx

class StaticWrapText(wx.PyControl):
   def __init__(self, parent, id=wx.ID_ANY, label='', pos=wx.DefaultPosition,
                size=wx.DefaultSize, style=wx.NO_BORDER,
                validator=wx.DefaultValidator, name='StaticWrapText'):
      wx.PyControl.__init__(self, parent, id, pos, size, style, validator, name)
      self.statictext = wx.StaticText(self, wx.ID_ANY, label, style=style)
      self.wraplabel = label
      #self.wrap()
   def wrap(self):
      self.Freeze()
      self.statictext.SetLabel(self.wraplabel)
      self.statictext.Wrap(self.GetSize().width)
      self.Thaw()
   def DoGetBestSize(self):
      self.wrap()
      #print self.statictext.GetSize()
      self.SetSize(self.statictext.GetSize())
      return self.GetSize()

class MyForm(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")

        # Add a panel so it looks the correct on all platforms
        self.panel = wx.Panel(self, wx.ID_ANY)

        text = "I'm subclasses the statictext because I want it to act exactly like a static text, but correctly wordwrap as needed. I've found several examples of it on the web, but none that worked how I wanted. The wordwrap makes it look much nicer when the user may decide to re-size the window, so I would definitely like to have it be wordwrapped. I know about the wx.lib.wordwrap, but chose to use the built in Wrap function of the statictext control instead. It basically does the same thing from what I understand."
        txt = StaticWrapText(self.panel, label=text)
        wxbutton = wx.Button(self.panel, label='Button', size=wx.Size(120,50))
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(txt,      0, wx.EXPAND, 5)
        sizer.Add(wxbutton, 1, wx.EXPAND, 5)
        self.panel.SetSizer(sizer)

# Run the program
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = MyForm()
    frame.Show()
    #frame.panel.SendSizeEvent()
    frame.panel.GetSizer().Layout()
    app.MainLoop()

As a final note, in my original program posted, the following line needs to be added just before or after frame.Show():
frame.panel.Panel2.GetSizer().Layout()

Interestingly...with that original example this can be before or after frame.Show() but the other example requires that it be after frame.Show(). I'm not sure why, but just put it after and you're safe.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...