Table-like Form in an GridBoxSizer

For a SQL-Database-Frontend I build table-like forms. Therefore i dynamicaly build wx.Controls like wx.TextCtrl or wx.Choice and place them in an wx.GridBagSizer. So we have:

  1. For each row in the database a row in the sizer with a wx.Control and the data coming from the database as value of this wx.Control.
  2. Additional this table get a top row with wx.StaticText for each column as Labels.
  3. A bottom row with wx.Controls without contents to enter new data.
  4. Finaly there should be a navigation panal above the table to move around in the table, search in it and do some more features.

Bildschirmfoto_2020-11-28_21-31-51

In this table the user should edit the data, move around, delete data and in the last line enter new data. All that without need to explicit save the data by pressing a button.

Therefore I have to control

  1. where the cursor and/or the focus is an from where it comes. It would be enough to get out in which row the cursor/focus is and where it has been. It could be something like a ChangedRowEvent. That has to be independent of how the user change the row, using the TAB-key, arrow-keys, ENTER-key or even the mouse
  2. whether the data in the row we leaved was changed. It could be a boolean variable DataChangedInRow which could be set to True by some event-handlers. But which events are relevant for that?

It would be great to get some hints or directions for further search.

Use a proper widget like a grid. This has all what you want and need and are trying to re-invent.

See the wxPython demo.

Dynamically filling sizers with data is OK for some use cases, but your use case is clearly crying for a proper data control like grid or list control.

Depending on your needs, hold out for virtual grid vs. ‘normal’ grid.
I think the difference is described in the documentation somewhere. Basically, the ‘normal’ grid is holding the data while the virtual grid will ask your data table for data whenever it’s required.

The problem with Grid is that inline editing is very clumsy, both for the developer and the user.

I don’t see anything wrong with the ulrich’s current approach though. Choice and TextCtrl are no less ‘proper’ than Grid. The only thing is that I would be slightly worried about using too many OS/windowing system ressources if simple controls are created and destroyed at a rapid pace, but that might just be superstition.

An alternative is to keep the Grid or ListCtrl read-only, and edit the currently selected record on a separate panel.

There are two approaches to tracking changes: On is to use the content-changed events on the controls: EVT_TEXT for TextCtrl, EVT_CHOICE for Choice etc. I think this is the most straight-forward way. There’s no substitute for reading the documentation for the controls you use, each new control will have a different event.

Alteratively, you can use EVT_KILL_FOCUS to detect when focus moves away from a record, and then read the control’s values. If you go down that path, be mindfuld that there can be situations where you intuitively expect focus to change but it doesn’t, such as when switching to a different app.

Well, out of the box, the grid behaves like if editing a spreadsheet. You can customize as much as you want, though.

It has row and column labels. The row labels are the natural place for the ids of the datasets.
The row and col labels can be made dragable via a mix-in if you require the user being able to re-order datasets or columns.

Things get a bit complicated if you want non-contiguous selections or live updates of other widgets. For the first use case you more or less need to track the selections yourself. For the second use case you need to use e.g. wx.CallAfter if a grid cell update should trigger an update of other widgets which may again trigger changes of the grid cell.
For most use cases, the grid is just fine and it scales up well.

The main limitations:

  • It’s not possible to resize the label column/row with the mouse. (My workaround is to handle Ctrl-Dragging of the first row/col to resize the labels.)
  • I think it’s not possible to use custom renderers for the label column/row.
  • It’s not possible to have multiple rows/columns for the labels.
  • It’s not well accessible for users with screen readers. That’s a major limitation for one of my projects and is my use case where it’s really much more complicated than using widgets (wxGlade).

I have dialogs which use sizers in the proposed way, but these will not add or remove datasets.

I have been using the grid control for 20 years now. I don’t remember whether I learnt more from the demo or from the chapter “Coordinating the grid control” of “wxPython in Action”.

If you are interested in creating user friendly programs, you need to get familiar with the grid sooner or later.
Try the demos to see what’s possible.

See the GridLabelRenderer sample in the demo and the wx.lib.mixins.gridlabelrenderer module.

Thanks very much for all the hints!

The grid looks quite perfect for my usecase. There is just one question, which I can’t answer yet.

Beside the table-like forms I have a lot of detail forms with quite a lot of wx.Control elements in it. Therefore I wrote validators to manage the transfer from and to the form elements. There seems to be a way to use these validators even in the grid by setting the CellEdotors to wx.Controls, for example wx.Choice:

grid.GetCellEditor(2,2).SetControl(wx.Choice(self, choices=['Morgen', 'Abend']))

But that isn’t the naturally way to handle the data in the grid, I think. When I use Grid.GetCellValue and Grid.SetCellValue I have to manage the transfer (formatting and so on) twice.

What would You prefer?

No, SetControl is not meant to be used that way. If you want to customize an editor then you should derive a new class from GridCellEditor and implement your custom functionality there. See the GridCustEditor sample in the demo for an example.

When using a Grid the data lives in the GridTable associated with the grid, and the cell editor handles moving the data to/from the control as needed. The default editor classes were not implemented with Validators in mind, but it should be doable in custom editors.