wxPython newbie ... gridbagsizer: all I want to do is replace a widget at a specific row and column with another widget

Appreciate any answers to this question…

I am using a gridbagsizer to layout a grid …this works I can put down static text and print out the grid

Now this should be simple and easy, all I want to do is replace a widget at a specific row and column with another widget and I am at a complete loss how to do this…after hours of googling and trying different things

E.g. Generate a placeholder grid

gs1 = wx.GridBagSizer()

for x in range (1,2):
for y in range (1,9):
gs1.Add(20, 20, pos = (x,y))

Now I can get the gbsizeritem like this

Item = gs1.FindItemAtPosition(pos=(1,7))

but I can not find any way to replace the widget at 1,7 with another one!!!

Any suggestions?

Background … I am making an ancestor class and the idea is to add slots to place the widgets, this should make laying out forms easier.

It's certainly possible. I'm doing it in wxGlade.

Try these two methods, at least one should work:

- use Item.AssignWindow(new_widget)
this would probably not allow to modify row/col span

- detach the old item and then add the new widget as usual:

     gs1\.Detach\(widget\) or gs1\.Detach\(Item\.GetWindow\(\)\)

If none of these work, please post example code.

Regards,

Dietmar

···

On 6/6/2018 8:30 PM, Just Me wrote:

Any suggestions?

On a separate note, do you understand that this is only going to create a 1 x 8 array? x will only be 1, and y will be 1, 2, 3, 4, 5, 6, 7, 8. Is that what you intended?

···

On Jun 6, 2018, at 11:30 AM, Just Me davidaworboys@gmail.com wrote:

gs1 = wx.GridBagSizer()

for x in range (1,2):
for y in range (1,9):
gs1.Add(20, 20, pos = (x,y))


Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Appreciate the replies and Dietmar and Tim … I sure wish the gridsizers had an add widget method something like I have now in my Slot class, seems like an obvious thing to have.

So this is what I came up with - it kind of works, still some funnies, and I am refining it, but I will appreciate any suggestions for improvement as I have only been learning python a few weeks (but I am an old software hand in other languages)

The slot panel class

__class Slot_Panel(wx.Panel):
__Col_Max = 0
__Row_Max = 0
__Parent_Window = None
__Sizer = None

def __init__(self,parent, max_slot_rows = 1, max_slot_cols = 1):
    Row       = 0       
    Col       = 0       
    Spacer    = None
                                   
    #assert isinstance(parent, ??),"parent must be wx.Frame" #TODO What is the object type of a wx.Frame?
    assert isinstance(max_slot_rows, int) and max_slot_rows,"max_slot_rows must be an integer > 0"
    assert isinstance(max_slot_cols, int) and max_slot_cols,"max_slot_cols must be an integer > 0"
   
    super().__init__(parent = parent)
   
    self.__Parent_Window = parent
    self.__Sizer = wx.GridBagSizer(max_slot_rows, max_slot_cols)
   
    self.__Col_Max = max_slot_cols
    self.__Row_Max = max_slot_rows
    for Row in range (1,max_slot_rows + 1):
        for Col in range (1,max_slot_cols + 1):               
            #for debug - write out slot number               
            Spacer = wx.StaticText(self, id = wx.ID_ANY, label = "Control Slot "+str(Row * Col))               
            #Spacer = wx.StaticText(self, -1, "")
            self.__Sizer.Add(Spacer, pos = (Row,Col), span = (1, 1), flag = wx.EXPAND|wx.ALL, border = 5)
           
def Add_Control(self, new_widget,  slot_row =1, slot_col = 0):
   
    #assert isinstance(new_widget, ??),"new_widget must be wx.Control" #TODO What is the object type of a wx.control?
    assert isinstance(slot_row, int) and slot_row > 0 and slot_row <= self.__Row_Max,"slot row must be an integer > 0 and <= max_slot_rows"
    assert isinstance(slot_col, int) and slot_col > 0 and slot_col <= self.__Col_Max,"slot col must be an integer > 0 and <= max_slot_cols"
   
    Old_Widget = self.__Sizer.FindItemAtPosition(wx.GBPosition(slot_row,slot_col))
           
    self.__Sizer.Detach(Old_Widget.GetWindow())       
                           
    self.__Sizer.Add(new_widget, pos = (slot_row,slot_col), span = (1, 1), flag = wx.EXPAND|wx.ALL, border = 5)
    self.__Sizer.Layout()__       

An MDI Child using this class

**class MDI_Child(wx.MDIChildFrame):
Win_Id = ["",0]

def __init__(self,frame,id=-1,title = ""):
    super().__init__(frame,id,title)
   
   
    Control_Panel = Slot_Panel(self,max_slot_rows = 8, max_slot_cols = 8 )
    Button_Panel  = Slot_Panel(self,max_slot_rows = 1, max_slot_cols = 8 )
                   
    Button_Panel.Add_Control(wx.Button(Button_Panel,label = "Ok"),slot_row = 1, slot_col = 6)
    Button_Panel.Add_Control(wx.Button(Button_Panel,label = "Cancel"),slot_row = 1, slot_col = 7)
    Button_Panel.Add_Control(wx.Button(Button_Panel,label = "Append"),slot_row = 1, slot_col = 8)
   
    Control_Panel.SetBackgroundColour('light blue')
    Button_Panel.SetBackgroundColour('blue')
                           
    sizer = wx.BoxSizer(wx.VERTICAL)
    sizer.Add(Control_Panel, 1, wx.EXPAND)
    sizer.Add(Button_Panel, 1, wx.EXPAND)
    self.SetSizer(sizer)**

In the program this looks like this -

I’m still wondering why you start the ranges at 1. Every language
has its “native” starting point for arrays and ranges. In Visual
Basic, for example, the natural starting point is 1. But in Python,
like C and C++, things almost always start with 0. If a
GridBagSizer has 5 rows and 5 columns, those rows and columns are
numbered 0 through 4. That’s why range(5) returns [0,1,2,3,4].
It’s just the way Python is done.
If you did that, you’d need to change your StaticText to something
like “Control Slot %d,%d” % (Row,Col).

···

Just Me wrote:

      So this is what I came up with - it kind of works, still

some funnies, and I am refining it, but I will appreciate any
suggestions for improvement as I have only been learning
python a few weeks (but I am an old software hand in other
languages)

for Row in range (1,max_slot_rows + 1):
__ for Col in range (1,max_slot_cols +
1):
#for debug - write out slot
number
Spacer = wx.StaticText(self, id = wx.ID_ANY,
label = "Control Slot "+str(Row * Col))
#Spacer = wx.StaticText(self, -1, “”)
self.__Sizer.Add(Spacer, pos = (Row,Col),
span = (1, 1), flag = wx.EXPAND|wx.ALL, border = 5)
__

-- Tim Roberts, Providenza & Boekelheide, Inc.

timr@probo.com

Maybe you should write what you actually want to accomplish.
There are not too many use cases where on-the-fly modifications like these make sense.
I know that it's possible to do these as I'm working on wxGlade and for a GUI builder it actually makes sense to change the layout.
For 'normal' user programs it seems hard to imagine such a use case. If you need dynamic dialogs / forms, just create, destroy and re-create them on the fly instead of re-using parts of it.

Regards,
Dietmar

Hi Tim,

I start at 1 in the ranges because I find it easier to think of screen form layouts as starting at row 1, col 1. For example, I might put the Save button at Row 1 Col 3. Perhaps this is odd, as I am comfortable with zero based arrays and such like from other languages!!

Appreciate the feedback, as always

···

On Thursday, 7 June 2018 14:20:30 UTC-4, Tim Roberts wrote:

Just Me wrote:

      So this is what I came up with - it kind of works, still

some funnies, and I am refining it, but I will appreciate any
suggestions for improvement as I have only been learning
python a few weeks (but I am an old software hand in other
languages)

for Row in range (1,max_slot_rows + 1):
__ for Col in range (1,max_slot_cols +
1):
#for debug - write out slot
number
Spacer = wx.StaticText(self, id = wx.ID_ANY,
label = "Control Slot "+str(Row * Col))
#Spacer = wx.StaticText(self, -1, “”)
self.__Sizer.Add(Spacer, pos = (Row,Col),
span = (1, 1), flag = wx.EXPAND|wx.ALL, border = 5)
__

I'm still wondering why you start the ranges at 1.  Every language

has its “native” starting point for arrays and ranges. In Visual
Basic, for example, the natural starting point is 1. But in Python,
like C and C++, things almost always start with 0. If a
GridBagSizer has 5 rows and 5 columns, those rows and columns are
numbered 0 through 4. That’s why range(5) returns [0,1,2,3,4].
It’s just the way Python is done.

If you did that, you'd need to change your StaticText to something

like “Control Slot %d,%d” % (Row,Col).

-- Tim Roberts, ti...@probo.com
Providenza & Boekelheide, Inc.

I find it easier to design screen forms as a grid - that is why I like slots as I can lay the form out by specifying the slot in which each control element is placed.

By building the ancestor Slot_Panel, and encapsulating most of the grunt work , I can inherit from that and layout my form quickly, by doing what I did with the posted example - one control on each line I still have to add spanning and a few other things to get the magic I want. This should lead to faster/shorter coding with fewer bugs.

Thank you for pointing me in the right direction :slight_smile:

···

On Thursday, 7 June 2018 14:36:43 UTC-4, Dietmar Schwertberger wrote:

Maybe you should write what you actually want to accomplish.

There are not too many use cases where on-the-fly modifications like
these make sense.

I know that it’s possible to do these as I’m working on wxGlade and for
a GUI builder it actually makes sense to change the layout.

For ‘normal’ user programs it seems hard to imagine such a use case. If
you need dynamic dialogs / forms, just create, destroy and re-create
them on the fly instead of re-using parts of it.

Regards,

Dietmar

It seems that you actually prefer to place widgets by pixels instead of using sizers. You just plan to use a grid sizer instead of pixel coordinates. wxPython does not require a sizer. You could place by pixels if you absolutely want.

Honestly, this does not sound like a good plan. wx has a quite useful set of sizers for different purposes. The gridbag sizer is the one that I have never needed at all during 18 years of using wx. The box, static box and flex grid sizers are probably all you will ever need.

Still, this does not yet explain why you want to replace widgets within a gridbag sizer. If your plan is to "design" your layout on the command line by changing it on the fly, then I would recommend using a GUI builder like wxGlade. These are actually made for designing visually. No need to re-invent the wheel.

Regards,

Dietmar

···

On 6/7/2018 8:49 PM, Just Me wrote:

I find it easier to design screen forms as a grid - that is why I like slots as I can lay the form out by specifying the slot in which each control element is placed.

Just Me wrote:

I start at 1 in the ranges because I find it easier to think of screen
form layouts as starting at row 1, col 1. For example, I might put the
Save button at Row 1 Col 3.

Yes, but the grid itself starts counting at 0. Your grid size HAS a row
0 and column 0, and I'm afraid at some point you're going to get burned
because you have an inaccurate mental model.

I guess it's just something to think about.

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Hi Dietmar,

I have done many applications with pixel placed widgets in other languages and toolkits, but they often do not play nice when Windows are resized. An array of slots does seem like a good and obvious way to layout a form - without absolute pixel positioning - and I am hoping that by using slots as hints and the gridbagsizer to manage resizing then Windows resizing will not be as big an issue.

As a wx newbie, I will say choosing the right sizer and getting sizers to position things where one wants is anything but obvious - for me using slots, solves this problem as I am now getting the layout I want. And I have done a lot of wx reading in the last couple of days!

I did look at wxGlade but was not all that happy with the tool … but I will not dig into that here

At any rate, I guess we all do everything a little different.

···

On Thursday, 7 June 2018 15:08:28 UTC-4, Dietmar Schwertberger wrote:

On 6/7/2018 8:49 PM, Just Me wrote:

I find it easier to design screen forms as a grid - that is why I
like slots as I can lay the form out by specifying the slot in which
each control element is placed.

It seems that you actually prefer to place widgets by pixels instead of
using sizers. You just plan to use a grid sizer instead of pixel
coordinates. wxPython does not require a sizer. You could place by
pixels if you absolutely want.

Honestly, this does not sound like a good plan. wx has a quite useful
set of sizers for different purposes. The gridbag sizer is the one that
I have never needed at all during 18 years of using wx. The box, static
box and flex grid sizers are probably all you will ever need.

Still, this does not yet explain why you want to replace widgets within
a gridbag sizer. If your plan is to “design” your layout on the command
line by changing it on the fly, then I would recommend using a GUI
builder like wxGlade. These are actually made for designing visually. No
need to re-invent the wheel.

Regards,

Dietmar