Using FlexGridSizer to render multiple controls/elements

Would some of you mind telling me if I’m missing out on anything important relating to layout, sizer implementation, etc. in following bit of test code - just using this to try figure out if it will actually render something like a data capture UI as I want it to - readable, clean, in order/sequence, etc. etc. - and, yes, this is just test code thus far, based on tutorial material, so, no, won’t all make sense - for example, the accessible_output.speech.Speaker implementation is my form of spoken debug information rendering, and I am sort of dynamically rendering lots of test controls just for test purposes thus far:

#start code

import wx
from accessible_output import speech

class Example(wx.Frame):
spk = None

def __init__(self, *args, **kwargs):
    self.spk = speech.Speaker()
    #self.spk.output("init")
    super(Example, self).__init__(*args, **kwargs)
    self.InitUI()
    self.Centre()
    self.Maximize()
    self.Show()
    self.SetWindowStyle(self.GetWindowStyle() + wx.VSCROLL)
    #self.spk.output("shown")
   
def InitUI(self):
    #self.spk.output("initUI")
    try:
        menuBar = wx.MenuBar()
        fileMenu = wx.Menu()
        exitItem = fileMenu.Append(wx.ID_EXIT, "&Quit\tCtrl+Q", "Close the program")
        menuBar.Append(fileMenu, "&File")
        self.SetMenuBar(menuBar)
        self.Bind(wx.EVT_MENU, self.quitProgram, exitItem)
        panel = wx.Panel(self)
        vBox = wx.BoxSizer(wx.VERTICAL)
        lStatics = []
        lControls = []
        #normal textboxes
        for I in range(10):
            lStatics.append(wx.StaticText(panel, label="text " + str(I)))
            lControls.append(wx.TextCtrl(panel))
            lControls[I].Bind(wx.EVT_SET_FOCUS, self.tellPos, lControls[I])
        #multiline text boxes, with label above them
        for I in range(5):
            lStatics.append(wx.StaticText(panel, label="Multi-line " + str(I)))
            lControls.append(wx.TextCtrl(panel, style=wx.TE_MULTILINE))
        #radio buttons
        lRBHorizontals = []
        for I in range(5):
            lRBHorizontals.append(wx.BoxSizer(wx.HORIZONTAL))
            lRBHorizontals[I].Add(wx.RadioButton(parent=panel, id=wx.ID_ANY, label=str(I)+"1", style=wx.RB_GROUP), flag=wx.LEFT, border=10)
            lRBHorizontals[I].Add(wx.RadioButton(parent=panel, id=wx.ID_ANY, label=str(I)+"2"), flag=wx.CENTER, border=10)
            lRBHorizontals[I].Add(wx.RadioButton(parent=panel, id=wx.ID_ANY, label=str(I)+"3"), flag=wx.RIGHT, border=10)
            lStatics.append(wx.StaticText(panel, label="radio button grouping " + str(I)))
            lControls.append(lRBHorizontals[I])
        #more text boxes
        for I in range(11, 15):
            lStatics.append(wx.StaticText(panel, label="text " + str(I)))
            lControls.append(wx.TextCtrl(panel))
            lControls[I].Bind(wx.EVT_SET_FOCUS, self.tellPos, lControls[I])
        #add all statics and controls to fgs
        sOut = str(len(lStatics)) + " statics " + str(len(lControls)) + " controls"
        #self.spk.output(sOut)
        fgs = wx.FlexGridSizer(rows=len(lStatics), cols=2, vgap=9, hgap=25)
        for I in range(len(lStatics)):
            fgs.AddMany([(lStatics[I]), (lControls[I], 1, wx.EXPAND)])

#add fgs to vBox
vBox.Add(fgs, flag=wx.ALIGN_CENTER|wx.CENTER, border=10)
#buttons at bottom
hBoxButtons = wx.BoxSizer(wx.HORIZONTAL)
btn1 = wx.Button(panel, label=’&Ok’, size=(70, 30))
btn1.Bind(wx.EVT_SET_FOCUS, self.tellPos, btn1)
hBoxButtons.Add(btn1)
btn2 = wx.Button(panel, label=’&Close’, size=(70, 30))
btn2.Bind(wx.EVT_SET_FOCUS, self.tellPos, btn2)
hBoxButtons.Add(btn2, flag=wx.LEFT|wx.BOTTOM, border=5)
vBox.Add(hBoxButtons, flag=wx.ALIGN_RIGHT|wx.RIGHT, border=10)
panel.SetSizer(vBox)
self.Bind(wx.EVT_BUTTON, self.nothingHappening, btn1)
self.Bind(wx.EVT_BUTTON, self.quitProgram, btn2)
except Exception as exc:
dlg = wx.MessageDialog(parent=self, message=str(exc), caption=“Error message”, style=wx.OK)
dlg.ShowModal()
dlg.Destroy()
#self.spk.output("exception occurred - " + str(exc))
finally:
sNada = “”
#self.spk.output(“initUI done?”)
#end of initUI

def nothingHappening(self, evt):
    dlg = wx.MessageDialog(parent=self, message="You clicked a button", caption="Button clicked", style=wx.OK)
    dlg.ShowModal()
    dlg.Destroy()
#end of nothingHappening

def quitProgram(self, evt):
    self.Close()
#end of quitProgram

def tellPos(self, event):
    sOut = "position " + str(event.GetEventObject().Position)
    sOut += " dimensions " + str(event.GetEventObject().Size)
    #self.spk.output(sOut, interrupt=True)
#end of tellPos

#end of class Example

if name == ‘main’:
app = wx.App()
Example(None, title=“Lots of fields/controls”)
app.MainLoop()

#end code

And, part of why am asking here is that current, sighted test user isn’t terribly technical, so not easiest to ask/get answers for questions ask them about interface etc.

(lastly, while normally work with tab character for indentation, since that operates bit better along with my screenreader software, I did a search/replace to use 4 space characters in above code sample - hope worked alright)

TIA

Jacob Kruger
Blind Biker
Skype: BlindZA
‘…fate had broken his body, but not his spirit…’

Jacob Kruger wrote:

Would some of you mind telling me if I'm missing out on anything
important relating to layout, sizer implementation, etc. in following
bit of test code - just using this to try figure out if it will actually
render something like a data capture UI as I want it to - readable,
clean, in order/sequence, etc. etc. - and, yes, this is just test code
thus far, based on tutorial material, so, no, won't all make sense - for
example, the accessible_output.speech.Speaker implementation is my form
of spoken debug information rendering, and I am sort of dynamically
rendering lots of test controls just for test purposes thus far:

And, part of why am asking here is that current, sighted test user isn't
terribly technical, so not easiest to ask/get answers for questions ask
them about interface etc.
(lastly, while normally work with tab character for indentation, since
that operates bit better along with my screenreader software, I did a
search/replace to use 4 space characters in above code sample - hope
worked alright)

Hi Jacob. You are on the right path.

The layout was good except for a couple things. First since you maximized the frame it ends up with a relatively narrow column of widgets in the middle of the frame, with lots of empty space on either side. And due to the alignment flag the buttons are placed way over on the right, and at least on my screen they are so far from the other widgets that they are almost unnoticeable.

What you probably want to do instead of maximizing the frame is to fit it to the size of the contents. If you put your top-level panel in a sizer assigned to the frame and use the frame's self.Fit() method then it will resize the frame to the min size needed to display all the widgets. Fit should work without the frame having a sizer too, but it is more automatic with the sizer.

The next issue was lack of spacing around the items in the vBox sizer. I see that you used border=10 args, but you forgot to tell it which sides should have that border. Adding wx.ALL to the flags value took care of that problem.

I have attached your sample with my modifications marked with "#* modified" comments so you can find them easier.

jacob-layout.py (4.9 KB)

···

--
Robin Dunn
Software Craftsman

Excellent!

Thanks.

I was actually testing Maximize() and forgot about it, since wasn't sure if a form of auto fit would take place, so, definitely better to know about handling it.

Might also, in terms of button locations, implement more like 4 columns in total, in the FlexGridSizer, since seems like using either wx.FlexGridSizer.Add() or wx.FlexGridSizer.AddMany() just automatically inserts into next cell in each row, before moving over to next row each time?

One last question - for now - does this line actually implement a form of vertical scrollbar if needed:
self.SetWindowStyle(self.GetWindowStyle() + wx.VSCROLL)

Wasn't sure how to determine if, by default, the window would automatically implement a scrollbar if needed, partly since the value returned by self.GetWindowStyle() is a combination/addition of something like the relevant wx.* values, so it's just - to me anyway, a large integer value at moment?

Thanks again

Jacob Kruger
Blind Biker
Skype: BlindZA
'...fate had broken his body, but not his spirit...'

···

----- Original Message ----- From: "Robin Dunn" <robin@alldunn.com>
To: <wxpython-users@googlegroups.com>
Sent: Monday, December 30, 2013 10:53 PM
Subject: Re: [wxPython-users] Using FlexGridSizer to render multiple controls/elements

Jacob Kruger wrote:

Would some of you mind telling me if I'm missing out on anything
important relating to layout, sizer implementation, etc. in following
bit of test code - just using this to try figure out if it will actually
render something like a data capture UI as I want it to - readable,
clean, in order/sequence, etc. etc. - and, yes, this is just test code
thus far, based on tutorial material, so, no, won't all make sense - for
example, the accessible_output.speech.Speaker implementation is my form
of spoken debug information rendering, and I am sort of dynamically
rendering lots of test controls just for test purposes thus far:

And, part of why am asking here is that current, sighted test user isn't
terribly technical, so not easiest to ask/get answers for questions ask
them about interface etc.
(lastly, while normally work with tab character for indentation, since
that operates bit better along with my screenreader software, I did a
search/replace to use 4 space characters in above code sample - hope
worked alright)

Hi Jacob. You are on the right path.

The layout was good except for a couple things. First since you
maximized the frame it ends up with a relatively narrow column of
widgets in the middle of the frame, with lots of empty space on either
side. And due to the alignment flag the buttons are placed way over on
the right, and at least on my screen they are so far from the other
widgets that they are almost unnoticeable.

What you probably want to do instead of maximizing the frame is to fit
it to the size of the contents. If you put your top-level panel in a
sizer assigned to the frame and use the frame's self.Fit() method then
it will resize the frame to the min size needed to display all the
widgets. Fit should work without the frame having a sizer too, but it
is more automatic with the sizer.

The next issue was lack of spacing around the items in the vBox sizer.
I see that you used border=10 args, but you forgot to tell it which
sides should have that border. Adding wx.ALL to the flags value took
care of that problem.

I have attached your sample with my modifications marked with "#*
modified" comments so you can find them easier.

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

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Jacob Kruger wrote:

Might also, in terms of button locations, implement more like 4 columns
in total, in the FlexGridSizer, since seems like using either
wx.FlexGridSizer.Add() or wx.FlexGridSizer.AddMany() just automatically
inserts into next cell in each row, before moving over to next row each
time?

Yes that is correct, all cells in the wx.FlexGridSizer are filled as you add items. And if you happen to have some spots in the where you don't want to put anything then you can spacers, or simply a (w,h) tuple, to use up that cell and then move on to the next. Another alternative is to use the wx.GridBagSizer, which lets you specify the row,col of each item as you add it, and items can also span more than one row or column.

One last question - for now - does this line actually implement a form
of vertical scrollbar if needed:
self.SetWindowStyle(self.GetWindowStyle() + wx.VSCROLL)

That isn't quite what you are looking for. Setting the wx.VSCROLL flag on a wx.Panel or plain wx.Window basically just turns on the scrollbar, but it doesn't manage any kind of automatic scrolling or anything. That would be left up to the class to implement for itself. There are other classes that will do that for you however. Check out wx.ScrolledWindow, and in the wxPython library there is wx.lib.scrolledpanel which works well for scrollable data entry forms. In either case the scrollbars will be shown or hidden based on the virtual size of the window.

···

--
Robin Dunn
Software Craftsman

Ok, perfect.

Will look at other types of sizers as well, especially since might want to at times make some of the 'hosted' controls span columns/rows, but, think this should/will work well enough for now, and the relevant user base will most likely be clicking in fields with mouse, so they'd also use scrollbar themselves easily enough, if the window's contents won't fit on screen normally as such, so wouldn't necessarily need it to handle auto-scrolling as such.

But, most likely the wx.ScrolledWindow would best suit this situation for now, to 'host' all other content, since am not at moment too worried about automatic scrolling, but, more to do with it being possible to just fit all controls onto part of interface, and have them available if necessary.

Along lines of that, will a window/frame try to auto-fit inside the available space on screen, or should I be double checking sort of rendering size if necessary, and/or specifying size of frame before .Show() is called, or maybe do something like call a .Refresh after having possibly changed it's rendering .Size tuple property/attribute?

Suppose could check rendering size of FlexGridSizer element, and then make sure window is sized large enough to contain it?

Thanks again

Stay well

Jacob Kruger
Blind Biker
Skype: BlindZA
'...fate had broken his body, but not his spirit...'

···

----- Original Message ----- From: "Robin Dunn" <robin@alldunn.com>
To: <wxpython-users@googlegroups.com>
Sent: Monday, December 30, 2013 11:54 PM
Subject: Re: [wxPython-users] Using FlexGridSizer to render multiple controls/elements

Jacob Kruger wrote:

Might also, in terms of button locations, implement more like 4 columns
in total, in the FlexGridSizer, since seems like using either
wx.FlexGridSizer.Add() or wx.FlexGridSizer.AddMany() just automatically
inserts into next cell in each row, before moving over to next row each
time?

Yes that is correct, all cells in the wx.FlexGridSizer are filled as you add items. And if you happen to have some spots in the where you don't want to put anything then you can spacers, or simply a (w,h) tuple, to use up that cell and then move on to the next. Another alternative is to use the wx.GridBagSizer, which lets you specify the row,col of each item as you add it, and items can also span more than one row or column.

One last question - for now - does this line actually implement a form
of vertical scrollbar if needed:
self.SetWindowStyle(self.GetWindowStyle() + wx.VSCROLL)

That isn't quite what you are looking for. Setting the wx.VSCROLL flag on a wx.Panel or plain wx.Window basically just turns on the scrollbar, but it doesn't manage any kind of automatic scrolling or anything. That would be left up to the class to implement for itself. There are other classes that will do that for you however. Check out wx.ScrolledWindow, and in the wxPython library there is wx.lib.scrolledpanel which works well for scrollable data entry forms. In either case the scrollbars will be shown or hidden based on the virtual size of the window.

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

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

And, after posting that reply, 'saw' you'd modd'ed the code to add in self.Fit() for the wx.Frame before self.Show(), so you'd already answered that question :slight_smile:

Stay well

Jacob Kruger
Blind Biker
Skype: BlindZA
'...fate had broken his body, but not his spirit...'

···

----- Original Message ----- From: "Jacob Kruger" <jacob@blindza.co.za>
To: <wxpython-users@googlegroups.com>
Sent: Tuesday, December 31, 2013 12:37 AM
Subject: Re: [wxPython-users] Using FlexGridSizer to render multiple controls/elements

Ok, perfect.

Will look at other types of sizers as well, especially since might want to at times make some of the 'hosted' controls span columns/rows, but, think this should/will work well enough for now, and the relevant user base will most likely be clicking in fields with mouse, so they'd also use scrollbar themselves easily enough, if the window's contents won't fit on screen normally as such, so wouldn't necessarily need it to handle auto-scrolling as such.

But, most likely the wx.ScrolledWindow would best suit this situation for now, to 'host' all other content, since am not at moment too worried about automatic scrolling, but, more to do with it being possible to just fit all controls onto part of interface, and have them available if necessary.

Along lines of that, will a window/frame try to auto-fit inside the available space on screen, or should I be double checking sort of rendering size if necessary, and/or specifying size of frame before .Show() is called, or maybe do something like call a .Refresh after having possibly changed it's rendering .Size tuple property/attribute?

Suppose could check rendering size of FlexGridSizer element, and then make sure window is sized large enough to contain it?

Thanks again

Stay well

Jacob Kruger
Blind Biker
Skype: BlindZA
'...fate had broken his body, but not his spirit...'

----- Original Message ----- From: "Robin Dunn" <robin@alldunn.com>
To: <wxpython-users@googlegroups.com>
Sent: Monday, December 30, 2013 11:54 PM
Subject: Re: [wxPython-users] Using FlexGridSizer to render multiple controls/elements

Jacob Kruger wrote:

Might also, in terms of button locations, implement more like 4 columns
in total, in the FlexGridSizer, since seems like using either
wx.FlexGridSizer.Add() or wx.FlexGridSizer.AddMany() just automatically
inserts into next cell in each row, before moving over to next row each
time?

Yes that is correct, all cells in the wx.FlexGridSizer are filled as you add items. And if you happen to have some spots in the where you don't want to put anything then you can spacers, or simply a (w,h) tuple, to use up that cell and then move on to the next. Another alternative is to use the wx.GridBagSizer, which lets you specify the row,col of each item as you add it, and items can also span more than one row or column.

One last question - for now - does this line actually implement a form
of vertical scrollbar if needed:
self.SetWindowStyle(self.GetWindowStyle() + wx.VSCROLL)

That isn't quite what you are looking for. Setting the wx.VSCROLL flag on a wx.Panel or plain wx.Window basically just turns on the scrollbar, but it doesn't manage any kind of automatic scrolling or anything. That would be left up to the class to implement for itself. There are other classes that will do that for you however. Check out wx.ScrolledWindow, and in the wxPython library there is wx.lib.scrolledpanel which works well for scrollable data entry forms. In either case the scrollbars will be shown or hidden based on the virtual size of the window.

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

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.