Resizing the main panel

Here's some demo code. There is a menu, a status bar, and a panel with a green background. I want the panel to resize when the screen gets bigger or smaller. Currently the min size gets set when I call SetSize(), so you can't make it smaller.

Any help or pointers would be appreciated.

Thanks,
James Bigler

Just realized I forgot to attach the demo code.

James

resize.py (2.03 KB)

Quoting James Bigler <bigler@cs.utah.edu>:

Just realized I forgot to attach the demo code.

James

Here is some modified code that I think does what you want:

···

---------------
import wx

class MyApp(wx.App):
     def OnInit(self):
         frame = Frame(parent=None, title=\'Resize Panel\')
         frame.Show()
         self.SetTopWindow(frame)
         return True

class Frame(wx.Frame):
     def __init__(self, parent, title):
         wx.Frame.__init__(self, parent=parent, title=title)

         # Setup menu bar
         menuBar = wx.MenuBar()
         menu1 = wx.Menu()
         self.Bind(wx.EVT_MENU, self.OnQuit, menu1.Append(wx.NewId(), \"&Quit\"))
         menuBar.Append(menu1, \"&File\")
         menu2 = wx.Menu()
         self.Bind(wx.EVT_MENU, self.OnAbout, menu2.Append(wx.NewId(), \"About Me\"))
         menuBar.Append(menu2, \"&Help\")
         self.SetMenuBar(menuBar)

         # Create status bar
         self.CreateStatusBar()
         self.SetStatusText(\"Welcome to wxPython!\")

         # Create the panel
         self.panel = wx.Panel(self, -1, size=wx.Size(300,400))
         self.panel.SetBackgroundColour(wx.NamedColor(\"green\"))

         box = wx.BoxSizer(wx.HORIZONTAL)
         box.Add(self.panel, 1, wx.EXPAND)
         self.SetSizerAndFit(box)

         print \"Min Size = %s\" % self.panel.GetMinSize()
         print \"Max Size = %s\" % self.panel.GetMaxSize()
         print \"Best Size = %s\" % self.panel.GetBestSize()

     def OnQuit(self, event):
         self.Close()

     def OnAbout(self, event):
         wx.MessageBox(\"This is a test program to show how to resize the main panel. I want the green panel to change size when the screen is resized bigger and smaller than the original. \",
                       \"About Me\", wx.OK | wx.ICON_INFORMATION, self)

if __name__ == \'__main__\':
     app = MyApp(False)
     app.MainLoop()

-----------------

Regards,

Ray Smith
http://RaymondSmith.com

ray@raymondsmith.com wrote:

Here is some modified code that I think does what you want:

..SNIP..

Yes, you discovered what I did eventually. The OnSize event was causing part of the problem. I needed to add event.Skip(True) to make it work on making it bigger.

I still can't make it smaller than the initial size, however. I even tried to change the minimum size after the window was shown, but that didn't have an effect.

     def OnSize(self, event):
         if (not self.panel.minSizeSet):
             self.panel.SetMinSize(wx.Size(20,20))
             self.panel.minSizeSet = True

         self.SetStatusText("Size = %s" % (self.panel.GetSize(),) )
         event.Skip(True)

So I want the panel to be a certain initial size, but I want to be able to make it bigger or smaller afterwards.

Thanks,
James

Quoting James Bigler <bigler@cs.utah.edu>:

I still can\'t make it smaller than the initial size, however. I even
tried to change the minimum size after the window was shown, but that
didn\'t have an effect.

    def OnSize(self, event):
        if (not self.panel.minSizeSet):
            self.panel.SetMinSize(wx.Size(20,20))
            self.panel.minSizeSet = True

        self.SetStatusText(\"Size = %s\" % (self.panel.GetSize(),) )
        event.Skip(True)

So I want the panel to be a certain initial size, but I want to be able
to make it bigger or smaller afterwards.

Hi James,

One way is to set the frame to a default size and don\'t do the:
self.SetSizerAndFit(box)

See code below:

···

-----------
import wx

class MyApp(wx.App):
     def OnInit(self):
         frame = Frame(parent=None, title=\'Resize Panel\')
         frame.Show()
         self.SetTopWindow(frame)
         return True

class Frame(wx.Frame):
     def __init__(self, parent, title):
         wx.Frame.__init__(self, parent=parent, title=title, size=(300,400))

         # Setup menu bar
         menuBar = wx.MenuBar()
         menu1 = wx.Menu()
         self.Bind(wx.EVT_MENU, self.OnQuit, menu1.Append(wx.NewId(), \"&Quit\"))
         menuBar.Append(menu1, \"&File\")
         menu2 = wx.Menu()
         self.Bind(wx.EVT_MENU, self.OnAbout, menu2.Append(wx.NewId(), \"About Me\"))
         menuBar.Append(menu2, \"&Help\")
         self.SetMenuBar(menuBar)

         # Create status bar
         self.CreateStatusBar()
         self.SetStatusText(\"Welcome to wxPython!\")

         # Create the panel
         self.panel = wx.Panel(self, -1)
         self.panel.SetBackgroundColour(\"green\")

         box = wx.BoxSizer(wx.HORIZONTAL)
         box.Add(self.panel, 1, wx.EXPAND)
         # !!! This resizes the frame !!!
         #self.SetSizerAndFit(box)

         print \"Min Size = %s\" % self.panel.GetMinSize()
         print \"Max Size = %s\" % self.panel.GetMaxSize()
         print \"Best Size = %s\" % self.panel.GetBestSize()

     def OnQuit(self, event):
         self.Close()

     def OnAbout(self, event):
         wx.MessageBox(\"This is a test program to show how to resize the main panel. I want the green panel to change size when the screen is resized bigger and smaller than the original. \",
                       \"About Me\", wx.OK | wx.ICON_INFORMATION, self)

if __name__ == \'__main__\':
     app = MyApp(False)
     app.MainLoop()

----------------

Regards,

Ray Smith
http://RaymondSmith.com

ray@raymondsmith.com wrote:

Quoting James Bigler <bigler@cs.utah.edu>:

I still can\'t make it smaller than the initial size, however. I even
tried to change the minimum size after the window was shown, but that
didn\'t have an effect.

    def OnSize(self, event):
        if (not self.panel.minSizeSet):
            self.panel.SetMinSize(wx.Size(20,20))
            self.panel.minSizeSet = True

        self.SetStatusText(\"Size = %s\" % (self.panel.GetSize(),) )
        event.Skip(True)

So I want the panel to be a certain initial size, but I want to be able
to make it bigger or smaller afterwards.

Hi James,

One way is to set the frame to a default size and don\'t do the:
self.SetSizerAndFit(box)

..SNIP CODE EXAMPLE..

This works OK, but then the panel isn't the right size because of the space taken by the menu and status bar (and perhaps additional things in the future).

I really need the panel to be a certain size to start off with, but be able to be resized bigger and *smaller* after the initial layout.

Thanks,
James

Quoting James Bigler <bigler@cs.utah.edu>:

This works OK, but then the panel isn\'t the right size because of the
space taken by the menu and status bar (and perhaps additional things
in the future).

I really need the panel to be a certain size to start off with, but be
able to be resized bigger and *smaller* after the initial layout.

Hi James,

Sorry, you should be able to set the size of the panel in the same way:

See code:

···

-------------
import wx

class MyApp(wx.App):
     def OnInit(self):
         frame = Frame(parent=None, title=\'Resize Panel\')
         frame.Show()
         self.SetTopWindow(frame)
         return True

class Frame(wx.Frame):
     def __init__(self, parent, title):
         wx.Frame.__init__(self, parent=parent, title=title)

         # Setup menu bar
         menuBar = wx.MenuBar()
         menu1 = wx.Menu()
         self.Bind(wx.EVT_MENU, self.OnQuit, menu1.Append(wx.NewId(), \"&Quit\"))
         menuBar.Append(menu1, \"&File\")
         menu2 = wx.Menu()
         self.Bind(wx.EVT_MENU, self.OnAbout, menu2.Append(wx.NewId(), \"About Me\"))
         menuBar.Append(menu2, \"&Help\")
         self.SetMenuBar(menuBar)

         # Create status bar
         self.CreateStatusBar()
         self.SetStatusText(\"Welcome to wxPython!\")

         # Create the panel
         self.panel = wx.Panel(self, -1, size=(300,400))
         self.panel.SetBackgroundColour(\"green\")

         box = wx.BoxSizer(wx.HORIZONTAL)
         box.Add(self.panel, 1, wx.EXPAND)
         # !!! This resizes panel !!!
         #self.SetSizerAndFit(box)

         print \"Min Size = %s\" % self.panel.GetMinSize()
         print \"Max Size = %s\" % self.panel.GetMaxSize()
         print \"Best Size = %s\" % self.panel.GetBestSize()
         print \"Size = %s\" % self.panel.GetSize()

     def OnQuit(self, event):
         self.Close()

     def OnAbout(self, event):
         wx.MessageBox(\"This is a test program to show how to resize the main panel. I want the green panel to change size when the screen is resized bigger and smaller than the original. \",
                       \"About Me\", wx.OK | wx.ICON_INFORMATION, self)

if __name__ == \'__main__\':
     app = MyApp(False)
     app.MainLoop()

----------------

Regards,

Ray Smith
http://RaymondSmith.com

ray@raymondsmith.com wrote:

Quoting James Bigler <bigler@cs.utah.edu>:

This works OK, but then the panel isn\'t the right size because of the
space taken by the menu and status bar (and perhaps additional things
in the future).

I really need the panel to be a certain size to start off with, but be
able to be resized bigger and *smaller* after the initial layout.

Hi James,

Sorry, you should be able to set the size of the panel in the same way:

...SNIP CODE...

         # Create the panel
         self.panel = wx.Panel(self, -1, size=(300,400))
         self.panel.SetBackgroundColour("green")

         box = wx.BoxSizer(wx.HORIZONTAL)
         box.Add(self.panel, 1, wx.EXPAND)
         # !!! This resizes panel !!!
         #self.SetSizerAndFit(box)

...SNIP CODE...

I tried you code and the panel doesn't start off with the right size. Plus the status bar doesn't appear until I make the window long enough (looks like to be around the requested height of the panel).

What I don't get is why the sizer forces the min size to be (300,400). I even tried to force it to be smaller, but the sizer overrides it somehow.

Thanks,
James

Quoting James Bigler <bigler@cs.utah.edu>:

...SNIP CODE...

        # Create the panel
        self.panel = wx.Panel(self, -1, size=(300,400))
        self.panel.SetBackgroundColour(\\\"green\\\")

        box = wx.BoxSizer(wx.HORIZONTAL)
        box.Add(self.panel, 1, wx.EXPAND)
        # !!! This resizes panel !!!
        #self.SetSizerAndFit(box)

...SNIP CODE...

I tried you code and the panel doesn\\\'t start off with the right size.
Plus the status bar doesn\\\'t appear until I make the window long enough
(looks like to be around the requested height of the panel).

What I don\\\'t get is why the sizer forces the min size to be (300,400).
I even tried to force it to be smaller, but the sizer overrides it
somehow.

Hi James,

I added:
         print \\\"Size = %s\\\" % self.panel.GetSize()
and got the results:
Size = (300, 400)

did you get something else?

The status bar is displayed as soon as the program starts on my PC\'s (WinXP and Win2000 using Python 2.4 wxPython 2.6.3.2).

I have attached your program with my mods. Previously the inline version was formatted poorly.

Regards,

Ray Smith

resize.py (1.79 KB)

ray@raymondsmith.com wrote:
..SNIP..

Hi James,

I added:
        print \\\"Size = %s\\\" % self.panel.GetSize()
and got the results:
Size = (300, 400)

did you get something else?

I got out that size, but wxPython doesn't size the panel in the window properly. I've attached some images of what it looks like originally and after I resize it a bit.

The status bar is displayed as soon as the program starts on my PC\'s (WinXP and Win2000 using Python 2.4 wxPython 2.6.3.2).

I'm using Ubuntu 5.10 with Python 2.4.2, wxPython (GTK) 2.6.1.1. That's pretty close to what you are using, so maybe this is a GTK issue?

I have attached your program with my mods. Previously the inline version was formatted poorly.

I was able to run the files properly after I removed all the '\'s in front of the quotes.

Thanks,
James

BTW, I really appreciate your help with this. I've been fighting this for days now.

ray@raymondsmith.com wrote:

Quoting James Bigler <bigler@cs.utah.edu>:

...SNIP CODE...

        # Create the panel
        self.panel = wx.Panel(self, -1, size=(300,400))
        self.panel.SetBackgroundColour(\\\"green\\\")

        box = wx.BoxSizer(wx.HORIZONTAL)
        box.Add(self.panel, 1, wx.EXPAND)
        # !!! This resizes panel !!!
        #self.SetSizerAndFit(box)

...SNIP CODE...

I tried you code and the panel doesn\\\'t start off with the right size.
Plus the status bar doesn\\\'t appear until I make the window long enough
(looks like to be around the requested height of the panel).

What I don\\\'t get is why the sizer forces the min size to be (300,400).
I even tried to force it to be smaller, but the sizer overrides it
somehow.

Hi James,

I added:
        print \\\"Size = %s\\\" % self.panel.GetSize()
and got the results:
Size = (300, 400)

did you get something else?

This is the size before the sizer does a layout. If you look at the size after the frame has been shown and the layout happens then you'll see the problem James is having. Without the SetSizer* call in your sample the default frame size is being used and so the panel is being resized in the first layout.

To address the other problems there are a couple things that needs to be understood about sizers and default sizes.

First, SetSizerAndFit is a convenience function that simply calls self.SetSizer(sizer) and sizer.SetSizeHints(self), so when you call that with the frame you are setting the min size of the frame based on the current minsize needed by the sizer, so that is why the frame can't be resized smaller. So changing that to just self.SetSizer and a separate call to self.Fit will set the initial size correctly based on the sizer's min size, but still allow the frame to get smaller.

Second, when a sizer calculates the space it needs it calls the GetSBestFittingSize method on each widget that will return a size based on the widget's min size (if set) and a calculated best size. But for things like the panel which has no children, no sizer, and no set minsize there is no way to calculate a best size so the current size is used instead. For widgets that will have a flexible size in the sizer this means that as soon as the sizer changes the size larger, then the best size is larger and the sizer may not make it smaller (depending on the sizer parameters.) Also, for most widgets passing a size to the constructor will set the min size to that value.

So in the case of this example where you want the initial size to not be the min size, you need to set the initial size in some other way than by letting the sizer do it. See attached sample for one way to do it.

resize.py (1.97 KB)

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn wrote:

Quoting James Bigler <bigler@cs.utah.edu>:

...SNIP CODE...

        # Create the panel
        self.panel = wx.Panel(self, -1, size=(300,400))
        self.panel.SetBackgroundColour(\\\"green\\\")

        box = wx.BoxSizer(wx.HORIZONTAL)
        box.Add(self.panel, 1, wx.EXPAND)
        # !!! This resizes panel !!!
        #self.SetSizerAndFit(box)

...SNIP CODE...

I tried you code and the panel doesn\\\'t start off with the right size.
Plus the status bar doesn\\\'t appear until I make the window long enough
(looks like to be around the requested height of the panel).

What I don\\\'t get is why the sizer forces the min size to be (300,400).
I even tried to force it to be smaller, but the sizer overrides it
somehow.

Hi James,

I added:
        print \\\"Size = %s\\\" % self.panel.GetSize()
and got the results:
Size = (300, 400)

did you get something else?

This is the size before the sizer does a layout. If you look at the size after the frame has been shown and the layout happens then you'll see the problem James is having. Without the SetSizer* call in your sample the default frame size is being used and so the panel is being resized in the first layout.

To address the other problems there are a couple things that needs to be understood about sizers and default sizes.

First, SetSizerAndFit is a convenience function that simply calls self.SetSizer(sizer) and sizer.SetSizeHints(self), so when you call that with the frame you are setting the min size of the frame based on the current minsize needed by the sizer, so that is why the frame can't be resized smaller. So changing that to just self.SetSizer and a separate call to self.Fit will set the initial size correctly based on the sizer's min size, but still allow the frame to get smaller.

So, one of the things I had to note about your comment is that it creates a min size for the *frame*. I was changing the min size of the panel, but not changing the min size of the frame. Because of this, I couldn't size the panel down.

Second, when a sizer calculates the space it needs it calls the GetSBestFittingSize method on each widget that will return a size based on the widget's min size (if set) and a calculated best size. But for things like the panel which has no children, no sizer, and no set minsize there is no way to calculate a best size so the current size is used instead. For widgets that will have a flexible size in the sizer this means that as soon as the sizer changes the size larger, then the best size is larger and the sizer may not make it smaller (depending on the sizer parameters.) Also, for most widgets passing a size to the constructor will set the min size to that value.

So in the case of this example where you want the initial size to not be the min size, you need to set the initial size in some other way than by letting the sizer do it. See attached sample for one way to do it.

The problem with this is that it did weird things when I had a wxGLCanvas in the panel. Something would cause a SIZE event and it would pop down to the min size of (20, 20).

Here's the relevant block of code that sets the size of the panel, does a fit to get everything the correct size in the beginning and then changes the min size of the panel and frame.

         # Create the panel
         self.panel = wx.Panel(self, -1)
         self.panel.SetBackgroundColour(wx.NamedColor("green"))

         box = wx.BoxSizer(wx.HORIZONTAL)
         box.Add(self.panel, 1, wx.EXPAND)
         # Set the initial sizes I want
         self.panel.SetSize((300, 400))
         self.SetSizerAndFit(box)
         # Now fix the min sizes of the panel and frame
         self.panel.SetMinSize((20,20))
         self.SetMinSize(self.GetSize() - self.panel.GetSize()
                         + self.panel.GetMinSize())

This also does the right thing when I have a wxGLCanvas inside the panel. I've attached the latest code.

Thank you Robin and Ray!
James

resize2.py (2.28 KB)

···

ray@raymondsmith.com wrote: