UCU110
UCU110

Reputation: 413

wxPython: Dynamically Flow Buttons to Next Row on Window-Resize

The following wxPython sample code is meant to create some buttons and then add them to a horizontal panel in such a way that the buttons should flow to a new row when they no longer fit in the panel.

In addition, the buttons should change position (increase/decrease the number of rows) as the user resizes the frame/window. (please see http://wxpython-users.1045709.n5.nabble.com/Button-wrap-td2365760.html).

    import wx

words = ['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur',
'adipisicing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt',
'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua', 'ut', 'enim', 'ad',
'minim', 'veniam', 'quis', 'nostrud', 'exercitation', 'ullamco',
'laboris', 'nisi', 'ut', 'aliquip', 'ex', 'ea', 'commodo',
'consequat', 'duis', 'aute', 'irure', 'dolor', 'in', 'reprehenderit',
'in', 'voluptate', 'velit', 'esse', 'cillum', 'dolore', 'eu',
'fugiat', 'nulla', 'pariatur', 'excepteur', 'sint', 'occaecat',
'cupidatat', 'non', 'proident', 'sunt', 'in', 'culpa', 'qui',
'officia', 'deserunt', 'mollit', 'anim', 'id', 'est', 'laborum']

class Example(wx.Frame):

    def __init__(self, *args, **kwargs):
        super(Example, self).__init__(*args, **kwargs) 
        self.InitUI()
        self.Ctrls()
        i = 0
        for word in words:
            i += 1
            print " word = ",word, "  ",i
            self.gridSizer.SetCols(5)
            button = wx.Button(self.panel, -1, word, size=wx.Size(70,25))
            self.gridSizer.Add(button, 1, wx.EXPAND)
        self.panel.Layout()

    def InitUI(self):

        self.SetSize((800, 400))
        self.SetTitle('Dynamically Flow Buttons to Next Row on Window-Resize')
        self.Centre()
        self.Show(True)

    def Sizers(self):

        self.gridSizer = wx.GridSizer(cols=0, hgap=1, rows=1, vgap=1)
        self.panel.SetSizer(self.gridSizer)

    def Ctrls(self):

        self.Bind(wx.EVT_SIZE, self.OnFrameSize)
        self.panel = wx.Panel(parent=self,pos=wx.Point(0,0), size=wx.Size(750,550), style=wx.TAB_TRAVERSAL)
        self.Sizers()

    def OnFrameSize(self, event):

        self.UpdateButtonLayout()
        event.Skip()

    def UpdateButtonLayout(self):

        panelwidth = self.panel.GetSize().width
        print panelwidth
        factor = panelwidth / 70
        print "Factor: ", factor
        self.gridSizer.SetCols(factor)
        self.panel.Layout()

def main():

    ex = wx.App()
    Example(None)
    ex.MainLoop()

if __name__ == '__main__':
    main()

When I run this example I get the following error;

Traceback (most recent call last):
  File "test.py", line 68, in <module>
    main()
  File "test.py", line 64, in main
    Example(None)
  File "test.py", line 26, in __init__
    self.gridSizer.Add(button, 1, wx.EXPAND)
  File "/usr/local/lib/wxPython-3.0.2.0/lib/python2.7/site-packages/wx-3.0-osx_cocoa/wx/_core.py", line 14457, in Add
    return _core_.Sizer_Add(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion "Assert failure" failed at /BUILD/wxPython-src-3.0.2.0/src/common/sizer.cpp(1401) in DoInsert(): too many items (6 > 5*1) in grid sizer (maybe you should omit the number of either rows or columns?)

The problem is with line number 26. When I comment that out I see only a single button. Can someone suggest some changes to make it work? Thanks.

Upvotes: 0

Views: 716

Answers (2)

Rolf of Saxony
Rolf of Saxony

Reputation: 22458

A late answer but using a FlexGridSizer would achieve the same result, where the number of rows is set to zero.
In the questions code, you would also have to delete the line self.gridSizer.SetCols(5)

import wx

words = ['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur',
'adipisicing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt',
'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua', 'ut', 'enim', 'ad',
'minim', 'veniam', 'quis', 'nostrud', 'exercitation', 'ullamco',
'laboris', 'nisi', 'ut', 'aliquip', 'ex', 'ea', 'commodo',
'consequat', 'duis', 'aute', 'irure', 'dolor', 'in', 'reprehenderit',
'in', 'voluptate', 'velit', 'esse', 'cillum', 'dolore', 'eu',
'fugiat', 'nulla', 'pariatur', 'excepteur', 'sint', 'occaecat',
'cupidatat', 'non', 'proident', 'sunt', 'in', 'culpa', 'qui',
'officia', 'deserunt', 'mollit', 'anim', 'id', 'est', 'laborum']

class Example(wx.Frame):

    def __init__(self, *args, **kwargs):
        super(Example, self).__init__(*args, **kwargs) 
        self.InitUI()
        self.Ctrls()
        i = 0
        for word in words:
            i += 1
            print " word = ",word, "  ",i
            button = wx.Button(self.panel, -1, word, size=wx.Size(70,25))
            self.gridSizer.Add(button, 1, wx.EXPAND)
        self.panel.Layout()

    def InitUI(self):

        self.SetSize((800, 400))
        self.SetTitle('Dynamically Flow Buttons to Next Row on Window-Resize')
        self.Centre()
        self.Show(True)

    def Sizers(self):

        self.gridSizer = wx.FlexGridSizer(0,5,1,1)
        self.panel.SetSizer(self.gridSizer)

    def Ctrls(self):

        self.Bind(wx.EVT_SIZE, self.OnFrameSize)
        self.panel = wx.Panel(parent=self,pos=wx.Point(0,0), size=wx.Size(750,550), style=wx.TAB_TRAVERSAL)
        self.Sizers()

    def OnFrameSize(self, event):

        self.UpdateButtonLayout()
        event.Skip()

    def UpdateButtonLayout(self):

        panelwidth = self.panel.GetSize().width
        print panelwidth
        factor = panelwidth / 70
        print "Factor: ", factor
        self.gridSizer.SetCols(factor)
        self.panel.Layout()

def main():

    ex = wx.App()
    Example(None)
    ex.MainLoop()

if __name__ == '__main__':
    main()

Upvotes: 0

Mike Driscoll
Mike Driscoll

Reputation: 33111

What you are looking for is the wx.WrapSizer. I updated your code a bit to use it instead:

import wx

words = ['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur',
'adipisicing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt',
'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua', 'ut', 'enim', 'ad',
'minim', 'veniam', 'quis', 'nostrud', 'exercitation', 'ullamco',
'laboris', 'nisi', 'ut', 'aliquip', 'ex', 'ea', 'commodo',
'consequat', 'duis', 'aute', 'irure', 'dolor', 'in', 'reprehenderit',
'in', 'voluptate', 'velit', 'esse', 'cillum', 'dolore', 'eu',
'fugiat', 'nulla', 'pariatur', 'excepteur', 'sint', 'occaecat',
'cupidatat', 'non', 'proident', 'sunt', 'in', 'culpa', 'qui',
'officia', 'deserunt', 'mollit', 'anim', 'id', 'est', 'laborum']

class Example(wx.Frame):

    def __init__(self, *args, **kwargs):
        super(Example, self).__init__(*args, **kwargs) 
        self.InitUI()
        self.Ctrls()
        i = 0
        for word in words:
            i += 1
            print " word = ",word, "  ",i
            button = wx.Button(self.panel, -1, word, size=wx.Size(70,25))
            self.wrapSizer.Add(button, 1, wx.EXPAND)
        self.Show(True)
        self.panel.Layout()

    def InitUI(self):
        self.SetSize((800, 400))
        self.SetTitle('Dynamically Flow Buttons to Next Row on Window-Resize')
        self.Centre()


    def Sizers(self):
        self.wrapSizer = wx.WrapSizer()
        self.panel.SetSizer(self.wrapSizer)

    def Ctrls(self):
        self.panel = wx.Panel(parent=self,pos=wx.Point(0,0), size=wx.Size(750,550), style=wx.TAB_TRAVERSAL)
        self.Sizers()



def main():

    ex = wx.App()
    Example(None)
    ex.MainLoop()

if __name__ == '__main__':
    main()

Note that you cannot set the number of columns. You can read more about this fun widget at the following:

Upvotes: 1

Related Questions