I'm still having trouble sizing things

I have this example program which has several problems, and I’ve been staring at it far too long.

It takes one optional argument, --scrolled, which controls whether or not the red part is a wx.lib.scrolledpanel.

The first problem is that I want the text in the red area to be scrolled up and down, but wrapped to fit the current width of the window. In the non-scrolled version, the wrapping works, but in the scrolled version the text just extends off to the right.

The second problem is that if I don’t use a panel to wrap the statictext then it sizes itself to the frame, but with a panel is sizes itself to the text if the text is smaller than the frame. I think I have the sizers set up exactly the same between the version with the panel and the version without.

A third problem is that the existence of a panel in the top part seems to cause the ratio (2 for the top, 1 for the bottom) to be lost when the frame is too short, the bottom portion disappears off the bottom of the window.

That might be too many things for a single post, but we will give this a try!

Thanks in advance for any insight you might have.

#!/usr/bin/env python3

##
## Check for python 3
##
import sys
assert sys.version_info.major == 3

if "--scrolled" in sys.argv:
  scrolled = True
else:
  scrolled = False

try:
  import wx
except:
  print( 'ERROR: python wx module is required' )
  sys.exit( 1 )
import wx.lib.scrolledpanel

class scrolled_panel( wx.lib.scrolledpanel.ScrolledPanel ):
  def __init__( self, parent ):
    super().__init__( parent )
    self.sizer = wx.BoxSizer( wx.VERTICAL )
    self.SetSizer( self.sizer )

class unscrolled_panel( wx.Panel ):
  def __init__( self, parent ):
    super().__init__( parent )
    self.sizer = wx.BoxSizer( wx.VERTICAL )
    self.SetSizer( self.sizer )
    
class wx_frame( wx.Frame ):
  def _button( self, event ):
    print( self, self._button )
    
  def __init__(self, parent, id, title, requested_size=None):
    scaledown = 1.0
    minsize = (600,480)
    maxsize =  wx.DisplaySize()
    if not requested_size:
      size = (int( maxsize[0] * .333), int( maxsize[1] * .666 ))
    else:
      size = requested_size
    xsize = size[0]
    ysize = size[1]
    if xsize > maxsize[0]:
      warning( self.__init, "requested width is larger than display (%d versus %d)" % (xsize, maxsize[0]) )
      xsize = maxsize[0]
    if ysize > maxsize[1]:
      warning( self.__init, "requested height is larger than display (%d versus %d)" % (ysize, maxsize[1]) )
      ysize = maxsize[1]
    if xsize < minsize[0]  or ysize < minsize[0]:
      if requested_size: 
        warning( self.__init__, "requested width or height is too small (%s versus %s)" % (str( size ), str( minsize )) )
      xsize = minsize[0]
      ysize = minsize[1]
    super().__init__( parent, title=title, size=(xsize,ysize) )
    self.SetBackgroundColour( wx.BLUE )
    self.SetMinSize( minsize )
    self.SetMaxSize( maxsize )

    self.vbox = wx.BoxSizer( wx.VERTICAL )

    self.panel = wx.Panel( self )
    if scrolled:
      self.top_panel = scrolled_panel( self.panel )
    else:
      self.top_panel = unscrolled_panel( self.panel )
      
    self.panel_text = wx.StaticText( self.top_panel , label="This is a line with a newline at the end that is repeated many times.\n" * 20 + "This is a short line many times with no newline.  " * 10, style=wx.ALIGN_LEFT )
    self.panel_text.SetBackgroundColour( wx.RED )
    self.panel_text.SetFont( wx.Font( 13, wx.DEFAULT, wx.NORMAL, wx.DEFAULT ) )
    self.top_panel.sizer.Add( self.panel_text, 1, wx.ALIGN_TOP, wx.ALL | wx.EXPAND, 10 )
    if scrolled:
      self.top_panel.SetupScrolling( scroll_x=False )
      self.panel_text.Wrap( self.top_panel.Size[0] )

    self.vbox.Add( self.top_panel, 2, wx.EXPAND | wx.ALL, 10 )

    self.lower_text = wx.StaticText( self.panel, label="" )
    self.lower_text.SetBackgroundColour( wx.GREEN )
    self.vbox.Add( self.lower_text, 1, wx.EXPAND | wx.ALL, 10 )
    
    self.panel.SetSizer( self.vbox )
    
    self.Layout()
    self.Centre()
        
if __name__ == '__main__':
    app = wx.App()
    frame = wx_frame( None, -1, "Testing Example" )
    frame.Show()
    app.MainLoop()

Adding a print statement there to show the value of self.top_panel.Size[0] will probably give you a hint about what the problem is.

Also, using the WIT can be a big help when trying to figure out layout issues, see: Widget Inspection Tool

Ok, that widget inspection tool is kind of fun.

I made something that works the way I want it to. It isn’t coded the way I want, and I can’t say that if I had to write again from scratch that I would succeed, but I have it. I feel like I’m getting somewhere. More practice will surely help.

#!/usr/bin/env python3

import wx
import wx.lib.scrolledpanel

app = wx.App(False)
frame = wx.Frame( None )
frame.SetBackgroundColour( wx.RED )

panel = wx.lib.scrolledpanel.ScrolledPanel( frame )

panel.SetBackgroundColour( wx.BLUE )
text = wx.StaticText( panel, style=wx.TE_MULTILINE, label="static text STATIC TEXT static text STATIC TEST static text\n" * 25 )
text.SetBackgroundColour( wx.GREEN )

sizer = wx.BoxSizer( wx.VERTICAL )
sizer.Add( text, 1, wx.ALL | wx.EXPAND, 10 )

panel.SetSizer( sizer )
panel.SetupScrolling( scroll_x=False)

def on_size( event ):
  event.Skip()
  text.SetSize( panel.GetSize() )
  text.Freeze()
  text.Wrap( frame.GetSize().width )
  text.Thaw()

frame.Bind( wx.EVT_SIZE, on_size )

frame.Show()
app.MainLoop()

No one else is commenting here, but I think I finally defeated the first wx python boss!

#!/usr/bin/env python3

##
## Check for python 3
##
import sys
assert sys.version_info.major == 3

if "--debug" in sys.argv:
  debug = True
else:
  debug = False

import locale

try:
  import wx
except:
  print( 'ERROR: python wx module is required' )
  sys.exit( 1 )
import wx.lib.scrolledpanel

class wx_frame( wx.Frame ):
  def __init__(self, parent, id=-1, title="Untitled", requested_size=None):
    scaledown = 1.0
    minsize = (600,480)
    maxsize =  wx.DisplaySize()
    if not requested_size:
      size = (int( maxsize[0] * .333), int( maxsize[1] * .666 ))
    else:
      size = requested_size
    xsize = size[0]
    ysize = size[1]
    if xsize > maxsize[0]:
      warning( self.__init, "requested width is larger than display (%d versus %d)" % (xsize, maxsize[0]) )
      xsize = maxsize[0]
    if ysize > maxsize[1]:
      warning( self.__init, "requested height is larger than display (%d versus %d)" % (ysize, maxsize[1]) )
      ysize = maxsize[1]
    if xsize < minsize[0]  or ysize < minsize[0]:
      if requested_size: 
        warning( self.__init__, "requested width or height is too small (%s versus %s)" % (str( size ), str( minsize )) )
      xsize = minsize[0]
      ysize = minsize[1]
    super().__init__( parent, title=title, size=(xsize,ysize) )
    if debug:
      self.SetBackgroundColour( wx.BLACK )
    else:
      self.SetBackgroundColour( wx.WHITE )
    self.SetMinSize( minsize )
    self.SetMaxSize( maxsize )

TEXT = "\n".join( ["UPPER TEXT random text to lengthen the line and make it longer for testing wrapping and seeing it works."] * 20 )    

def on_size( event ):
  event.Skip() 
  utext.Freeze()
  utext.SetSize( upper.GetSize() )
  utext.SetLabel( TEXT )
  utext.Wrap( upper.GetSize().width )
  utext.Thaw()
    
app = wx.App( False )
frame = wx_frame( None, title="t031.py" )

upper = wx.lib.scrolledpanel.ScrolledPanel( frame )
lower = wx.Panel( frame )

upper.SetBackgroundColour( wx.RED )
lower.SetBackgroundColour( wx.BLUE )

fsizer = wx.BoxSizer( wx.VERTICAL )
fsizer.Add( upper, 2, wx.ALL | wx.EXPAND, 5 )
fsizer.Add( lower, 1, wx.ALL | wx.EXPAND, 5 )

utext = wx.StaticText( upper, label = TEXT )

usizer = wx.BoxSizer( wx.VERTICAL )
usizer.Add( utext, 1, wx.ALL | wx.EXPAND, 0 )

lsizer = wx.WrapSizer()

upper.SetupScrolling( scroll_x=False )
upper.SetSizer( usizer )
lower.SetSizer( lsizer )

frame.SetSizer( fsizer )
frame.Bind( wx.EVT_SIZE, on_size )

buttons = {}
for i in range( 20 ):
  buttons[i] = wx.Button( lower, label="Button %d" % i)
  lsizer.Add( buttons[i] )
  
frame.Show()    
app.MainLoop()