wxpython wx.Notebook does not get any width - python

I am trying to create a project tool with a wx.Notebook with tabs at the top and a general panel at the bottom. The bottom panel should be independent of the notebook and not change when the tabs change. When I only add the notebook the notebook itself works fine, but when I also add my bottom panel (an extended wx.Panel) the notebook gets squeezed like shown in the image here.
I have one panel for the window and add the notebook and bottom panel (called BottomGroup, extending wx.Panel) to separate panels. Can anyone spot the trouble? Maybe something with the sizers? My Window is like this (nevermind that the tabbing is wrong):
class Window(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, None, wx.ID_ANY, title)
self.InitUI()
def InitUI(self):
menuBar = wx.MenuBar()
menu = wx.Menu()
menu_load = menu.Append(wx.ID_OPEN, 'Open', 'Open project')
menu_save = menu.Append(wx.ID_SAVE, 'Save', 'Save project')
menu_save_as = menu.Append(wx.ID_SAVEAS, 'Save as', 'Save project as')
menu_exit = menu.Append(wx.ID_EXIT, 'Quit', 'Quit application')
menuBar.Append(menu, '&File')
self.SetMenuBar(menuBar)
mainPanel = wx.Panel(self)
self.noteBookPanel = wx.Panel(mainPanel)
self.notebook = wx.Notebook(self.noteBookPanel)
self.bottomPanel = wx.Panel(mainPanel)
self.bottomGroup = BottomGroup(self.bottomPanel)
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.pageNodePathsTables = PageNodesPathsTables(self.notebook)
self.pageEscapeCriteria = PageEscapeCriteria(self.notebook)
self.pageFileHandling = PageFileHandling(self.notebook)
self.notebook.AddPage(self.pageNodePathsTables, "Define Paths and Nodes")
self.notebook.AddPage(self.pageEscapeCriteria, "Define Escape Criteria")
self.notebook.AddPage(self.pageFileHandling, "File Handling")
mainSizer.Add(self.noteBookPanel,1,wx.TOP)
mainSizer.Add(self.bottomGroup,1,wx.BOTTOM)
self.Bind(wx.EVT_MENU, self.onSave, menu_save)
self.Bind(wx.EVT_MENU, self.onLoad, menu_load)
self.Bind(wx.EVT_MENU, self.OnQuit, menu_exit)
self.SetDimensions(WindowOpenX,WindowOpenY,WindowWidth,WindowHeight)
self.Show(True)
Update:
I have refactored my code to this (only the refactored part shown):
self.notebook = wx.Notebook(self)
self.bottomGroup = BottomGroup(self)
self.setupMenu()
#frameSizer = wx.GridBagSizer(rowGap,columnGap)
#frameSizer.Add(self.notebook,pos=(0,0), span=(1,1),
# flag=wx.LEFT|wx.TOP|wx.EXPAND, border = 5)
#frameSizer.Add(self.bottomGroup,pos=(1,0), span=(1,1),
# flag=wx.LEFT|wx.BOTTOM|wx.EXPAND, border = 5)
frameSizer = wx.BoxSizer(wx.VERTICAL)
frameSizer.Add(self.notebook, 2, wx.EXPAND)
frameSizer.Add(self.bottomGroup,0)
self.SetSizer(frameSizer)
where self.setupMenu() is defined as:
def setupMenu(self):
self.pageNodePathsTables = PageNodesPathsTables(self.notebook)
self.pageEscapeCriteria = PageEscapeCriteria(self.notebook)
self.pageFileHandling = PageFileHandling(self.notebook)
self.pagePlotHistoryData = PagePlotHistoryData(self.notebook)
self.pageCalculateEscape = PageCalculateEscape(self.notebook)
self.notebook.AddPage(self.pageNodePathsTables, "Define Paths and Nodes")
self.notebook.AddPage(self.pageEscapeCriteria, "Define Escape Criteria")
self.notebook.AddPage(self.pageFileHandling, "File Handling")
self.notebook.AddPage(self.pagePlotHistoryData, "Plot History Data")
self.notebook.AddPage(self.pageCalculateEscape, "Calculate Escape")
This is a lot clearer and easier than the above code. It works fine, except that the bottomGroup is now stacked upon the notebook (i.e. both elements start at the upper left corner of the wx.Frame). I have tried both the wx.BoxSizer and the wx.GridBagLayout (as commented out above). Do you have any suggestions to this problem?
My BottomGroup is defined like this:
class BottomGroup(wx.Panel):
def __init__(self,parent):
wx.Panel.__init__(self,parent)
panelSizer = wx.GridBagSizer(rowGap,columnGap)
btnSaveProject = wx.Button(parent, label="Save project", size=(100,50))
btnLoadProject = wx.Button(parent, label="Open project", size=(100,50))
panelSizer.Add(btnSaveProject, pos=(0,0), span=(1,1),
flag=wx.EXPAND|wx.LEFT, border = borderWidth)
panelSizer.Add(btnLoadProject, pos=(0,1), span=(1,1),
flag=wx.EXPAND|wx.LEFT, border = borderWidth)
self.SetSizer(panelSizer)
My main method is like this:
if __name__ == '__main__':
app = wx.App()
Window(None, WindowHeader)
app.MainLoop()

I don't want to provide you with a full solution, but rather a little advice on UI programming.
You screwed up by creating to many panels and never setting a sizer. Before writing UI code always think of the structure (hierarchy) of widgets you want to create. Think of their layout. Then write down the code, grouped by principle of locality: approximately one level of the hierarchy should be handled in a few lines.
Good, let's apply the principle. Notebook and bottom panel at top level:
wx.Frame
+- wx.NoteBook
+- PageNodesPathsTables
+- ...
+- BottomGroup
At this level, everything comes down to these four, simple lines:
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.notebook, 2, wx.EXPAND)
sizer.Add(self.bottomPanel, 0)
self.SetSizer(sizer)
Obviously you know how to handle a wx.NoteBook properly. Very important is the call to self.SetSizer (non-existent in your code), because otherwise the frame has no idea which sizer it should use for layouting its children.
You demands about the partitioning in two halves where pretty clear. Words of wisdom: Don't do more nesting than needed. This might be more extensible (and can easily be postponed), but at the moment it's not needed. If you do something like this:
self.bottomPanel = wx.Panel(mainPanel)
self.bottomGroup = BottomGroup(self.bottomPanel)
You have introduced at least one level (self.bottomPanel) with a single child (self.bottomGroup) that has no clue how to layout its child components. Erase the unnecessary nesting, and you will get rid of panel creation, sizer creation, sizer assignment, and so on. Collapsing it will get you a quicker overview. Todo:
Rewrite this section of you program. Get rid of superfluous instructions.
Make sure every panel has a sizer.
Refactor menu creation in a separate method.
Parameter parent in the Window constructor is unused. Fix.
Your example was missing a main method, imports, etc. Next time, supply an SSCCE.

Related

How to create an info icon with wxPython

I so far failed to create what is colloquially called an "info icon" with wxPython. An icon with some sort of 'i' image that shows a large tooltip on hover.
I can add a wx.StaticBitmap for the image but it ignores all SetToolTipString or SetToolTip(wx.ToolTip()) calls. OR I can add a large tool tip to a wx.StaticText as shown below.
Ignore that the icon doesn't have the correct size yet.
Needless to say that eventually the tooltip needs a background color that is different from the panel background color (not the focus here). I can't use wx.adv.RichToolTip because I'm on wxPython 3.0.2.0 osx-cocoa.
What is a good way to solve this?
If you create a button with an ID of wx.ID_HELP then you'll get the stock help button for the platform, if it has one. Then you can do whatever you want with it like any button. Assign a tooltip, do something in the EVT_BUTTON event, etc. See the StockButtons sample in the demo. If the stock image or label doesn't meet your needs then you can probably just use a wx.BitmapButton to show the image you want and still have the standard tooltip support.
Something else you may want to look into is the ContextHelp sample in the demo. It shows how to use a wx.ContextHelpButton which, when clicked, puts the application into context-help mode. A popup tip window will then be shown for whatever widget is clicked on next. Not quite what you are asking for, but it might be a good fit.
wxArtProvider may be able to help http://docs.wxwidgets.org/trunk/classwx_art_provider.html
import wx
class Test(wx.Frame):
def __init__(self,parent,msg,title):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self, size=(300,400))
mainSizer = wx.BoxSizer(wx.HORIZONTAL)
staticIcon = wx.BitmapButton(self.panel, bitmap=wx.ArtProvider.GetBitmap(wx.ART_WARNING), size=(32,32))
mainSizer.Add(staticIcon, flag=wx.ALL, border=10)
ttip = "xxxxxxxxxxxxxxx\n"
ttip += "xxxxxxxxxxxxxxxxxxxxxxxxxx\n"
ttip += "xxxxxxxxxxxxxxxxxxxxxxxxxxx\n"
ttip += "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
staticIcon.SetToolTipString(ttip)
buttonText = wx.StaticText(self.panel, -1, msg, wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(buttonText, flag=wx.ALL, border=10)
staticIcon.Bind(wx.EVT_BUTTON, self.OnButton)
self.SetSizer(mainSizer)
self.Show()
def OnButton(self, evt):
print "The button was pressed - display some help"
if __name__ == '__main__':
app = wx.App()
Test(None, "Dummy Exercise", "Test 123")
app.MainLoop()
If all you want to do is show a tooltip when the image is moused over, then you need to bind your instance of the wx.StaticBitmap to EVT_MOTION:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
bmp = wx.ArtProvider.GetBitmap(wx.ART_WARNING)
self.image = wx.StaticBitmap(self, bitmap=bmp)
self.image.Bind(wx.EVT_MOTION, self.on_mouse_over)
def on_mouse_over(self, event):
self.image.SetToolTipString('BLAH BLAH BLAH')
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Icon Mouser')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
When I run this code, I get something like this:

wxPython : Issues on windows using multiple sizers, but works well on linux

I am writing a small app that works very well on linux, but I have some trouble on windows. Here is the code sample:
import wx
#####################################################################
class Main(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="StackOverflow", pos=wx.DefaultPosition, size=(800,600))
self.SetMinSize( self.GetSize() )
p = wx.Panel(self)
nb = wx.Notebook(p)
page1 = AddToCollection(nb)
page2 = CollectionStatistics(nb)
nb.AddPage(page1, "Page 1")
nb.AddPage(page2, "Page 2")
# finally, put the notebook in a sizer for the panel to manage
# the layout
sizer = wx.BoxSizer()
sizer.Add(nb, 1, wx.EXPAND)
p.SetSizer(sizer)
#########################################################################
class CollectionStatistics(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
#########################################################################
class AddToCollection(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.v1_qty_list = [str(x) for x in range(9)]
self.v2_qty_list = [str(x) for x in range(9)]
self.sizername = wx.GridBagSizer(5, 5)
self.sizername.AddGrowableCol(0,0)
self.name_txt = wx.StaticText(self, label="Enter Name :")
self.sizername.Add(self.name_txt,(2,0),(1,1),wx.EXPAND)
self.name = wx.TextCtrl(self,style=wx.TE_PROCESS_ENTER,value=u"")
self.sizername.Add(self.name,(3,0),(1,1),wx.EXPAND)
self.Bind(wx.EVT_TEXT_ENTER, self.OnPressEnter, self.name)
self.SetSizerAndFit(self.sizername)
self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y )
##########################################################################
def OnPressEnter(self,event):
self.selected_name = self.name.GetValue()
self.AddToCol()
##########################################################################
def AddToCol(self):
self.sizerAdd = wx.GridBagSizer(5, 5)
self.sizerAdd.AddGrowableCol(0, 0)
self.name.Enable(False)
### Expansion
self.expansion = wx.Choice(self, -1, choices=['test 1', 'test 2'])
self.expansion.SetSelection(0)
self.sizerAdd.Add(self.expansion,(5,0),(1,6),wx.EXPAND)
### Quantities txt
self.v1_txt = wx.StaticText(self, label="V1 Quantity :")
self.sizerAdd.Add(self.v1_txt,(7,0),(1,1),wx.EXPAND)
self.v2_txt = wx.StaticText(self, label="V2 Quantity :")
self.sizerAdd.Add(self.v2_txt,(8,0),(1,1),wx.EXPAND)
### Quantities choices
self.v1_qty = wx.Choice(self, -1, choices=self.v1_qty_list)
self.v1_qty.SetSelection(0)
self.sizerAdd.Add(self.v1_qty,(7,5),(1,1),wx.EXPAND)
self.v2_qty = wx.Choice(self, -1, choices=self.v1_qty_list)
self.v2_qty.SetSelection(0)
self.sizerAdd.Add(self.v2_qty,(8,5),(1,1),wx.EXPAND)
### Ok Button
self.Add_btn = wx.Button(self, -1, "Add")
self.Add_btn.Bind(wx.EVT_BUTTON, self.OnAdd)
self.sizerAdd.Add(self.Add_btn,(9,5),(1,1),wx.EXPAND)
### Reset Button
self.Reset_btn = wx.Button(self, -1, "Reset")
self.Reset_btn.Bind(wx.EVT_BUTTON, self.OnResetPanel)
self.sizerAdd.Add(self.Reset_btn,(9,4),(1,1),wx.EXPAND)
self.SetSizerAndFit(self.sizerAdd)
self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y )
######################################################################
def OnResetPanel(self,event):
### Kill all children
self.expansion.Destroy()
self.v1_txt.Destroy()
self.v1_qty.Destroy()
self.v2_txt.Destroy()
self.v2_qty.Destroy()
self.Add_btn.Destroy()
self.Reset_btn.Destroy()
### Reinitialise sizer
self.name.Enable(True)
self.name.SetValue("")
######################################################################
def OnAdd(self,event):
print 'Add'
self.OnResetPanel(self)
######################################################################
######################################################################
if __name__ == "__main__":
app = wx.App()
Main().Show()
app.MainLoop()
Basically, I have a TextCtrl in a first sizer which is waiting for an entry. Once the user hits enter, several objects appear in a second sizer.
The issue on windows seems to come from the use of the two gridbagsizers (sizername and sizerAdd). After pressing enter (waited event in the __init__), the objects defined within the sizerAdd do not appear. When I extend the window where the script is running, these objects appear magically !
Any idea ?
EDIT : The code is now runnable
I think the problem in your code is these two lines at the end of your AddToCol method:
self.SetSizerAndFit(self.sizerAdd)
self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y )
At this point, you're changing the sizer of the AddToCollection panel from self.sizername to self.sizerAdd. The Enter Name: label and the textbox however are still within the self.sizername sizer. However, this sizer isn't the sizer for any window, nor has it been added to any other sizer.
Generally, in wxPython, every sizer should be set as the sizer for a window, or be added to another sizer. This other sizer would then be the sizer for a window, or be contained within another sizer, and so on. In your case, your self.sizername sizer ends up being neither, and in this situation I would expect unpredictable behaviour. If your code works on Linux then I would say that it happens to work by accident.
I can think of a few things you could do here:
Add self.sizerAdd as a child of self.sizername. This can be done by replacing the two lines above with
self.sizername.Add(self.sizerAdd,(4,0),(1,1),wx.EXPAND)
self.sizername.Layout()
In AddToCol, add the widgets directly to the self.sizername sizer instead of adding them to self.sizerAdd.
Create a wx.BoxSizer() with vertical orientation, set that to be the sizer for the AddToCollection panel, and add the self.sizername and self.sizerAdd sizers to your BoxSizer.
In all three cases, after creating the new widgets you will need to call the Layout() method on the top-level sizer, be it either self.sizername or the top-level BoxSizer. The code snippet under option 1 includes this line already.
Additionally, you may need to modify your OnResetPanel() method. If you chose options 1 or 3, you will need to remove the self.sizerAdd sizer from whichever sizer you added it to. For example, in option 1, you would add the line
self.sizername.Remove(self.sizerAdd)
Another approach would be for your AddToCol method to create all the widgets within a Panel and add that to the main panel at the end. Your AddToCol method would then need to create a child panel, add the extra controls as children of this panel instead of the main panel (self), set the sizer of the child panel to self.sizerAdd and finally add this panel to the self.sizername sizer.
def AddToCol(self):
self.sizerAdd = wx.GridBagSizer(5, 5)
self.sizerAdd.AddGrowableCol(0, 0)
self.name.Enable(False)
self.child_panel = wx.Panel(self)
### Expansion
self.expansion = wx.Choice(self.child_panel, -1, choices=['test 1', 'test 2'])
self.expansion.SetSelection(0)
self.sizerAdd.Add(self.expansion,(5,0),(1,6),wx.EXPAND)
# Create other widgets as before but with the parent set to self.child_panel
# instead of self.
self.child_panel.SetSizer(self.sizerAdd)
self.sizername.Add(self.child_panel,(4,0),(1,1),wx.EXPAND)
self.sizername.Layout()
You would then also need to replace the line
self.sizername.Remove(self.sizerAdd)
in OnResetPanel() with the two lines:
self.sizername.Remove(self.child_panel)
self.child_panel.Destroy()
One thing which bugged me about my approach 1 above was that I saw the widgets briefly appear in the top-left corner before appearing in the correct place. This adaptation fixes this problem and so makes the GUI behave itself a bit better. I couldn't reproduce your black area issue you mention in your comment, but hopefully this approach fixes your problem as well.

Python/WXWidgets: ST_NO_AUTORESIZE not being honored for wx.StaticText

I want to throw up a view in the center of the screen at a fixed size, with some static text being displayed centered both horizontally and vertically.
So far, I have the following 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 ) )
# Center form
self.Center()
self.txtField = wx.StaticText(self, label=text, pos=(80,120), size=(320,200), style=wx.ALIGN_CENTRE_HORIZONTAL | wx.ST_NO_AUTORESIZE)
self.txtField.SetFont(wx.Font(24, wx.DEFAULT, wx.BOLD, 0))
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()
app.MainLoop()
The goal is to actually have the text vertically centered, but for now, I was just trying to be explicit about the positioning of the static text on the frame.
For a brief split second, the text appears in the position I put it in, but then it quickly jumps to the very top bound of the window and expands to the maximum width. (I deliberately set the width and position low so I'd be able to see if this behavior was occurring or not.)
It does not matter if I use wx.Dialog or wx.Frame.
As you can see I did define the NO_AUTORESIZE flag, but this is not being honored.
Can anyone explain what's happening?
Python 2.7.5/wxWidgets 2.8.12.1/Mac OS X 10.8.4
Turns out that it's a limitation of Mac OS X's native dialog implementation.
The following made it work on OS X. I never did try it on Windows but from other forum posts it appears it would have worked as-is on Windows.
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 ) )
# Center form
self.Center()
# (For Mac) Setup a panel
self.panel = wx.Panel(self)
# Create text field
self.txtField = wx.StaticText(self.panel, label=text, pos=(80,120), size=(320,200), style=wx.ALIGN_CENTRE_HORIZONTAL | wx.ST_NO_AUTORESIZE)
self.txtField.SetFont(wx.Font(24, wx.DEFAULT, wx.BOLD, 0))
self.txtField.SetAutoLayout(False)
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()
app.MainLoop()

How to layout wxPython panels that expand both directions

I'm building a UI in wxPython,[note 1] and I have three main body panels, two toolbars, and a status bar, with two BoxSizer elements (a vertical one containing all of the above, and a horizontal one containing the body panels). I cannot get the layout to work quite right, and I'm getting some behavior that I can't find in the documentation. I'm leaving out some details, but here is the relevant (working) part of the app:
import wx
import wx.adv
class MyApp(wx.App):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def OnInit(self):
self.frame = AppFrame(parent=None, title="My Sweet App")
self.SetTopWindow(self.frame)
self.frame.Show()
return True
class AppFrame(wx.Frame):
def __init__(self, size=(1024, 768), *args, **kwargs):
super().__init__(size=size, *args, **kwargs)
# Menu (quit only)
menubar = wx.MenuBar()
file_menu = wx.Menu()
quit_item = wx.MenuItem(
file_menu, wx.ID_EXIT, '&Exit', 'Close the application')
file_menu.Append(quit_item)
self.Bind(wx.EVT_MENU, OnQuit, id=wx.ID_EXIT)
menubar.Append(file_menu, '&File')
self.SetMenuBar(menubar)
# Outer box wrapper
main_box = wx.BoxSizer(orient=wx.VERTICAL)
self.SetSizer(main_box)
main_box.SetMinSize(size)
# Inner box with the three main view panels in it
wrap_panels_box = wx.BoxSizer(orient=wx.HORIZONTAL)
wrap_panels_box.SetMinSize(200, 200)
panel1 = wx.Panel(self, -1)
panel2 = wx.Panel(self, -1)
panel3 = wx.Panel(self, -1)
common_flags = wx.SizerFlags().Expand().Border(wx.ALL, 5)
wrap_panels_box.AddMany([(panel1, common_flags),
(panel2, common_flags),
(panel3, common_flags)])
# Two toolbars for different sets of commands
toolbar1 = wx.ToolBar(parent=self)
tool1 = toolbar1.CreateTool(
toolId=wx.ID_NEW, label='',
bmpNormal=wx.ArtProvider.GetBitmap(wx.ART_NEW, wx.ART_TOOLBAR))
toolbar1.AddTool(tool1)
toolbar1.Realize()
toolbar2 = wx.ToolBar(parent=self)
tool2 = toolbar2.CreateTool(
toolId=wx.ID_SAVE, label='',
bmpNormal=wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR))
toolbar2.AddTool(tool2)
toolbar2.Realize()
statusbar = wx.StatusBar(parent=self)
# Add all layout elements
bar_flags = common_flags.Proportion(0)
main_box.Add(toolbar1, bar_flags)
main_box.Add(wrap_panels_box, common_flags.Proportion(1))
main_box.Add(toolbar2, bar_flags)
main_box.Add(statusbar, bar_flags)
self.Layout()
def OnQuit(event):
exit(0)
if __name__ == '__main__':
app = MyApp()
app.MainLoop()
All the sub-component generation methods (_GenerateMenu(), _GenerateToolStatusBars(), and _GenerateViewPanels()) work as expected and basically as desired, so I'm leaving them aside.
The various pieces are largely in the right places, but I have a couple quirks here.
1. Status bar expansion
The status bar returned by the _GenerateToolStatusBars() method acts like it has Proportion(1) set on it: it expands or contracts vertically as the main window is expanded vertically. It also has additional space above it. I can make this stop, however, by setting the panel proportions as follows:
bar_flags = common_flags.Proportion(-1)
main_box.Add(toolbar1, bar_flags)
main_box.Add(wrap_panels_box, common_flags.Proportion(0))
main_box.Add(toolbar2, bar_flags)
main_box.Add(statusbar, bar_flags)
A -1 value isn't even documented for the Proportion()[note 2] setting on a Sizer, and the behavior basically matches what I would expect from the original code sample. What's going on here?
2. Later elements in BoxSizer sliding over earlier elements
Regardless of how I have the proportions set (at least between the two options above), the latter items behave as expected with relation to each other. They slide over the first element if the box becomes small, however. So, if I have _GenerateViewPanels() return the panels (as usual), they slide up and cover the top toolbar. If I make that do nothing (none of the normal panels are generated), the next toolbar slides up and covers the top toolbar. To reiterate: none of the bottom toolbars or panels interact with each other that way; they only do it with the first toolbar. As before, I'm confused: what's going on here?
[Edit: Made the above code a fully working sample application.]
Notes:
I'm using a wxPython snapshot build of Phoenix, specifically 2.9.5.81-r73784, running against Python 3.3 on both Windows 7 and OS X. It is possible this is a problem from the snapshot build, but I'm doubtful.
Proportion() is a fancy wrapper for the basic proportion=<value> argument for adding an element to a Sizer. As for why I tried -1, I just mis-remembered the default/base values for the proportion argument to BoxSizer.Add().
The problem is apparently with the distinct Proportion() calls. A little testing and use of the wxPython Widget Inspection Tool makes clear that common_flags is being modified by the calls on it.
All methods of SizerFlags objects return the same object (not a copy of the object), so calling a method updates the object and all references to it – it does not return a copy, but the same object. So, the original code with comments added explaining what went wrong:
common_flags = wx.SizerFlags().Expand().Border(wx.ALL, 1) # creates the object
bar_flags = common_flags.Proportion(0) # bar_flags points to the common_flags object
# Referencing common_flags with Proportion set to 0
main_box.Add(toolbar1, bar_flags)
# Changes the value of common_flags.
main_box.Add(wrap_panels_box, common_flags.Proportion(1))
# Since bar_flags points to common_flags, it also has Proportion set to 1
main_box.Add(toolbar2, bar_flags)
main_box.Add(statusbar, bar_flags)
The solution is simple: declare bar_flags and box_flags as separate objects. This involves some small repetition of code, but it's worth note that you are not repeating the action on the same object; you are performing the same actions on multiple objects. Supplying the following code instead solves the issue:
bar_flags = wx.SizerFlags().Expand().Border(wx.ALL, 1).Proportion(0)
box_flags = wx.SizerFlags().Expand().Border(wx.ALL, 1).Proportion(1)
main_box.Add(tool_status_bars.main, bar_flags)
main_box.Add(wrap_panels_box, box_flags)
main_box.Add(tool_status_bars.panel_view, bar_flags)
main_box.Add(tool_status_bars.status, bar_flags)
As expected, the boxes now relate to each other as they should: the wrap_panels_box expands, while the tool and status bars do not.

Grid and Sizers in wxPython

So I've got an issue with Grid and Sizers in wxPython, if i include this table the formating seems to mess up somehow, the entire window appears to have the correct size and the items appear at the correct locations.
But when this table is included the inner light grey which I guess is the panel ?
but items below the table are hidden because of this dark grey field
removing the setcolsize calls doesn't fix it either
Thanks for any replies
wx.Frame.__init__(self, parent, title=title)
panel = wx.Panel(self)
grid = wx.GridBagSizer(hgap=5, vgap=5)
# some other items comes before this
# FileGrid
fileF = wx.grid.Grid(panel)
fileF.CreateGrid(2,3)
fileF.SetColLabelSize(0)
fileF.SetRowLabelSize(0)
fileF.SetCellValue(0, 0, "Old")
fileF.SetCellValue(0, 1, "New")
fileF.SetCellValue(0, 2, "Update?")
grid.Add(fileF, pos=(5,0))
# FileGrid end
runBtn = wx.Button(panel, wx.ID_APPLY, "Apply")
self.Bind(wx.EVT_BUTTON, self.applyScRen, runBtn)
grid.Add(runBtn, pos=(6,0))
self.SetSizerAndFit(grid)
You do not provide the full frame structure but this should get you on the right track.
Change the last line to:
panel.SetSizerAndFit(grid)
That already improves matters. The container for the sizer is the panel and that, in turn, is contained inside the frame.
EDIT
This pastebin has the full working version. It produces this image followed by the key change I made to your code:
#panel.CreateStatusBar()
vSizer.Add(grid, 0, wx.ALL, 5)
panel.SetSizerAndFit(grid)
self.CreateStatusBar()
self.SetInitialSize()
self.Show(True)

Categories

Resources