Add Column objects to Tree and Table public API#4199
Add Column objects to Tree and Table public API#4199freakboy3742 merged 30 commits intobeeware:mainfrom
Column objects to Tree and Table public API#4199Conversation
|
I think tests should pass now, so this should be ready for review. |
freakboy3742
left a comment
There was a problem hiding this comment.
A partial review to get you unstuck.
I've picked up a couple of documentation issues, and I've pushed some minor updates to the table_columns example app.
The two big questions for me at this point are about compatibility with the older API.
First, should we retain the accessors argument? It needs to continue to exist for the short term for backwards compatibility purposes, but given that it is a very narrow interpretation of what can be done with a column, and there's a better way to express the idea using AccessorColumn... is there any reason to continue to allow accessors as an long term solution?
Second - I'm also wondering if missing_value should be moved into Column responsibility. It doesn't seem out of the question that a "missing" value might be column-dependent (e.g, "missing" might be best represented by a different icon, or "Unknown", or a sentinel date, depending on the data type and context - and all in the same table).
| "babel", | ||
| ] | ||
|
|
||
| [tool.briefcase.app.table.macOS] |
There was a problem hiding this comment.
| [tool.briefcase.app.table.macOS] | |
| [tool.briefcase.app.table_columns.macOS] |
Rinse and repeat for other platform tables below.
|
|
||
| -8<- "snippets/accessor-values.md" | ||
|
|
||
| You can define your own custom columns which can do things like formatting text in a particular way, combining multiple attributes to produce the value to display, or even accessing data via indexes rather than attribute lookup. |
There was a problem hiding this comment.
There's an implied "how" follow up question here. Longer term, a topic guide or howto might be useful. In the short term, pointing out that a custom column can be any ColumnT-satisfying class is probably sufficient.
Co-authored-by: Russell Keith-Magee <russell@keith-magee.com>
Thanks.
It remains needed for the case where it gets passed through to the I'd be happy to get rid of it in the long-term, but we'd need some alternative to cover this case (like a "data source factory" argument).
It would make some sense, but a custom column has complete control over what to do if it can't determine a valid value - it's got the suggestion of a default passed in from the widget, but a custom column doesn't have to follow that suggestion if it's got a better idea. We could add various default values as arguments to |
🤦 Despite this being a subject of recent conversation, I completely missed this as the retained use case for On that basis, I agree we can't remove it - at least, not without some other changes. The thing that is jarring for me is that there are essentially two use cases for accessors - an initial mechanism for describing which data fills which column; and the ordering that is used to resolve list-based data into Rows in a ListSource. The column mapping can be (and in the simple case, will be) described by the column headings; and there's a clear path to "deprecate But the ordering that is used to resolve list-based data into Rows is:
Those two properties strike me as odd - there's something you can define, but can't change, even though there's an argument with the same name elsewhere in the API; but the property won't even be used in many cases.
I can see what you're describing here - but I suspect it's going to end up being most complex for the very simple case that needs it - "A table display a list of tuples" is the simple case here; defining a "factory" for the simple case seems like overkill. I wonder if maybe it is as simple as:
I think that retains all the existing functionality; but it retains separation between the two use cases for
That's fair. Having a widget-level default that is overridden by a Column-level default that is overridden by an actual value makes sense; and it also strikes me as something that can be added in the future if it turns out it might be useful. |
I think that, or some variation on it, may work. But stepping back a bit, I think the general guide is that after all of this, we still want users to be able to do something like table = toga.Table(
columns=["Name", "Age"],
data=[
("Arthur Dent", 42),
("Ford Prefect", 37),
("Tricia McMillan", 38),
]
)and it Just Works. But when things get a bit more complex there's a clear path to resolve the complexity. The complexity we're running into here is essentially what happens when we have: data=[
("Arthur Dent", "Earth", 42),
("Ford Prefect", "Betelgeuse Five", 37),
("Tricia McMillan", "Earth", 38),
]and the user still wants name and age columns. The current solution is: table = toga.Table(
columns=["Name", "Age"],
accessors=["name", "planet", "age"],
data=[
("Arthur Dent", "Earth", 42),
("Ford Prefect", "Betelgeuse Five", 37),
("Tricia McMillan", "Earth", 38),
]
)(Edit: actually we also need table = toga.Table(
columns=["Name", "Age"],
data=ListSource(
accessors=["name", "planet", "age"],
data=[
("Arthur Dent", "Earth", 42),
("Ford Prefect", "Betelgeuse Five", 37),
("Tricia McMillan", "Earth", 38),
],
),
)ie. "the data became more complex, so the data source definition has be more complex" is better than "the data became more complex, so the table widget has be more complex" This approach would mean:
In other words, we rely on the data sources to track the order of the accessors, rather than trying to do it in the widget. And if that doesn't do what the user wants, the fix is for the user to explicitly use a This is sort of an alternative fix for #4071 and is a slight behaviour change from the current state of the main branch (behaviour should be the same for the case where the user never sets data explicitly to a |
|
I think this is ready for a re-review: I've addressed all the comments, but there is one issue remaining which may not need to be addressed in this PR, but should be thought about: currently In any case, what this means is that if a Table is created without any columns or data initially, this will cause an exception. It may also occur if all the columns are custom columns without an accessor attribute. There are work-arounds involving creating an empty data source manually with some dummy accessors, but it seems to me that we might be able to drop the requirement and only error if the user doesn't supply accessors and then tries to use them. |
+1 to this. It gives good conceptual separation of data concerns and display concerns; and we don't need to worry about backwards compatibility with code we haven't formally released.
Yeah - I think we can handle that separately. Allowing a ListSource/TreeSource to be created with no accessors, and then raising an exception if you try to use data that requires accessors would make sense to me. |
freakboy3742
left a comment
There was a problem hiding this comment.
I've pushed some minor updates - mostly stylistic things in docs. The only thing of any substance is catching the case of using accessors in insert_column() and append_column() calls and raising a DeprecationWarning; but otherwise, this looks like a really solid improvement to the Table/Tree API. Thanks for all your work on this!
This is an initial cut at making
Columnobjects part of the public API forTreeandTableobjects.This PR:
TreeandTableAPIs to useColumnTprotocol objects in the place of column headings and accessors.columnsproperty with the current columnsshow_headingsargument gives more direct control over whether table headers are shownTreeandTableimplementation API, passing a column object rather than heading and accessor (and keeps backwards compatibility with any 3rd-party implementations)ColumnABC which is more appropriate for creating simple column subclasses thanAccessorColumnAccessorColumn.Fixes #4091.
PR Checklist: