Idiomatic model/view binding for non-tabular data
-
Qt's Model/View framework is a comprehensive, straightforward solution for getting, say, SQL query results displaying in a table. All well and good.
I'm curious, though, about what the intended scope of the model/view framework is, and how to leverage it idiomatically when NOT representing tabular/hierarchical data. I don't want to reinvent the wheel -- since a lot of work obviously went into the framework --but neither do I want to force a square peg into a round hole by using Qt's M/V for something it wasn't intended to work well for.
Incidentally, I'm not using QML at the moment. I'll be revisiting that option later, but for now assume it's not available.
Anyhoo, my questions. Feel free to riff on them and go all free-form if I'm making fundamentally incorrect assumptions. :-)
Suppose I want to represent object-structured data, say in a popup window or something, with various (Q_)properties of a (Q_)object being mapped to values displayed in different widgets. The source of the data might be an array of the structs, but is probably a single pointer. The solution that springs to mind would be to write a QAbstractItemModel for the class, which exposed the relevant properties as cells in a one-row grid, and then a QDataWidgetMapper to associate the columns with the widgets.... but that's potentially a lot of boilerplate code, and it throws away the property names in the model/view interface in favor of opaque integers.
How should I model arrays of heterogeneous objects? Say, a QList<IFruit *>, where some of the values are AppleFruit, some are CitrusFruit (or its subclasses LemonFruit, OrangeFruit), etc., such that some of the properties are static type-wide, while others are per-subclass? Obviously much of this will depend on how the view works, but what's the best practice?
Is the "hierarchy of tables" data shape intended solely for situations where the tables are self-similar (e.g. the file system model), or is it also appropriate for situations where different hierarchy levels, or even child tables of different items in a single row, have different column sets?
Thanks,
Ben
-
Hi,
Thanks for raising an interesting topic, I hope it will end up in a good discussion. Let me note that my response represents my view of things; no more, no less.
[quote author="sneftel" date="1331900742"]Qt's Model/View framework is a comprehensive, straightforward solution for getting, say, SQL query results displaying in a table. All well and good.
I'm curious, though, about what the intended scope of the model/view framework is, and how to leverage it idiomatically when NOT representing tabular/hierarchical data. I don't want to reinvent the wheel -- since a lot of work obviously went into the framework --but neither do I want to force a square peg into a round hole by using Qt's M/V for something it wasn't intended to work well for.
Incidentally, I'm not using QML at the moment. I'll be revisiting that option later, but for now assume it's not available.
Anyhoo, my questions. Feel free to riff on them and go all free-form if I'm making fundamentally incorrect assumptions. :-)
Suppose I want to represent object-structured data, say in a popup window or something, with various (Q_)properties of a (Q_)object being mapped to values displayed in different widgets. The source of the data might be an array of the structs, but is probably a single pointer. The solution that springs to mind would be to write a QAbstractItemModel for the class, which exposed the relevant properties as cells in a one-row grid, and then a QDataWidgetMapper to associate the columns with the widgets.... but that's potentially a lot of boilerplate code, and it throws away the property names in the model/view interface in favor of opaque integers.
How should I model arrays of heterogeneous objects? Say, a QList<IFruit*>, where some of the values are AppleFruit, some are CitrusFruit (or its subclasses LemonFruit, OrangeFruit), etc., such that some of the properties are static type-wide, while others are per-subclass? Obviously much of this will depend on how the view works, but what's the best practice?
[/quote]
In my view, Qt's model/view system is not designed and not fit for this this kind of use. Sure, you can create something like a property editor just fine with it (there are several good examples to be found that do just that), but I think it is does not scale much beyond that. I would not take it beyond what it is: an abstract interface to represent list, grid and tree type data so it can be represented in standard widgets. I think even QWidgetDataMapper is already pushing it a bit, though I can see its use for some scenarios.I see QAIM as a base for adaptor classes that can be used to create an interface for your own, domain specific data stores, so the data from your store can easily be represented in a list, a table, a tree, a column view or even in a QML context. I use it for that, and for that alone.
[quote]
Is the "hierarchy of tables" data shape intended solely for situations where the tables are self-similar (e.g. the file system model), or is it also appropriate for situations where different hierarchy levels, or even child tables of different items in a single row, have different column sets?
[/quote]
In principle, every cell can have its own number of child columns and rows. However, there is no Qt view that actually supports this, and it is a rediculously complex structure to present to your users anyway. Trees are already a very complex UI element, but multi-level grids are much, much worse. So, while the model in theory supports it, it is actually never used. In practice, only the cells in the 0-th column can have children, and these are expected to have the same columns as the parent items. -
[quote author="Andre" date="1331902049"]
I would not take it beyond what it is: an abstract interface to represent list, grid and tree type data so it can be represented in standard widgets.
[/quote]Alright, that's pretty much what I was guessing.[quote]I see QAIM as a base for adaptor classes that can be used to create an interface for your own, domain specific data stores, so the data from your store can easily be represented in a list, a table, a tree, a column view or even in a QML context.[/quote]Ahh... so concrete models should be per-source, not per-schema. Thanks for that insight.
[quote]In practice, only the cells in the 0-th column can have children, and these are expected to have the same columns as the parent items. [/quote]Got it.
So if Qt's model/view framework is not the peg for this hole, is there a different peg I'm not aware of? Some simple way to attach a QObject* to a grouping QWidget, and property names to the QWidget's descendant widgets, and get bidirectional data synchronization?
-
[quote author="sneftel" date="1331912182"]
[quote]I see QAIM as a base for adaptor classes that can be used to create an interface for your own, domain specific data stores, so the data from your store can easily be represented in a list, a table, a tree, a column view or even in a QML context.[/quote]Ahh... so concrete models should be per-source, not per-schema. Thanks for that insight.[/quote]
Just to be sure: when I use the term 'data strore', I don't nessecairily mean an (SQL) data base. I mean some piece of application internal API that manages the applications data. Views on (parts of) that data may be provided by QAIM's, but there may be many such views, perhaps provided by other QAIM's, or by other interfaces entirely. I made a store in the past that provided a view through a QAIM, but at the same time also through a graphics view.So, if you talk about providing a model per source instead of per scheme, I hope you don't think about building one huge model to visualize a single data source with many different schemes, each having its own tables & views, each having their own content? I mean: creating a QAIM to represent the structure of an SQL data source so you can make it navigable in a tree view is one thing (I did that), but trying to use a single model for your complete data access is not a good idea.
[quote]
[quote]In practice, only the cells in the 0-th column can have children, and these are expected to have the same columns as the parent items. [/quote]Got it.So if Qt's model/view framework is not the peg for this hole, is there a different peg I'm not aware of? Some simple way to attach a QObject* to a grouping QWidget, and property names to the QWidget's descendant widgets, and get bidirectional data synchronization?[/quote]
Of what I understand of your requirements, I don't see why you think you need such a generic solution at all. I think it will only complicate your design. It sounds like you want to have a very generic mechanism to get & set values to forms, but I think you are overestimating how common the problem you're solving with that actually is. If you add in the business-logic topics of data validation, constraints, etc., then you will very soon come to the conclusion that you will need domain-specific code anyway, taking away any gain you could have had from the generic solution you devised with so much effort.
Of course, for part of the issues you can create your own generic building blocks. Creating an object that provides bi-directional binding of widgets to an objects property value is certainly doable, for instance.