Widget Windows in UltimateListCtrl Won't Resize/Refresh - python

My goal is to get a ULC that has a column of TextCtrls that will dynamically resize the row as the user types, so if there is a much better way, say so.
Here is what I've tried:
The ExpandoTextCtrl is exactly what I want. I have a working example, so I know I'm able to implement it correctly, I'm after a column of these:
import wx
import sys
import wx.lib.expando as ex
class TestPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
text = "\"I\'ll Be Missing You\" is a song recorded by American rapper Puff Daddy and American singer Faith Evans, featuring R&B group 112, in memory of fellow Bad Boy Records artist Christopher \"The Notorious B.I.G.\" Wallace, who was gunned down on March 9, 1997. --Wikipedia"
self.edit_text = ex.ExpandoTextCtrl(self, value = text, size = (200,50))
self.edit_text.SetMaxHeight(sys.maxint)
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="expando Demo")
panel = TestPanel(self)
self.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = TestFrame()
app.MainLoop()
As you add or remove lines it adjusts the height on the fly, for any character (not just the number of newlines or something simple like that).
If I add it to a cell in an UltimateListCtrl, it has a static size which is approximately column width and 2 lines of text visible, so it won't even instantiate showing the entire text, but it also won't resize either.
I have the same problem with other types of Windows is the list. I wrote a button that changes its size when you click it. This code of mine (I can post if you want but it feels redundant) runs perfectly in a panel of its own or in a Sizer with other widgets, etc, but in the ULC it will only instantiate at the original size and never changes with calls to button.SetSize().
I have researched bug reports for the ULC but haven't seen anything relevant and not fixed. I have tried calling the ULC's Refresh(), Update(), and Show(False/True) methods, and all of the above on the parent Panel and the Frame with no success.
Here is the code, based around a common example of the ULC online:
import wx
import sys
from wx.lib.agw import ultimatelistctrl as ULC
import wx.lib.expando as ex
class TestPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.sizes = self.size_gen()
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont.SetWeight(wx.BOLD)
boldfont.SetPointSize(12)
self.ultimateList = ULC.UltimateListCtrl(self, agwStyle = wx.LC_REPORT
| wx.LC_VRULES
| wx.LC_HRULES
| ULC.ULC_HAS_VARIABLE_ROW_HEIGHT)
info = ULC.UltimateListItem()
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT | ULC.ULC_MASK_CHECK
info._image = []
info._format = 0
info._kind = 1
info._text = "Artist Name"
self.ultimateList.InsertColumnInfo(0, info)
info = ULC.UltimateListItem()
info._format = wx.LIST_FORMAT_RIGHT
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT | ULC.ULC_MASK_FONT
info._image = []
info._text = "Title"
info._font = boldfont
self.ultimateList.InsertColumnInfo(1, info)
info = ULC.UltimateListItem()
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
info._format = 0
info._text = "Genre"
info._font = font
info._image = []
self.ultimateList.InsertColumnInfo(2, info)
self.ultimateList.InsertStringItem(0, "Newsboys")
self.ultimateList.SetStringItem(0, 1, "Go")
self.ultimateList.SetStringItem(0, 2, "Rock")
text = "\"I\'ll Be Missing You\" is a song recorded by American rapper Puff Daddy and American singer Faith Evans, featuring R&B group 112, in memory of fellow Bad Boy Records artist Christopher \"The Notorious B.I.G.\" Wallace, who was gunned down on March 9, 1997. --Wikipedia"
self.ultimateList.InsertStringItem(1, "Puffy")
edit_text = ex.ExpandoTextCtrl(self.ultimateList, value = text, size=(200,50))
edit_text.SetMaxHeight(sys.maxint)
self.ultimateList.SetItemWindow(1, col=1, wnd=edit_text, expand=True)
self.ultimateList.SetStringItem(1, 2, "Pop")
self.ultimateList.InsertStringItem(2, "Family Force 5")
self.button = wx.Button(self.ultimateList, label='button', size =(200,200))
self.button.Bind(wx.EVT_BUTTON, self.on_button)
self.ultimateList.SetItemWindow(2, 1, self.button, expand=True)
#self.ultimateList.SetStringItem(2, 1, "III")
self.ultimateList.SetStringItem(2, 2, "Crunk")
self.ultimateList.SetColumnWidth(0, 150)
self.ultimateList.SetColumnWidth(1, 200)
self.ultimateList.SetColumnWidth(2, 100)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.ultimateList, 1, wx.EXPAND)
self.SetSizer(sizer)
def on_button(self, event):
self.button.SetSize(self.sizes.next())
def size_gen(self):
sizes = [(150,200),(200,200),(80,80)]
index = 0
while True:
yield sizes[index]
index = index + 1
if index > 2:
index = 0
########################################################################
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="MvP UltimateListCtrl Demo")
panel = TestPanel(self)
self.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = TestFrame()
app.MainLoop()
EDIT
I've tried several more approaches now. I got rid of all the column formatting and replaced those 3 blocks with simplelist.InsertColumn(index, label) calls. The most useful thing I did was remove the expand=True from the ULC.SetItemWindow() call. This seems to have returned control of the button's width (but not height) to the button. Since it starts at 150Wx200H, when I call next and it changes to 200x200, the button overflows into the next cell to the right. When I call next again, commanding an 80x80 size, the button shrinks to 150x200, its unable to be set smaller than its initial size.
If I initialize the button smaller than I ever need, say 50x30, then I can set all the sizes (80,80;200,200) correctly in both dimensions, but the button then overflows into its neighbours to the right and below.
You can see that the list is not refreshing any of the rows.
You can also see that the widget maintains its original upper left corner position (maybe this is correct, but I don't think so)
If I add list.Refresh/DoLayout/Layout/Update to the on_button, it has no effect.
Dragging or repositioning the window (top level Frame) has no effect.
ULC.SendSizeEvent has no effect.
Next Idea
I also tried deleting the entire row and inserting a new button of new size, like so:
def on_button(self,event):
new_size = self.sizes.next()
print new_size
l = self.ultimateList
label = l.GetItemWindow(1, 1).GetLabel()
l.DeleteItem(1)
self.button = wx.Button(self.ultimateList, label=label, size=new_size)
self.button.Bind(wx.EVT_BUTTON, self.on_button)
l.InsertStringItem(1, 'Family Farce 5')
l.SetItemWindow(1,1,self.button)
l.SetStringItem(1,2,'Crunk')
I don't think this is an ok strategy, as destroying and rebuilding an edit_text on every keystroke sounds like it would have a whole lot of problems, but to be fair I haven't tried it yet.
Anyway, with the button I can call it and resizes correctly. The problem is that the ULC doesn't redraw subsequent rows based on the new height, or even the initial height, but the default height of a row. It does draw the recreated row with the button the correct height.
This time (deleting the line and adding a new line) I noticed resizing the window (top level Frame) forced the redraw, so I added SendSizeEvent to on_button and now the button works perfectly.
Apparently I've worked out how to replace a row with one of a new size.
So the question is still about Windows in a ULC, I can dynamically resize a widget, but how can I force the ULC to redraw itself after sizing a widget?
Another way to say it: Why does SendSizeEvent force a refresh after inserting a new item into the list, but not after modifying an existing item? I could subclass the ULC and extend a particular method or property if I knew which one to do. I've tried looking at the source but I can't make heads or tails of it.

First: ULC creates the line heights only once and if the height is already there when painting a line, it wont do it again.
Furthermore: When you add a new window, the size gets written into the line object. This will never be done again and cannot be triggered by any means manually.
However:
If you adjust UltimateListCtrl like this it will work.
Function GetWindowSize of class UltimateListItemData (line 2863):
def GetWindowSize(self):
""" Returns the associated window size. """
wnd = self._wnd
if wnd.GetSizer(): # the window is a complex one hold by a sizer
size = wnd.GetBestSize()
else: # simple window, without sizers
size = wnd.GetSize()
print(size)
return size
In your code you need to invalidate the old line heights, so it gets recalculated:
def on_button(self, event):
self.button.SetSize(self.sizes.next())
self.ultimateList._mainWin.ResetLineDimensions(True)
self.ultimateList._mainWin.RecalculatePositions()
Please note, that this modifies ULC and accesses private variables.
Lokla

Related

wxPython SetMinSize seems to set size explicitly, not allows window to grow when needed

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:

Python/wxWidgets: Vertically aligning text in a wrapped StaticText

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()

When its containing panel or frame becomes too small, wxListBox stops using its horizontal scrollbar and expands to take up too much space

SSCCE is at the bottom
My layout has two wx.ListBoxes, side-by side in a wx.FlexGridSizer:
My real layout is more complex, thus the FGS, but this small example still exhibits the problem.
As you can see above, I have successfully used style = wx.LB_HSCROLL to make each listbox use a horizontal scroll bar when one of its elements would make it too large to fit in the wx.Frame.
However, as I resize the window smaller and smaller, eventually some critical point is reached, the first listbox decides it doesn't want to use its scrollbar anymore, and instead expands to its full size, pushing the second box to the right:
The point at which the list goes crazy depends on how long the string is. If I put a long enough string in the first box, then the above process is reversed: the layout starts off wrong and I have to resize the window up to the critical point, where all of a sudden the listbox starts using its scrollbar, gets a lot smaller, and the window becomes split down the middle as it should be.
I'm not sure if this is a bug in wxWidgets/wxPython or if I'm doing something wrong, but it's frustrating either way. Here is the simplest code I can come up with that shows the problem:
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent = None, size = (640, 480))
self.list1 = wx.ListBox(self, style = wx.LB_HSCROLL)
self.list2 = wx.ListBox(self, style = wx.LB_HSCROLL)
self.list1.Append('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
self.list2.Append('bbbbbbbbbbb')
self.fgs = wx.FlexGridSizer(1, 2)
self.fgs.AddMany([(self.list1, 1, wx.EXPAND), (self.list2, 1, wx.EXPAND)])
self.fgs.AddGrowableRow(0, 1)
self.fgs.AddGrowableCol(0, 1)
self.fgs.AddGrowableCol(1, 1)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Sizer = fgs
self.Layout()
self.Show()
def Exit(self, event):
self.Close(True)
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
EDIT: Here is my implementation of ravenspoint's code in python (code above was changed slightly to support this):
def OnSize(self, event):
if not self.list1 or not self.list2;
return
clientRect = self.GetClientRect()
min = wx.Size(clientRect.width / 2, clientRect.height)
self.list1.MinSize = min
self.list2.MinSize = min
You may have to look after handling the resize event yourself. In C++ the handler would look something like this:
void MyFrame::OnSize(wxSizeEvent& )
{
if( ! ( list1 && list2 ) )
return;
wxRect frame_client = GetClientRect();
wxSize min(frame_client.width/2,frame_client.height );
list1->SetMinSize(min);
list2->SetMinSize(min);
fgs->Layout();
}
Since I ran into this problem as well, I'll post this for future reference. At least with wxPython, you must specify a min size or it will use the best size (at least for list boxes) and the sizer could possibly shove widgets outside of the current window frame. Thus, the added code to get the above code working as desired is:
self.list1.SetMinSize((10,10));
self.list2.SetMinSize((10,10));
The total working answer is:
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent = None, size = (640, 480))
self.list1 = wx.ListBox(self, style = wx.LB_HSCROLL)
self.list2 = wx.ListBox(self, style = wx.LB_HSCROLL)
self.list1.Append('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
self.list2.Append('bbbbbbbbbbb')
self.fgs = wx.FlexGridSizer(1, 2)
self.fgs.AddMany([(self.list1, 1, wx.EXPAND), (self.list2, 1, wx.EXPAND)])
self.fgs.AddGrowableRow(0, 1)
self.fgs.AddGrowableCol(0, 1)
self.fgs.AddGrowableCol(1, 1)
# Don't forget these two lines to allow for correct
# expansion/contraction of the sizer!
self.list1.SetMinSize((10,10));
self.list2.SetMinSize((10,10));
self.Sizer = self.fgs
self.Layout()
self.Show()
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
app.MainLoop()

Issue with wxPython mulltiline text box being too large for wx.Frame - can I auto "shrink" to the correct size of the text box?

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.

Python+Qt, QScrollArea problem: what's wrong with this code?

I have the following code:
#!/usr/bin/env python
import sys
from PyQt4 import QtGui, QtCore
class SimfilePanel(QtGui.QWidget):
'''This class provides the simfile panel shown on the right side of the main window.'''
def __init__(self, parent=None):
'''Load song info here.'''
QtGui.QWidget.__init__(self, parent)
## Make widgets.
# Pane with simfile information.
simfileInfoPane = QtGui.QWidget()
simfileInfoPane.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
simfileInfoGrid = QtGui.QGridLayout()
simfileInfoPane.setLayout(simfileInfoGrid)
simfileInfoScrollArea = QtGui.QScrollArea()
simfileInfoScrollArea.setWidget(simfileInfoPane)
#if DEBUG: simfileInfoScrollArea.setBackgroundRole(QtGui.QPalette.Dark);
# This will change
labels = []
textfields = []
for i in range(0,20):
labels.append( QtGui.QLabel("Label "+str(i)) )
textfields.append( QtGui.QLineEdit() )
labels[i].setBuddy(textfields[i])
simfileInfoGrid.addWidget(labels[i], i, 0)
simfileInfoGrid.addWidget(textfields[i], i, 1)
## Put widgets in a grid layout.
mainvbox = QtGui.QVBoxLayout()
mainvbox.addWidget(simfileInfoScrollArea)
self.setLayout(mainvbox)
# Standalone testing
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
panel = SimfilePanel()
panel.show()
sys.exit(app.exec_())
I can't get anything that I'm putting into the simfileInfoGrid to display! They'll display if I leave out the scroll area, but I need the scroll area as I will have a lot of fields to edit in the final version and I don't want to stretch the entire window over the screen.
As you see I've tried to add a size policy to simfileInfoPane, but it doesn't seem to affect anything. The area that's supposed to contain my pane stays empty!
Add the pane to the scroll area after you've added all the grid's contents. In particular you need to call QScrollArea.setWidget after you have finished creating the widget you add.
I don't know exactly why this is the problem, but I do know that I tend to initialize widgets "bottom-up": I finish adding all the contents of a sub-layout before I ever add it to a parent layout. I believe this is Qt optimizing order of rendering but I could be wrong about that.
The code below is a patch, mostly so you can see where the one-line change is.
diff -u 1848547.py tmp2.py
--- 1848547.py 2009-12-04 11:19:09.000000000 -0800
+++ tmp2.py 2009-12-04 11:34:58.000000000 -0800
## -19,7 +19,6 ##
simfileInfoPane.setLayout(simfileInfoGrid)
simfileInfoScrollArea = QtGui.QScrollArea()
- simfileInfoScrollArea.setWidget(simfileInfoPane)
#if DEBUG:
simfileInfoScrollArea.setBackgroundRole(QtGui.QPalette.Dark)
## -33,6 +32,8 ##
simfileInfoGrid.addWidget(labels[i], i, 0)
simfileInfoGrid.addWidget(textfields[i], i, 1)
+ simfileInfoScrollArea.setWidget(simfileInfoPane)
+
## Put widgets in a grid layout.
mainvbox = QtGui.QVBoxLayout()
mainvbox.addWidget(simfileInfoScrollArea)
I have recently been struggling with the same thing, and I believe I have found the solution you are looking for.
The problem seems to be that when you add an empty widget to the scroll area, it has dimensions of zero by zero (since there is nothing inside of it).
The reason it doesn't get bigger is because there is a flag called widgetResizable that is False by default.
By simply calling setWidgetResizable(True) on the scroll area, the widget gets bigger as new items are added.
I hope this helps.

Categories