scrollbars don't work after calling sizer.Fit()

Hi,

I made an instance of wx.Notebook with a page being an instance of
wx.ScrolledWindow. In the scrolled window I add / delete some items.
In order to resize the scrolled window I then call sizer.Fit(self). The
panel is resized as expected - but when trying to use the scrollbars,
they don't scroll the panel anymore. After resizing the main window
(with my mouse) scrolling works again...

Any idea?

Thanks, Dietrich

When you call Fit then it is resizing the scrolled window to be large enough to fit the content without needing to be scrolled, even if that is larger than the client area of the notebook. When you tweak the size then the notebook is resizing the scrolled window to fit the client area again, meaning that it is smaller than the virtual size and so the scrollbars will be active again. Try using scrolledWindow.FitInside() instead.

···

On 3/15/10 2:05 AM, Dietrich Bollmann wrote:

Hi,

I made an instance of wx.Notebook with a page being an instance of
wx.ScrolledWindow. In the scrolled window I add / delete some items.
In order to resize the scrolled window I then call sizer.Fit(self). The
panel is resized as expected - but when trying to use the scrollbars,
they don't scroll the panel anymore. After resizing the main window
(with my mouse) scrolling works again...

Any idea?

--
Robin Dunn
Software Craftsman

> Hi,
>
> I made an instance of wx.Notebook with a page being an instance of
> wx.ScrolledWindow. In the scrolled window I add / delete some items.
> In order to resize the scrolled window I then call sizer.Fit(self). The
> panel is resized as expected - but when trying to use the scrollbars,
> they don't scroll the panel anymore. After resizing the main window
> (with my mouse) scrolling works again...
>
> Any idea?

When you call Fit then it is resizing the scrolled window to be large
enough to fit the content without needing to be scrolled, even if that
is larger than the client area of the notebook. When you tweak the size
then the notebook is resizing the scrolled window to fit the client area
again, meaning that it is smaller than the virtual size and so the
scrollbars will be active again. Try using scrolledWindow.FitInside()
instead.

Thank you very much!

Your advice helped a lot and the scrolled window works much better now!

But when the pane inside the scrolled window gets so short, that the
scrollbar on the side vanishes, and after some new content is loaded
which makes the scrollbar appear again, the problem is there again.

Resizing the window makes it work again as before. After resizing it
works until the pane fits without the scrollbar again where I have to
manually resize once more...

As I start with an empty pane inside the scrolled window, initially
(after starting the application) the problem is always there.

Thanks, Dietrich

···

On Mon, 2010-03-15 at 12:15 -0700, Robin Dunn wrote:

On 3/15/10 2:05 AM, Dietrich Bollmann wrote:

--
Robin Dunn
Software Craftsman
http://wxPython.org

When you load new content or modify your GUI by adding or deleting
widgets, it's usually a good idea to call the frame's Layout() method
so that it recalculates the size of its children (and re-sizes
accordingly). This may need to be combined with a Refresh() too.

···

On Mar 15, 10:04 pm, Dietrich Bollmann <dir...@web.de> wrote:

On Mon, 2010-03-15 at 12:15 -0700, Robin Dunn wrote:
> On 3/15/10 2:05 AM, Dietrich Bollmann wrote:
> > Hi,

> > I made an instance of wx.Notebook with a page being an instance of
> > wx.ScrolledWindow. In the scrolled window I add / delete some items.
> > In order to resize the scrolled window I then call sizer.Fit(self). The
> > panel is resized as expected - but when trying to use the scrollbars,
> > they don't scroll the panel anymore. After resizing the main window
> > (with my mouse) scrolling works again...

> > Any idea?

> When you call Fit then it is resizing the scrolled window to be large
> enough to fit the content without needing to be scrolled, even if that
> is larger than the client area of the notebook. When you tweak the size
> then the notebook is resizing the scrolled window to fit the client area
> again, meaning that it is smaller than the virtual size and so the
> scrollbars will be active again. Try using scrolledWindow.FitInside()
> instead.

Thank you very much!

Your advice helped a lot and the scrolled window works much better now!

But when the pane inside the scrolled window gets so short, that the
scrollbar on the side vanishes, and after some new content is loaded
which makes the scrollbar appear again, the problem is there again.

Resizing the window makes it work again as before. After resizing it
works until the pane fits without the scrollbar again where I have to
manually resize once more...

As I start with an empty pane inside the scrolled window, initially
(after starting the application) the problem is always there.

Thanks, Dietrich

-------------------
Mike Driscoll

Blog: http://blog.pythonlibrary.org

Please make a small runnable sample that demonstrates the problem. MakingSampleApps - wxPyWiki

···

On 3/15/10 8:04 PM, Dietrich Bollmann wrote:

On Mon, 2010-03-15 at 12:15 -0700, Robin Dunn wrote:

On 3/15/10 2:05 AM, Dietrich Bollmann wrote:

Hi,

I made an instance of wx.Notebook with a page being an instance of
wx.ScrolledWindow. In the scrolled window I add / delete some items.
In order to resize the scrolled window I then call sizer.Fit(self). The
panel is resized as expected - but when trying to use the scrollbars,
they don't scroll the panel anymore. After resizing the main window
(with my mouse) scrolling works again...

Any idea?

When you call Fit then it is resizing the scrolled window to be large
enough to fit the content without needing to be scrolled, even if that
is larger than the client area of the notebook. When you tweak the size
then the notebook is resizing the scrolled window to fit the client area
again, meaning that it is smaller than the virtual size and so the
scrollbars will be active again. Try using scrolledWindow.FitInside()
instead.

Thank you very much!

Your advice helped a lot and the scrolled window works much better now!

But when the pane inside the scrolled window gets so short, that the
scrollbar on the side vanishes, and after some new content is loaded
which makes the scrollbar appear again, the problem is there again.

Resizing the window makes it work again as before. After resizing it
works until the pane fits without the scrollbar again where I have to
manually resize once more...

As I start with an empty pane inside the scrolled window, initially
(after starting the application) the problem is always there.

--
Robin Dunn
Software Craftsman

Hi Mike Driscoll,

When you load new content or modify your GUI by adding or deleting
widgets, it's usually a good idea to call the frame's Layout() method
so that it recalculates the size of its children (and re-sizes
accordingly). This may need to be combined with a Refresh() too.

I experimented with both of them - but the problem remains the same.

I append the stripped-down code in the hope that this will help to
find the problem:

After starting the example code, a panel with 6 buttons is shown.

If you push on the button with the label '5', the 'items' page of
the notebook is filled with 5 buttons. As the page is to small to
show them all, scrollbars are shown. Both of the scrollbars don't
work...

When resizing the panel with the mouse, they finally work.

When clicking on the buttons the page is cleared and new items
are created. The scrollbars continue to work as long as there
are more items than fitting on the page. When button [0]
is pushed, all remaining items are destructed and
no scrollbars are shown any more. After recreating more than 2
items by pushing on button [5] for example, scrollbars are
shown again - but, as before, do not work. You can make them work
by manually resizing the frame with the mouse again...

Another way to create the same issue is by clicking on the buttons
in the notebook panel: clicking on button i will destruct item i...
When enough items are destructed to make the remaining ones
fit into the page again and then new items are recreated, the same
issue occurs...

Thanks for your help, Dietrich

Here comes the code:

---begin---
#!/usr/bin/env python

import os, sys
import wx

## =========================================================

class ItemPanel(wx.Panel):

    def __init__(self, parent, settings, i):
        wx.Panel.__init__(self, parent, wx.ID_ANY)
        self.settings = settings
        self.parent = parent

        # sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)
        self.sizer = sizer

        # add a line
        # !!! FIXME !!!: For some reason, when this line is not added, nothing is shown at all...
        sizer.Add(wx.StaticLine(self, -1, size=(1024,-1)), 0, wx.ALL, 5)

        # add a [Delete] button
        button = wx.Button(self, -1, "%d" % i)
        button.SetToolTipString("Delete this item")
        self.Bind(wx.EVT_BUTTON, self.onDelete, button)
        sizer.Add(button, 1, wx.SHAPED)

    def onDelete(self, event):
        self.parent.deleteItemPanel(self)

## =========================================================

class ItemListPanel(wx.Panel):

    def __init__(self, parent, settings):
        wx.Panel.__init__(self, parent, wx.ID_ANY)
        self.settings = settings
        self.parent = parent

        # storage for item panels
        self.itemPanels = {}

        # sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)
        self.sizer = sizer

        # fit inside the scrolled window
        self.parent.FitInside()

    def deleteItemPanel(self, itemPanel):

        # destroy the item panel
        itemPanel.Destroy()

        # delete from list of item panels
        del self.itemPanels[itemPanel]

        # fit inside the scrolled window
        self.parent.FitInside()

    def clear(self):

        # destroy the item panels
        for item in self.itemPanels.keys():
            item.Destroy()

        # clear the list with the destroyed item panels
        self.itemPanels = {}

        # fit inside the scrolled window
        self.parent.FitInside()

    def createItems(self, i):

        # clear up from old items
        self.clear()

        # add the new items
        for i in range(i):
            itemPanel = ItemPanel(self, self.settings, i)
            self.itemPanels[itemPanel] = i
            self.sizer.Add(itemPanel, 0, wx.EXPAND)

        # fit inside the scrolled window
        self.parent.FitInside()

## =========================================================

class ButtonPanel(wx.Panel):

    def __init__(self, parent, settings):
        wx.Panel.__init__(self, parent, wx.ID_ANY)
        self.parent = parent
        self.settings = settings

        # sizer
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.SetSizer(sizer)
        self.sizer = sizer
        
        # add test buttons
        self.testButtons = {}
        for i in range(6):

            # test button for i items
            button = wx.Button(self, -1, "%d" % i)

            # store button under its id
            id = button.GetId()
            self.testButtons[id] = i
            self.Bind(wx.EVT_BUTTON, self.onTestButton, button)
            sizer.Add(button, 0, wx.EXPAND)
        
        # Layout button panel
        self.SetAutoLayout(1)

    def onTestButton(self, event):

        # get number of items to create
        id = event.GetId()
        i = self.testButtons[id]

        # create the items
        self.settings['item-panel'].createItems(i)

## =========================================================

class ScrolledWindow(wx.ScrolledWindow):
    def __init__(self, parent, settings):
        wx.ScrolledWindow.__init__(self, parent, -1, (0, 0), size = wx.DefaultSize, style = wx.SUNKEN_BORDER)
        self.parent = parent
        self.settings = settings

        # sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)
        self.sizer = sizer

        # scroll rate
        self.SetScrollRate(20,20)

        # add item panel
        panel = ItemListPanel(self, settings)
        settings['item-panel'] = panel
        sizer.Add(panel, 0, wx.EXPAND)

## =========================================================

class Notebook(wx.Notebook):
    def __init__(self, parent, settings):
        wx.Notebook.__init__(self, parent, -1, size = (-1, 160), style = wx.BK_DEFAULT)
        self.parent = parent
        self.settings = settings

        # add items panel page
        panel = ScrolledWindow(self, settings)
        self.addPage(panel, "Items")

    def addPage(self, page, title):
        def OnCPSize(evt, panel = page):
            page.SetPosition((0, 0))
            page.SetSize(evt.GetSize())
        page.Bind(wx.EVT_SIZE, OnCPSize)
        wx.Notebook.AddPage(self, page, title)

## =========================================================

class MainWindow(wx.Frame):

    def __init__(self, parent, settings):
        wx.Frame.__init__(self, parent, title = "Test", size = (-1, -1))
        self.parent = parent
        self.settings = settings

        # sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)
        self.sizer = sizer

        # add item panel
        self.notebookPanel = Notebook(self, settings)
        sizer.Add(self.notebookPanel, 1, wx.EXPAND)

        # Buttons from the button panel
        self.buttonPanel = ButtonPanel(self, settings)
        sizer.Add(self.buttonPanel, 0, wx.EXPAND)
        
        # storing the main window in the settings
        settings['main-window'] = self

        # fit
        self.SetAutoLayout(1)
        sizer.Fit(self)

        # Show the frame.
        self.Show(True)

## =========================================================
## Main
## ---------------------------------------------------------

app = wx.App(False)
settings = {}
settings['main-window'] = MainWindow(None, settings)

app.MainLoop()

## =========================================================
## =========================================================

## fin.

---end---

-------------------
Mike Driscoll

Blog: http://blog.pythonlibrary.org

Thank you!

···

On Tue, 2010-03-16 at 06:12 -0700, Mike Driscoll wrote:

On Mar 15, 10:04 pm, Dietrich Bollmann <dir...@web.de> wrote:
> On Mon, 2010-03-15 at 12:15 -0700, Robin Dunn wrote:
> > On 3/15/10 2:05 AM, Dietrich Bollmann wrote:
> > > Hi,
>
> > > I made an instance of wx.Notebook with a page being an instance of
> > > wx.ScrolledWindow. In the scrolled window I add / delete some items.
> > > In order to resize the scrolled window I then call sizer.Fit(self). The
> > > panel is resized as expected - but when trying to use the scrollbars,
> > > they don't scroll the panel anymore. After resizing the main window
> > > (with my mouse) scrolling works again...
>
> > > Any idea?
>
> > When you call Fit then it is resizing the scrolled window to be large
> > enough to fit the content without needing to be scrolled, even if that
> > is larger than the client area of the notebook. When you tweak the size
> > then the notebook is resizing the scrolled window to fit the client area
> > again, meaning that it is smaller than the virtual size and so the
> > scrollbars will be active again. Try using scrolledWindow.FitInside()
> > instead.
>
> Thank you very much!
>
> Your advice helped a lot and the scrolled window works much better now!
>
> But when the pane inside the scrolled window gets so short, that the
> scrollbar on the side vanishes, and after some new content is loaded
> which makes the scrollbar appear again, the problem is there again.
>
> Resizing the window makes it work again as before. After resizing it
> works until the pane fits without the scrollbar again where I have to
> manually resize once more...
>
> As I start with an empty pane inside the scrolled window, initially
> (after starting the application) the problem is always there.
>
> Thanks, Dietrich
>

The only real problem I saw was the EVT_SIZE handler you were binding for the notebook pages. You should let the notebook manage the size and position of the page windows, that's what it's there for. (Hint: The position of a notebook page will likely *not* be (0,0).)

Beyond that I was never able to get it into a state where the scrollbars do not work. What platform and wx version are you running on?

···

On 3/17/10 1:11 AM, Dietrich Bollmann wrote:

Hi Mike Driscoll,

When you load new content or modify your GUI by adding or deleting
widgets, it's usually a good idea to call the frame's Layout() method
so that it recalculates the size of its children (and re-sizes
accordingly). This may need to be combined with a Refresh() too.

I experimented with both of them - but the problem remains the same.

I append the stripped-down code in the hope that this will help to
find the problem:

After starting the example code, a panel with 6 buttons is shown.

If you push on the button with the label '5', the 'items' page of
the notebook is filled with 5 buttons. As the page is to small to
show them all, scrollbars are shown. Both of the scrollbars don't
work...

When resizing the panel with the mouse, they finally work.

When clicking on the buttons the page is cleared and new items
are created. The scrollbars continue to work as long as there
are more items than fitting on the page. When button [0]
is pushed, all remaining items are destructed and
no scrollbars are shown any more. After recreating more than 2
items by pushing on button [5] for example, scrollbars are
shown again - but, as before, do not work. You can make them work
by manually resizing the frame with the mouse again...

Another way to create the same issue is by clicking on the buttons
in the notebook panel: clicking on button i will destruct item i...
When enough items are destructed to make the remaining ones
fit into the page again and then new items are recreated, the same
issue occurs...

--
Robin Dunn
Software Craftsman

Hi Robin Dunn,

> Hi Mike Driscoll,
>
>> When you load new content or modify your GUI by adding or deleting
>> widgets, it's usually a good idea to call the frame's Layout() method
>> so that it recalculates the size of its children (and re-sizes
>> accordingly). This may need to be combined with a Refresh() too.
>
> I experimented with both of them - but the problem remains the same.
>
> I append the stripped-down code in the hope that this will help to
> find the problem:
>
> After starting the example code, a panel with 6 buttons is shown.
>
> If you push on the button with the label '5', the 'items' page of
> the notebook is filled with 5 buttons. As the page is to small to
> show them all, scrollbars are shown. Both of the scrollbars don't
> work...
>
> When resizing the panel with the mouse, they finally work.
>
> When clicking on the buttons the page is cleared and new items
> are created. The scrollbars continue to work as long as there
> are more items than fitting on the page. When button [0]
> is pushed, all remaining items are destructed and
> no scrollbars are shown any more. After recreating more than 2
> items by pushing on button [5] for example, scrollbars are
> shown again - but, as before, do not work. You can make them work
> by manually resizing the frame with the mouse again...
>
> Another way to create the same issue is by clicking on the buttons
> in the notebook panel: clicking on button i will destruct item i...
> When enough items are destructed to make the remaining ones
> fit into the page again and then new items are recreated, the same
> issue occurs...
>

The only real problem I saw was the EVT_SIZE handler you were binding
for the notebook pages. You should let the notebook manage the size and
position of the page windows, that's what it's there for. (Hint: The
position of a notebook page will likely *not* be (0,0).)

Beyond that I was never able to get it into a state where the scrollbars
do not work. What platform and wx version are you running on?

I have to admit, that I am most grateful and impressed by your readiness
to help! Thank you very much!

Uncommenting four lines as you where proposing solved the problem I
already lost hours with :slight_smile: :

    def addPage(self, page, title):
        #| def OnCPSize(evt, panel = page):
        #| page.SetPosition((0, 0))
        #| page.SetSize(evt.GetSize())
        #| page.Bind(wx.EVT_SIZE, OnCPSize)
        wx.Notebook.AddPage(self, page, title)

Even the other issue - nothing appearing at all when uncommenting the
static line statement in the following snippet - vanished immediately :slight_smile:

        # add a line
        # !!! FIXME !!!: For some reason, when this line is not added,
nothing is shown at all...
        #| sizer.Add(wx.StaticLine(self, -1, size=(1024,-1)), 0, wx.ALL,
5)

To say the truth, I assembled the code from the wx python demo
applications (see file wxPython-2.8.10.1/demo/Notebook.py ) and
wondered, for what these lines actually are needed. As I didn't
understand them well, I just let them as they are and forgot about
them :slight_smile:

Here the relevant lines from file wxPython-2.8.10.1/demo/Notebook.py :

    def makeColorPanel(self, color):
        p = wx.Panel(self, -1)
        win = ColorPanel.ColoredPanel(p, color)
        p.win = win
        def OnCPSize(evt, win=win):
            win.SetPosition((0,0))
            win.SetSize(evt.GetSize())
        p.Bind(wx.EVT_SIZE, OnCPSize)
        return p

Why are they needed in this example?

Maybe some comment would help other people to avoid the same problem
when trying to experiment with the demo file.

Thanks again, Dietrich

···

On Wed, 2010-03-17 at 10:25 -0700, Robin Dunn wrote:

On 3/17/10 1:11 AM, Dietrich Bollmann wrote:

--
Robin Dunn
Software Craftsman
http://wxPython.org

Hi Robin Dunn,

Please make a small runnable sample that demonstrates the problem.
MakingSampleApps - wxPyWiki

Thanks again. I just found this mail just now :slight_smile: If I had seen it
before I would have put the example into an attachment...

Dietrich

···

On Tue, 2010-03-16 at 22:19 -0700, Robin Dunn wrote:

--
Robin Dunn
Software Craftsman
http://wxPython.org

Here the EVT_SIZE handler is used to resize the ColorPanel to fill its parent panel, which could also have been done with a sizer. The difference with yours is that you didn't have the extra panel in there so it was resizing the page window relative to the notebook instead.

···

On 3/18/10 8:20 AM, Dietrich Bollmann wrote:

Here the relevant lines from file wxPython-2.8.10.1/demo/Notebook.py :

     def makeColorPanel(self, color):
         p = wx.Panel(self, -1)
         win = ColorPanel.ColoredPanel(p, color)
         p.win = win
         def OnCPSize(evt, win=win):
             win.SetPosition((0,0))
             win.SetSize(evt.GetSize())
         p.Bind(wx.EVT_SIZE, OnCPSize)
         return p

Why are they needed in this example?

--
Robin Dunn
Software Craftsman

Dear Robin Dunn,

> Here the relevant lines from file wxPython-2.8.10.1/demo/Notebook.py :
>
> def makeColorPanel(self, color):
> p = wx.Panel(self, -1)
> win = ColorPanel.ColoredPanel(p, color)
> p.win = win
> def OnCPSize(evt, win=win):
> win.SetPosition((0,0))
> win.SetSize(evt.GetSize())
> p.Bind(wx.EVT_SIZE, OnCPSize)
> return p
>
> Why are they needed in this example?

Here the EVT_SIZE handler is used to resize the ColorPanel to fill its
parent panel, which could also have been done with a sizer. The
difference with yours is that you didn't have the extra panel in there
so it was resizing the page window relative to the notebook instead.

Ah, I see! Thank you very much for the explanation :slight_smile:

Dietrich

(Sorry for my late reply!)

···

On Fri, 2010-03-19 at 23:16 -0700, Robin Dunn wrote:

On 3/18/10 8:20 AM, Dietrich Bollmann wrote:

--
Robin Dunn
Software Craftsman
http://wxPython.org