I want to limit the size of a list control box. Let us take the following code:
import wx
class Students(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(380, 230))
hbox = wx.BoxSizer(wx.HORIZONTAL)
panel = wx.Panel(self, -1)
self.list = wx.ListCtrl(panel, -1, style=wx.LC_REPORT)
self.list.InsertColumn(0, 'name')
self.list.InsertColumn(1, 'age')
hbox.Add(self.list, 1,wx.EXPAND)
panel.SetSizer(hbox)
self.Centre()
self.Show(True)
app = wx.App()
Students(None, -1, 'studs')
app.MainLoop()
If I make the horizontal box sizer's proportion=0, like this:
hbox.Add(self.list, 0,wx.EXPAND)
then there is a different problem. The problem with proportion=1 is that after 'Age' column, there is a lot of empty space the list control box is eating up for the third column which doesn't exist.
The problem with proportion=0 is that it is too short in width.
I want to display the list control box with 'Name' and 'Age' columns only and save the rest of the space. How do I do that?
You can set the width of your list control manually and then set the proportion to 0.
The width of columns can be set manually:
import wx
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.panel.SetBackgroundColour(wx.GREEN)
self.list = wx.ListCtrl(self, style=wx.LC_REPORT, size=(200, -1))
column_size = self.list.GetSize()[0] / 2 - 2
self.list.InsertColumn(0, 'Name')
self.list.InsertColumn(1, 'Age')
self.list.SetColumnWidth(0, column_size)
self.list.SetColumnWidth(1, column_size)
self.sizer = wx.BoxSizer()
self.sizer.Add(self.list, proportion=0, flag=wx.EXPAND)
self.sizer.Add(self.panel, proportion=1, flag=wx.EXPAND)
self.SetSizerAndFit(self.sizer)
self.SetSize((600, 400))
self.Show()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
Or the ListCtrlAutoWidthMixin may be used:
import wx
import wx.lib.mixins.listctrl as listmix
class TestListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
def __init__(self, *args, **kwargs):
wx.ListCtrl.__init__(self, *args, **kwargs)
listmix.ListCtrlAutoWidthMixin.__init__(self)
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.panel.SetBackgroundColour(wx.GREEN)
self.list = TestListCtrl(self, style=wx.LC_REPORT, size=(200, -1))
self.list.InsertColumn(0, 'Name')
self.list.InsertColumn(1, 'Age')
self.sizer = wx.BoxSizer()
self.sizer.Add(self.list, proportion=0, flag=wx.EXPAND)
self.sizer.Add(self.panel, proportion=1, flag=wx.EXPAND)
self.SetSizerAndFit(self.sizer)
self.SetSize((600, 400))
self.Show()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
Don't use wx.EXPAND if you don't want the control to resize. You could also put a spacer into the horizontal sizer right after the list control and make it expand instead. Then the spacer will take up all the available space.
Related
I'm a newbie in wxpython and have been trying to put some widgets within books(treebook, notebook, choicebook) I have often ended up with some widgets placed inside containers not responding to events. I'm not sure what I'm doing wrong. Below is one of my code
import wx
class ChoicePanelTwo(wx.Panel):
def __init__(self, parent, seed):
wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
self.SetBackgroundColour('blue')
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.List = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
for i in range(seed):
self.List.InsertStringItem(i, str(i))
sizer.Add(self.List, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer)
class ChoicePanelOne(wx.Panel):
def __init__(self, parent, seed):
wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
self.SetBackgroundColour('green')
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.RegisterList = wx.Choicebook(self, wx.ID_ANY)
sizer.Add(self.RegisterList, 1, wx.ALL|wx.EXPAND, 5)
for i in range(seed):
self.RegisterList.AddPage(ChoicePanelTwo(self, seed*50), str(i))
self.SetSizer(sizer)
class TreePanel(wx.Panel):
def __init__(self, parent, seed):
wx.Panel.__init__(self, parent, id=wx.ID_ANY)
self.SetBackgroundColour('cyan')
self.Choicbook = wx.Choicebook(self, wx.ID_ANY)
for i in range(seed):
self.Choicbook.AddPage(ChoicePanelOne(self, seed*2), str(i))
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.Choicbook, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer)
class AppFrame(wx.Frame):
""" The main frame of the application
"""
title = 'Application'
WindowSize = (1024, 768)
seed = 2
def __init__(self):
wx.Frame.__init__(self, None, -1, self.title, size=self.WindowSize, style=wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.CAPTION|wx.CLOSE_BOX|wx.CLIP_CHILDREN)
self.create_main_panel()
def create_main_panel(self):
self.panel = TreePanel(self, self.seed)
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = AppFrame()
app.frame.Show()
app.MainLoop()
In this example. The choice book and list does seem to be working. What is that I'm doing wrong ?
It's a parenting issue. You are setting all the ChoicePanelOne instance's parents as the TreePanel when it should be the ChoicBook. And you're doing the same thing in ChoicePanelOne where you create a whole bunch of ChoicePanelTwo's whose parents are ChoicePanelOne when they should be RegisterList. Check out the slightly changed code below:
import wx
class ChoicePanelTwo(wx.Panel):
def __init__(self, parent, seed):
wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
self.SetBackgroundColour('blue')
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.List = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
for i in range(seed):
self.List.InsertStringItem(i, str(i))
sizer.Add(self.List, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer)
class ChoicePanelOne(wx.Panel):
def __init__(self, parent, seed):
wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
self.SetBackgroundColour('green')
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.RegisterList = wx.Choicebook(self, wx.ID_ANY)
sizer.Add(self.RegisterList, 1, wx.ALL|wx.EXPAND, 5)
for i in range(seed):
self.RegisterList.AddPage(ChoicePanelTwo(self.RegisterList, seed*50), str(i))
self.SetSizer(sizer)
class TreePanel(wx.Panel):
def __init__(self, parent, seed):
wx.Panel.__init__(self, parent, id=wx.ID_ANY)
self.SetBackgroundColour('cyan')
self.Choicbook = wx.Choicebook(self, wx.ID_ANY)
for i in range(seed):
self.Choicbook.AddPage(ChoicePanelOne(self.Choicbook, seed*2), str(i))
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.Choicbook, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer)
class AppFrame(wx.Frame):
""" The main frame of the application
"""
title = 'Application'
WindowSize = (1024, 768)
seed = 2
def __init__(self):
wx.Frame.__init__(self, None, -1, self.title, size=self.WindowSize,
style=wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.CAPTION|wx.CLOSE_BOX|wx.CLIP_CHILDREN)
self.create_main_panel()
def create_main_panel(self):
self.panel = TreePanel(self, self.seed)
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = AppFrame()
app.frame.Show()
app.MainLoop()
You should download the wxPython demo as it has lots of good examples in it. Also see the wiki or my blog article.
You could use the Widget Inspection Tool to help you diagnose the problem as well as it will show you what parent is where and how your sizers are laid out, among other things.
So I have the following code set up to demonstrate the problem:
import wx
class testPanel(wx.Panel):
def __init__(self, parent):
super(testPanel, self).__init__(parent)
self.hsizer = wx.BoxSizer(wx.HORIZONTAL)
self.txt = wx.TextCtrl(self, style=wx.TE_MULTILINE)
self.hsizer.Add(self.txt, proportion=1,
flag=wx.EXPAND)
self.SetSizer(self.hsizer)
self.hsizer.Fit(self)
self.Show(True)
class testFrame(wx.Frame):
def __init__(self, parent):
super(testFrame, self).__init__(parent)
self.mainPanel = wx.Panel(self)
self.vsizer = wx.BoxSizer(wx.VERTICAL)
self.txt1 = testPanel(self)
self.txt2 = testPanel(self)
self.vsizer.Add(self.txt1, proportion=1,
flag=wx.EXPAND)
self.vsizer.Add(self.txt2, proportion=1,
flag=wx.EXPAND)
self.mainPanel.SetSizer(self.vsizer)
self.vsizer.Fit(self.mainPanel)
self.mainSizer = wx.BoxSizer(wx.HORIZONTAL)
self.mainSizer.Add(self.mainPanel, proportion=1,
flag=wx.EXPAND)
self.SetSizer(self.mainSizer)
self.mainSizer.Fit(self)
self.Show(True)
app = wx.PySimpleApp()
frame = testFrame(None)
frame.Show(True)
app.MainLoop()
When this is run, everything displays as expected, but the two wx.TextCtrls won't receive focus ever. This isn't the case when the extra layer of panel is removed, but I can't avoid having that extra panel.
Use:
self.txt1 = testPanel(self.mainPanel)
self.txt2 = testPanel(self.mainPanel)
and they will get focus.
In wxPython things go better when you design a straight line of inheritance, with branches but without crossovers. As your testPanels are in a sizer that belongs to mainPanel, the natural parent is not the Frame (self) but mainPanel,
I'm having two problems with my program. The code below generates two panels when it should generate 3. It generates panel1 and panel2 no problem but panel3 should be to the right of panel2 is no where to be seen. Panel1 and panel2 are split vertically I'm trying to do the same with panel2 and panel3
My second problem is how do I generate another panel below panel1 without disrupting the splitter between panel1 and panel2 and then create a splitter between panel1 and new created panel below it?
import wx
class Panels(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title)
hbox = wx.BoxSizer(wx.HORIZONTAL)
splitter = wx.SplitterWindow(self, -1)
vbox1 = wx.BoxSizer(wx.VERTICAL)
panel1 = wx.Panel(splitter, -1)
panel12 = wx.Panel(panel1, -1, style=wx.BORDER_SUNKEN)
st1 = wx.StaticText(panel12, -1, 'Panel 1', (5, 5))
vbox1.Add(panel12, 1, wx.EXPAND)
panel1.SetSizer(vbox1)
vbox2 = wx.BoxSizer(wx.VERTICAL)
panel2 = wx.Panel(splitter, -1)
panel22 = wx.Panel(panel2, -1, style=wx.BORDER_RAISED)
st2 = wx.StaticText(panel22, -1, 'Panel 2', (5, 5))
vbox2.Add(panel22, 1, wx.EXPAND)
panel2.SetSizer(vbox2)
vbox3 = wx.BoxSizer(wx.VERTICAL)
panel3 = wx.Panel(splitter, -1)
panel32 = wx.Panel(panel3, -1, style=wx.BORDER_RAISED)
st3 = wx.StaticText(panel32, -1, 'Panel 3', (5, 5))
vbox3.Add(panel32, 1, wx.EXPAND)
panel3.SetSizer(vbox3)
hbox.Add(splitter, 1, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
self.SetSizer(hbox)
self.CreateStatusBar()
splitter.SplitVertically(panel1, panel3)
splitter.SplitVertically(panel2, panel3)
self.Centre()
self.Show(True)
def ExitApp(self, event):
self.Close()
app = wx.App()
Panels(None, -1, 'Panels')
app.MainLoop()
You have a couple options. You could nest your SplitterWindows, which is kind of confusing, but effective. Or you could use the MultiSplitterWindow widget.
For the first one, I'd do something like this:
Create a main splitter and a sub-splitter
In the sub-splitter, but the first two panels
Put the sub-splitter and the 3rd panel in the main splitter
Or some variation thereof.
EDIT: Here's one example:
import wx
########################################################################
class RandomPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent, color):
"""Constructor"""
wx.Panel.__init__(self, parent)
self.SetBackgroundColour(color)
########################################################################
class MainPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
topSplitter = wx.SplitterWindow(self)
hSplitter = wx.SplitterWindow(topSplitter)
panelOne = RandomPanel(hSplitter, "blue")
panelTwo = RandomPanel(hSplitter, "red")
hSplitter.SplitVertically(panelOne, panelTwo)
hSplitter.SetSashGravity(0.5)
panelThree = RandomPanel(topSplitter, "green")
topSplitter.SplitHorizontally(hSplitter, panelThree)
topSplitter.SetSashGravity(0.5)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(topSplitter, 1, wx.EXPAND)
self.SetSizer(sizer)
########################################################################
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Nested Splitters",
size=(800,600))
panel = MainPanel(self)
self.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
See also might want to look at wx.SashLayoutWindow: http://www.wxpython.org/docs/api/wx.SashLayoutWindow-class.html
Problem 1:
Create a horzontal vbox sizer. Add the sizers for panel2 and panel3 to that. This will put the panel2 and panel3 next to each other. Then add the horizontal sizer to the splitter as the second item.
Problem 2:
Create another splitter and add to a sizer (I assume splitter is a widget like panel). Then create your other panel and add it to that splitter. So you end up nesting the splitter inside a sizer. (May not need sizer for that though.) Then that sizer is inside the main splitter. You may be able to add the second splitter inside the first.
i have a wxpython listbox and its height set to -1, which would mean that it would automatically set to the height of the window. Is there a way to leave a 200px at the end of the window?
So basicilly i guess what i am looking for is (window_height - 200), to leave a 200 space.
-1 would go the full window height.
Thanks.
EDIT: I cant seem to get it to work with my code;
COLORS = ["red", "blue", "black", "yellow", "green"]
NUMBERS = ['0', '1', '2', '3', '4']
image=[];
import random
import wx
class images_tab(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
## random test useless t = wx.StaticText(self, -1, "This is a PageOne object", (20,20))
self.listBox = wx.ListBox(self, size=(200, -1), choices=image, style=wx.LB_SINGLE)
# self.button = wx.Button(self, label="Something else here? Maybe!")
self.sizer = wx.BoxSizer()
self.sizer.Add(self.listBox, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)
# self.sizer.Add(self.button, proportion=1, flag=wx.ALL)
self.SetSizer(self.sizer)
class MyNotebook(wx.Notebook):
def __init__(self, *args, **kwargs):
wx.Notebook.__init__(self, *args, **kwargs)
class MyPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.notebook = MyNotebook(self, size=(220, -1))
Images__tab = images_tab(self.notebook)
# add the pages to the notebook with the label to show on the tab
self.notebook.AddPage(Images__tab, "Click here to lookat pictures")
self.sizer = wx.BoxSizer()
self.sizer.Add(self.notebook, proportion=0, flag=wx.EXPAND)
#self.sizer.Add(self.button, proportion=0) #with button
self.SetSizer(self.sizer)
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.SetTitle("Python: Pictures")
self.panel = MyPanel(self)
app.frame = wx.Frame(parent=None, id=-1, size=(300,400))
self.Show()
app = wx.App(False)
win = MainWindow(None, size=(600, 400))
app.MainLoop()
Put it in a sizer: mysizer.Add(myListBox, 0, wx.BOTTOM, 200)
EDIT: Or you could use wx.GetDisplaySize() to get the width and height of the window, subtract 200 from the height and use that to set the size of the listbox.
I would like to be able to display Notebook and a TxtCtrl wx widgets in a single frame. Below is an example adapted from the wxpython wiki; is it possible to change their layout (maybe with something like wx.SplitterWindow) to display the text box below the Notebook in the same frame?
import wx
import wx.lib.sheet as sheet
class MySheet(sheet.CSheet):
def __init__(self, parent):
sheet.CSheet.__init__(self, parent)
self.SetLabelBackgroundColour('#CCFF66')
self.SetNumberRows(50)
self.SetNumberCols(50)
class Notebook(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(600, 600))
menubar = wx.MenuBar()
file = wx.Menu()
file.Append(101, 'Quit', '' )
menubar.Append(file, "&File")
self.SetMenuBar(menubar)
wx.EVT_MENU(self, 101, self.OnQuit)
nb = wx.Notebook(self, -1, style=wx.NB_BOTTOM)
self.sheet1 = MySheet(nb)
self.sheet2 = MySheet(nb)
self.sheet3 = MySheet(nb)
nb.AddPage(self.sheet1, "Sheet1")
nb.AddPage(self.sheet2, "Sheet2")
nb.AddPage(self.sheet3, "Sheet3")
self.sheet1.SetFocus()
self.StatusBar()
def StatusBar(self):
self.statusbar = self.CreateStatusBar()
def OnQuit(self, event):
self.Close()
class MyFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(450, 400))
self.text = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE)
self.Center()
class MyApp(wx.App):
def OnInit(self):
frame = Notebook(None, -1, 'notebook.py')
frame.Show(True)
frame.Center()
frame2 = MyFrame(None, -1, '')
frame2.Show(True)
self.SetTopWindow(frame2)
return True
app = MyApp(0)
app.MainLoop()
Making two widgets appear on the same frame is easy, actually. You should use sizers to accomplish this.
In your example, you can change your Notebook class implementation to something like this:
class Notebook(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(600, 600))
menubar = wx.MenuBar()
file = wx.Menu()
file.Append(101, 'Quit', '' )
menubar.Append(file, "&File")
self.SetMenuBar(menubar)
wx.EVT_MENU(self, 101, self.OnQuit)
nb = wx.Notebook(self, -1, style=wx.NB_BOTTOM)
self.sheet1 = MySheet(nb)
self.sheet2 = MySheet(nb)
self.sheet3 = MySheet(nb)
nb.AddPage(self.sheet1, "Sheet1")
nb.AddPage(self.sheet2, "Sheet2")
nb.AddPage(self.sheet3, "Sheet3")
self.sheet1.SetFocus()
self.StatusBar()
# new code begins here:
# add your text ctrl:
self.text = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE)
# create a new sizer for both controls:
sizer = wx.BoxSizer(wx.VERTICAL)
# add notebook first, with size factor 2:
sizer.Add(nb, 2)
# then text, size factor 1, maximized
sizer.Add(self.text, 1, wx.EXPAND)
# assign the sizer to Frame:
self.SetSizerAndFit(sizer)
Only the __init__ method is changed. Note that you can manipulate the proportions between the notebook and text control by changing the second argument of the Add method.
You can learn more about sizers from the official Sizer overview article.
You can use a splitter, yes.
Also, it makes sense to create a Panel, place your widgets in it (with sizers), and add this panel to the Frame.