I understand there are lots of settings for setting layouts. Here is a ListCtrl of fixed dimensions inside a horizontal sizer.
This absolute size either displays leftover space or is too small depending on number of columns.
What are the layout commands to draw a ListCtrl?
How do I make the ListCtrl more responsive, so that it:
shows data from many columns at a glance without needing a resize
doesnt become bad on different GUIs and resizes
Code listing for the panel that hosts the list:
class CalcPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.lc = wx.ListCtrl(self, -1, size=(200,200), style=wx.LC_REPORT)
self.lc.InsertColumn(0, 'State')
self.lc.InsertColumn(1, 'Capital')
#self.lc.SetColumnWidth(0, 140)
#self.lc.SetColumnWidth(1, 153)
#self.list_ctrl.Show()
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
btn = wx.Button(self, label="Add Line")
self.sizer.Add(btn)
self.sizer.Add(self.lc, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(self.sizer)
self.Fit()
def InitUI(self):
pass
Have you looked at wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin?
if you inherit from wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin as well as wx.ListCtrl, and call the classes __init__ in your __init__ method the last column will auto resize with resize events
You can also change which column auto resizes with setResizeColumn
I submitted an answer that was COMPLETELY wrong because I did not read the question anywhere near well enough. To make up for this mistake, I did some research and I think I have an answer that works.
Checking the wx.ListCtrl API, I've found a method SetColumnWidth(self, col, width) which I think does exactly what you want.
You said that your wx.ListCtrl is a fixed width? In that case...
lcWidth = 200
lcHeight = 200
self.lc = wx.ListCtrl(self, -1, size=(lcWidth,lcHeight), style=wx.LC_REPORT)
self.lc.InsertColumn(0, 'State')
self.lc.InsertColumn(1, 'Capital')
stateWidth = self.lc.GetColumnWidth(0)
self.lc.SetColumnWidth(1, (lcWidth - stateWdith))
Incidently, if you wish your wx.ListCtrl to remain a fixed size, you should remove the wx.GROW flag from your self.sizer.Add() call. wx.GROW will resize the item so that it fills all the available space, not what you want.
self.sizer.Add(self.lc, 1, wx.LEFT | wx.TOP)
If your wx.ListCtrl is NOT a fixed width, you could declare a custom wx.ListCtrl that handles this resizes accordingly
class ResizingColListCtrl (wx.ListCtrl):
def __init__(self, *args, **kwargs):
wx.ListCtrl.__init__(self, *args, **kwargs)
self.sizeColumns()
self.Bind(EVT_SIZE, self.sizeColumns)
def sizeColumns(event=None)
width = self.GetSize()[0]
numCols = self.GetColumnCount()
for i in range(numCols):
self.SetColumnWidth(i, width/numCols) #set your column width to whatever proportions you want
if event:
event.Skip() #since this is a wx.EVT_SIZE, this line is important
So far as I can tell, wxPython does not have this functionality natively.
Related
Considering the code below. I create a StaticText on which I set a Minimum size. However, the StaticText always gets this size as the actual size. Even though the text does not fit. Am I doing something wrong here ?
I want to use this functionality to create a key-value display with proper alignment of the values and only expanding the key part if needed:
a key - the value
another key - another value
a big key which does not fit - this value
the key - the value
import wx
class TestPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
sizer = wx.BoxSizer()
static_text = wx.StaticText(
parent=self, label="a long label exceeding min size."
)
static_text.SetMinSize(wx.Size(50, -1))
sizer.Add(static_text)
self.SetSizer(sizer)
if __name__ == "__main__":
app = wx.App()
frm = wx.Frame(None, title="Test MinSize")
pnl = TestPanel(frm)
frm.Show()
app.MainLoop()
This may not be want to you want to hear but here goes.
The sizer performs a dance between the programmers desire to display data and the variable nature of that data. Additionally it has to cope with that pesky external thing we refer to as the user, who has a habit of resizing windows.
SetMinSize is an instruction to the sizer, a hint if you like, on what it should attempt to do automatically. Most controls will also set the minimal size to the size given in the control’s constructor, as a best guess.
These instructions can be overridden or adjusted with the proportion and flag values of the sizer entry for that control.
Always keep in mind that other controls within the same sizer will be affected, which can have undesirable results on presentation.
If we give the sizer the wx.EXPAND flag for that control, it will show the whole widget whilst keeping the MinSize. In this case, expanding it vertically.
If we give the sizer a proportion of 1 it will stretch it as much as it can in the direction of the sizer, relative to other controls sharing that sizer.
To appreciate what goes on, it's best to play with code like this, altering the MinSize, proportion and flags, testing each change, until the voodoo of sizers becomes slightly less obscure.
Note: In addition, also test re-sizing the window, to see what happens in each scenario.
Additional testing notes:
Test sizers with more than 1 control or widget
Assign colours to highlight what isn't always obvious.
import wx
class TestPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
sizer = wx.BoxSizer(wx.HORIZONTAL)
static_text = wx.StaticText(
parent=self, label="a long label exceeding min size.")
static_text2 = wx.StaticText(self, label="another label")
static_text.SetBackgroundColour('green')
static_text.SetMinSize(wx.Size(50, -1))
sizer.Add(static_text, proportion=1, flag=wx.ALL, border=0)
sizer.Add(static_text2, 0, wx.EXPAND, 0)
self.SetSizer(sizer)
if __name__ == "__main__":
app = wx.App()
frm = wx.Frame(None, title="Test MinSize", size=(300,100))
pnl = TestPanel(frm)
frm.Show()
app.MainLoop()
Examples for the above code:
I've seen a lot of posts about this around the Internet but none of the advice works.
Here is my test code:
import wx
class DisplayText(wx.Dialog):
def __init__(self, parent, text="", displayMode=0):
# Initialize dialog
wx.Dialog.__init__(self, parent, size=(480,320), style=( wx.DIALOG_EX_METAL | wx.STAY_ON_TOP ) )
# Freeze UI so user won't see stuff flashing
self.Freeze()
# (For Mac) Setup a panel
self.panel = wx.Panel(self,size=(480,320))
self.panel.SetBackgroundColour(wx.Colour(0,0,128))
# Setup sizer for vertical centering
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
# Create text field
self.txtField = wx.StaticText(self.panel,size=(448,-1),style=wx.ALIGN_CENTRE_HORIZONTAL)
self.txtField.SetLabel(text)
self.txtField.Wrap(448)
self.txtField.SetLabel(self.txtField.GetLabel())
self.txtField.SetFont(wx.Font(36, wx.DEFAULT, wx.BOLD, 0))
self.txtField.SetForegroundColour(wx.Colour(255,255,255))
# Add the static text to the sizer
self.sizer.Add(self.txtField,1, 0,15)
self.panel.SetSizer(self.sizer)
# Ensure layouts are applied
self.sizer.Layout()
# Thaw UI
self.Thaw()
# Center form
self.Center()
app = wx.App(False)
c = DisplayText(None, text="Now is the time for all good men to come to the aid of their country.")
c.Show()
import wx.lib.inspection
wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
Based on my understanding of StaticText, the problem seems to be that when I call the Wrap() function, the control wraps the text but does not change the control's size. This means the sizer, unaware of the actual vertical size of the wrapped text, cannot properly reposition the static text.
One post I found suggested using wx.EXPAND, but this does not solve the problem. Allowing the sizer to expand the StaticText simply makes the StaticText fill the entire frame, and since StaticText doesn't vertically center in and of itself, the text will end up at the top of the form, not the center.
Another post suggested experimenting with wx.ALIGN_CENTER_VERTICAL in the sizer, but this didn't help either, because the same problem remains - the sizer can only work on the size of the control, and the control's size is NOT changing after the text is wrapped and re-rendered.
Various attempts have resulted in either the text wrapped but at the top of the form, only the first line being shown in the center of the form, only the first line being shown at the top of the form, and the text not being wrapped and thus flowing off the right hand edge of the form. But no combination of anything I've read has worked to vertically center the wrapped text on the form.
What I therefore would have to be able to do is somehow figure out the size of the text vertically after the wrapping has taken place. This can't be assumed because on different platforms, or even on the same platform, or even based on the text itself, the font's vertical pixel size may differ. So this needs to somehow be calculated.
I'm thinking if I can somehow figure out the actual size of the rendered text inside the StaticText control, I could then explicitly set the control's size and then let the sizer do its work.
This seems like it should be a relatively simple thing to accomplish...
Advice would be greatly appreciated!
The key was the static text style; you needed to enable the multi-line style for the wrap to work: wx.TE_MULTILINE.
I also set the layout on the panel itself, not the sizer but that might not be related to the problem.
On my machine, Windows 7 64bit, I needed to change the SetFont call; you might need to change it back.
import math
from threading import Thread
import time
import wx
e = lambda x: complex(math.cos(x), 0) + complex(0, math.sin(x))
r = lambda x: ((e(0*math.pi/3.0)*e(x)).real + 1)/2.0
g = lambda x: ((e(2*math.pi/3.0)*e(x)).real + 1)/2.0
b = lambda x: ((e(4*math.pi/3.0)*e(x)).real + 1)/2.0
colo = lambda rad: map(lambda x: int(128 * x(rad)), [r, g, b])
class DisplayText(wx.Dialog):
def __init__(self, parent, text="", displayMode=0):
# Initialize dialog
wx.Dialog.__init__(self, parent, size=(480, 320), style=( wx.DIALOG_EX_METAL | wx.STAY_ON_TOP ) )
# Freeze UI so user won't see stuff flashing
self.Freeze()
# (For Mac) Setup a panel
self.panel = wx.Panel(self, size=(480, 320))
self.panel.SetBackgroundColour(wx.Colour(0, 0, 128))
self.panel.SetBackgroundColour(wx.Colour(*colo(0)))
# Setup sizer for vertical centering
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.panel.SetSizer(self.sizer)
# Create text field
self.txtField = wx.StaticText(self.panel, size=(448, -1), style=(wx.ALIGN_CENTRE_HORIZONTAL | wx.TE_MULTILINE ))
self.txtField.SetFont(wx.Font(36, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD))
self.txtField.SetForegroundColour(wx.Colour(255, 255, 255))
self.txtField.SetLabel(text)
self.txtField.Wrap(448)
self.sizer.Add(self.txtField, 1, 0, 15)
# Add the static text to the sizer
# Ensure layouts are applied
self.panel.Layout()
self.panel.Refresh()
# Thaw UI
self.Thaw()
# Center form
self.Center()
self.angle = 0
self.angle_inc = (2*math.pi)/25.0
def change_colour(self):
t = Thread(target=self.epilepsy_mode)
t.start()
def epilepsy_mode(self):
while True:
time.sleep(0.02)
self.panel.SetBackgroundColour(wx.Colour(*colo(self.angle)))
self.panel.Refresh()
self.angle = (self.angle + self.angle_inc) % (2*math.pi)
app = wx.App(False)
c = DisplayText(None, text="Now is the time for all good men to come to the aid of their country.")
c.Show()
#import wx.lib.inspection
#wx.lib.inspection.InspectionTool().Show()
c.change_colour()
app.MainLoop()
I want to use wxpython to implement a GUI that has three wxTextCtrls widgets, and I want to keep them having the same size when I change the size of the window
If I do nothing to these wxTextCtrls, it works well. But when I add many lines of contents in one of these three widgets, like the first one, it's size will be bigger than the others' when I change the size of the window. How could I keep these widgets having same size? Thanks!
(PS: I think the image of the generated GUI could explain my question better, but I can't post image. Sorry)
My code is
import os
import wx
class My_Gui(wx.Frame):
def __init__(self, parent, title):
super(My_Gui, self).__init__(parent, title = title,size=(800,600))
self.InitUI()
self.Centre()
self.Show()
def InitUI(self):
my_panel = wx.Panel(self)
hbox = wx.BoxSizer(wx.HORIZONTAL)
fgs = wx.FlexGridSizer(3, 2, 25, 30)
button1 = wx.Button(my_panel, label='button1', size=(120,150))
button2 = wx.Button(my_panel, label='button2', size=(120,150))
button3 = wx.Button(my_panel, label='button3', size=(120,150))
self.tc1 = wx.TextCtrl(my_panel, style=wx.TE_MULTILINE | wx.HSCROLL)
self.tc2 = wx.TextCtrl(my_panel, style=wx.TE_MULTILINE | wx.HSCROLL)
self.tc3 = wx.TextCtrl(my_panel, style=wx.TE_MULTILINE | wx.HSCROLL)
fgs.AddMany([(button1),(self.tc1, 1, wx.EXPAND), (button2), \
(self.tc2, 1, wx.EXPAND), (button3), (self.tc3, 1, wx.EXPAND)])
fgs.AddGrowableRow(2, 1)
fgs.AddGrowableRow(1, 1)
fgs.AddGrowableRow(0, 1)
fgs.AddGrowableCol(1, 1)
hbox.Add(fgs, proportion=1, flag=wx.ALL|wx.EXPAND, border=40)
my_panel.SetSizer(hbox)
if __name__ == "__main__":
app = wx.App()
My_Gui(None, title='Example')
app.MainLoop()
Since wxWidgets 2.5.0, wxFlexGridSizer can also size items equally in one direction but unequally ("flexibly") in the other. If the sizer is only flexible in one direction (this can be changed using SetFlexibleDirection), it needs to be decided how the sizer should grow in the other ("non-flexible") direction in order to fill the available space. The SetNonFlexibleGrowMode method serves this purpose. (from: docs.wxwidgets.org)
So change:
fgs.SetFlexibleDirection( wx.BOTH )
Into:
fgs.SetFlexibleDirection( wx.HORIZONTAL )
fgs.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_ALL )
The only problem I see here is that the largest minimal size of the TextCtrls is applied to all the TextCtrls.
Not sure if I am asking this correctly (e.g. using widget/object in the correct context). This code is meant just as an experiment (learning about classes and wxPython right now) - so it might not make sense as functional code.
Problem: I have a window/frame that I am allowing the user to specify width/height, and there is a multiline text box that is nested inside the window/frame. Originally, I was setting the dimensions of the multiline text box to the same size of the parent frame, but the scroll bar was being hidden because the scroll bar of the text box is larger than the frame/text box size. So... I decided to just manually adjust the size of the text box to make sure the size of the text box accounted for the scroll bar (see code below, specifically lines "sizexTxt = sizex - 17" and "sizeyTxt = sizey - 44").
Question: Is there some sort of way for adjusting the text box other than manually adjusting pixel by pixel? Is there a suggestion someone can offer that would be considered pythonic?
import wx
class myFrame(wx.Frame):
def __init__(self, parent, title, sizexy, sizexyTxt):
wx.Frame.__init__(self, parent, title=title, size=sizexy)
self.Show(True)
wx.TextCtrl(self, style=wx.TE_MULTILINE, size=(sizexyTxt))
pass
sizex = input("X= ")
sizey = input("Y= ")
sizexy=(sizex, sizey)
sizexTxt = sizex - 17
sizeyTxt = sizey - 44
sizexyTxt = (sizexTxt, sizeyTxt)
myApp = wx.App(False)
frameNow = myFrame(None,"This is my title", sizexy, sizexyTxt)
myApp.MainLoop()
Jake pointed you in a right direction, but there is one more thing. You should be aware that first object which goes into the frame is wrapped into the basic sizer. The problem is that as far as I know you have no control of this sizer. So you have to add your own sizer, which you can use for frame fitting. You need the control over the sizer, because you would like the frame to change its size according to sizer.
import wx
class myFrame(wx.Frame):
def __init__(self, size, *arg, **kwarg):
wx.Frame.__init__(self, *arg, **kwarg)
self.text = wx.TextCtrl(self, style=wx.TE_MULTILINE, size=size)
self.sizer = wx.BoxSizer()
self.sizer.Add(self.text, proportion=1, flag=wx.EXPAND)
self.SetSizerAndFit(self.sizer)
self.Show()
myApp = wx.App(False)
size = []
for a in ["X", "Y"]:
d = wx.NumberEntryDialog(None, "Text size", "%s: " % a, "Window size entry", 100, 50, 200)
if d.ShowModal() == wx.ID_OK:
size.append(d.GetValue())
frameNow = myFrame(size, None)
myApp.MainLoop()
You'll want to use a sizer (or sizers) and then specify wx.EXPAND when adding to make it fit the sizer horizontally and wx.ALL to fit vertically.
Check out the sizer documentation here: http://wiki.wxpython.org/Getting%20Started#Sizers
Actually you might want to read the whole Working with Windows section, very useful.
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()
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:
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"
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...
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.
I use
width = 200 # panel width
txt = wx.StaticText(panel, label=text)
txt.Wrap(width)
This works great and the next widgets are positioned correctly. You can easily do the txt.Wrap(width) dynamically.
Why are you subclassing it? Do you need wordwrap? If so, there's a module for that in wx.lib.wordwrap that you can use.
In answer the the OP's comment, check this out:
import wx
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)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(txt, 1, wx.EXPAND, 5)
panel.SetSizer(sizer)
# Run the program
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()
I used the OP's comment for the text. Anyway, this works fine for me on Windows XP, Python 2.5 and wxPython 2.8.10.1.
I found what I think is a much easier and automatic way to handle this issue.
After creating the StaticText control, bind the control's wx.EVT_SIZE to a handler that calls the StaticText's Wrap() function with the event's GetSize()[0] as an argument (and then skips the event).
An example:
class MyDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent = parent, title = "Test Dialog", style = wx.CAPTION)
bigstr = "This is a really long string that is intended to test the wrapping functionality of the StaticText control in this dialog. If it works correctly, it should appear as multiple lines of text with a minimum of fuss."
self.__label__ = wx.StaticText(parent = self, label = bigstr)
self.__actionbutton__ = wx.Button(parent = self, label = "Go")
self.__label__.Bind(wx.EVT_SIZE, self.__WrapText__)
self.__actionbutton__.Bind(wx.EVT_BUTTON, self.__OnButton__)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.__label__, flag = wx.ALL | wx.EXPAND, border = 5)
sizer.Add(self.__actionbutton__, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.CENTER, border = 0)
self.SetSizer(sizer)
self.Layout()
def __OnButton__(self, event):
self.EndModal(wx.ID_OK)
def __WrapText__(self, event):
self.__label__.Wrap(event.GetSize()[0])
event.Skip()
This is what it looks like on my system (MSW, Python 2.7.5, wx 2.8.12.1):