[Development] thoughts about a shared models module, and models in general (was Re: Would a (QML) model aggregator class a welcome addition?)

Alan Alpert 416365416c at gmail.com
Wed Jan 15 21:48:54 CET 2014


On Wed, Jan 15, 2014 at 12:43 PM, Alan Alpert <416365416c at gmail.com> wrote:
> On Wed, Jan 15, 2014 at 12:48 AM, Rutledge Shawn
> <Shawn.Rutledge at digia.com> wrote:
>>
>> On 10 Dec 2013, at 11:43 PM, Alan Alpert wrote:
>>
>>> On Tue, Dec 10, 2013 at 12:32 PM, Alberto Mardegan
>>> <mardy at users.sourceforge.net> wrote:
>>>> Hi all!
>>>>  For one of my projects, I found the need to merge several models into
>>>> a single model. I wrote a class for it, and I think it's generic enough
>>>> to be useful for other people, and I wonder if it could be put into Qt
>>>> itself:
>>>>
>>>> https://gitlab.com/mardy/mappero/blob/directions/lib/Mappero/model-aggregator.h
>>>>
>>>> Note that the thing is not complete (especially the implementation),
>>>> it's just in a state where I can use it in my application; it needs a
>>>> bit of more work before being ready for the generic use.
>>>> I'm wondering if I should make this extra effort or not. :-)
>>>>
>>>> If the answer is yes, should it be added to QtCore or QtDeclarative?
>>>> Right now it's using QQmlListProperty so that the source models can be
>>>> declared inline,
>>>>
>>>> ModelAggregator {
>>>>    Model1 { ... }
>>>>    Model2 { ... }
>>>>    ...
>>>> }
>>>>
>>>> but that could be removed and substituted by a simple QList of models if
>>>> desired.
>>>>
>>>
>>> This doesn't sound core enough to really benefit from going into
>>> QtDeclarative (please stop developing for QtQuick 1 :P ; but I know
>>> you meant qtdeclarative the repo) or QtCore (no QML types can go into
>>> QtCore, because QtCore cannot depend on QtQml).
>>>
>>> Maybe we should have an add-on repository like qt-qml-extras which
>>> contains small but useful QML modules. Candidate content being this
>>> model aggregator, Qt.labs.folderlistmodel, (and I have a couple
>>> imports at home that I could clean up and contribute too) maybe even
>>> QtQuick.XmlListModel or Qt.GraphicalEffects?  Some of the add-on level
>>> imports, like Qt.GraphicalEffects, are large enough for their own
>>> repo. And some, like QtQuick.XmlListModel, feel "core" enough to go in
>>> qtdeclarative as an essential. But there's scope for a lot of small
>>> but useful QML modules, like ModelAggregator and
>>> Qt.labs.folderlistmodel, where a shared and non-essential repository
>>> would make more sense. The alternative is telling each of these little
>>> modules to get their own github/gitorious/playground repo and then
>>> we'd have to organize them with something like inqlude...
>>>
>>> Anyone else want a qml-extras repository for collecting small QML imports?
>>>
>>> And should it be qt-labs, playground, or qt (add on, not essential!)?
>>
>> I like the idea, but the trouble with models is you typically want to create and populate them from C++, which means you need the collection to be in a library which you can link with.  The QML modules that get installed e.g. under qml/QtQml/Models aren't used that way because everything under qml is meant to be instantiated only from QML.  But it would be nice if these models could be instantiated both ways.  The C++ interface is more urgent IMO, because so far the choices seem to be
>>
>> http://qt-project.org/doc/qt-5.1/qtquick/qtquick-modelviewsdata-cppmodels.html
>>
>> subclass QAbstractItemModel yourself (that requires some overhead, mostly boring code being rewritten each time); use a QStringList (but only for one column and only strings); use a QList of QObjects, which at least is quite flexible (but then each row has all the QObject overhead); or use one of the models that you can only instantiate from QML, and then you will have to write more Javascript to populate it.  I started writing https://codereview.qt-project.org/#change,74986 and then realized I can't link with it.
>
> Like several QtQml and QtQuick classes, you can have types in a module
> as well as available in C++. Ideally any C++ public classes relating
> to the QtQuick* imports will be in libQt5Quick.so, and ditto for
> QtQml.* and libQt5Qml.so. We have these two C++ libraries relating to
> QML already.
>
>
>> So this module would have to be installed under lib I suppose?  and then just because it's a separate, optional module, there would be an objection to depending on that module in any Qt essential module, such as Qt Quick Controls.  I want to use such a model for the shortcuts in the next-generation FileDialog (which will be using QtQuick.Controls, and therefore lives in the same git repository now), and we have another potential use case for font families for the font dialog, so it has to be OK for Controls to depend on it, wherever it goes.  The easy solution (and also the one which doesn't encourage reuse) is to put this model in QtQuick.Dialogs.Private because that's where I need it.  If someone else wants it, they can always copy the code.  It's not much code anyway, hardly worth worrying about.  But altogether we could end up with a worthwhile collection of models if various people have various ideas.
>>
>> Maybe it should rather be a static library to have one less runtime dependency?  Another advantage would be that a typical application might only need one of the models from the lib, so the memory cost would be lower if the linker was free to leave out the ones that you didn't use.  But then if QtQuick.Dialogs is already going to use it once, that means if your app uses it too, you've already got 2 copies.  Then if you run several Qt apps at the same time, the copied model code is taking up more memory than if you had linked with a modestly-sized dynamic library, even if each app only uses 10% of it.  I think that's the same reason all of Qt is usually in the form of dynamic libraries: yes you have to pay the cost of loading them, and the first application didn't really need all that code, but the OS only ends up loading the libs from disk to memory the first time they are used, and we assume there will be more than one application using them; so the amortized cost on a multi-tasking system is low.  It's only the code which nobody ever uses that is a waste of memory, and we try to minimize that by having most of Qt consist of extremely-reusable code with well-designed APIs so that most parts of it are needed by most applications.
>>
>> So I think the best choices are to keep putting such things in a library that everybody already links with (making that library larger), or create a new "blessed" essential library so that everyone can link with it.  We could say that since models are used mostly with Controls, the models library could belong to Controls.  Maybe you could also import e.g. QtQuick.Controls.Models if you choose to instantiate the model from QML instead, but then qml/QtQuick/Controls/Models/libmodelsplugin.so would be really small, just linking against lib/libQtQuickControlsModels.so.5 or whatever it's called, and exposing the same types to QML (at least the concrete classes, but so far I don't think we need more abstract classes in that module).
>>
>> Or we could refactor QtQml.Models to work this way.  That would probably be better to maximize code reuse.  The existing QML API would stay the same, and we would also add C++ API where applicable, and add new model types which have both C++ and QML APIs.  The QML module would need to link against a new library lib/libQtQmlModels.so.5 which contains the implementation.  If an application also links the same library, then it could instantiate the model from C++, and the QML will not care who instantiated it because the interface for accessing it is the same.
>>
>> Or is it better for the next-generation views and models to start over completely and leave QAIM behind?  Even in that case, we need to plan on having both a good C++ API and a good QML API for the models, both for populating them and for accessing the data.
>
> The next-generation views and models are expected to start fresh, but
> they're still in a research stage. I expect it would either be an
> additional library or go into QtCore like the current models.
> Co-existing C++ and QML types is a solved problem, there's a library
> which has the types and a plugin which exposes them to QML and links
> to the QtQml module (since the majority of the exposure is through the
> meta-object system).
>
>>
>> One of the better reasons to redesign the models would be to eliminate the kludge that you first have to look up the name of the field that you want, to convert its string name to a role; then you use the role to get the data.  You need both the role and a QModelIndex object.  Widgets didn't use views that way; the role was meant as a way of providing different aspects on data at the same column,row coordinates, but models were usually organized into columns and rows first, and then roles were added only if needed.  Whereas QML views ask only for a role at a specific row, so the column index is not used.  (Neither way is suitable for direct access to multi-dimensional data sources either, FWIW.)  It's really an oversimplification to say that a view consists only of repeated rows, and because you probably aren't building a spreadsheet (even though somebody would probably like to), you don't need columns at all.  Maybe the QModelIndex object is a good idea, but then it should be the only thing that you use to find the data, so that it's possible to generate a hash code for that object, if your model is architected so that hashing helps.  If you want to have named fields and rows, then that type of model index should consist of a name and a row.  If your data is organized in some other way, then maybe you could use a different type of index object.
>>
>> But even for the most common type of model, which has numbered rows and named fields on each row, it would be nice to get rid of the need to go through a hash table each time you want to convert the field name to what is effectively a column ID.  That result never changes.  But the "delegates" (which aren't actually delegates in the old sense) are all individual objects, so therefore each one of them must convert the name of each field that it wants to access into an int; then it needs to construct a QModelIndex, which exists only to carry another int, the row number; then it can finally ask the model for the data.  It should instead be possible for the field name, where used, to be some type of symbol: a special string which already has the int which the model will need, if the model needs an int, like the role was.  That might be the same thing as having a syntactic enum in QML.  The enum type could be declared in either C++ or in QML.  An instance of that enum could be assigned symbolically but the conversion to int would happen right away, so that when you use it in a model index it doesn't need to be done again.  For output purposes (when persisting QML to a file, or in console.log), it's a symbol, no need to see the number and convert it in your head.
>>
>> Today, if a model was designed for a widget-based view, it will have needed some rework to use it in Qt Quick.  We managed to reuse QAIM without making it easy for applications to reuse their instances of QAIM; so IMO it was a mistake to reuse QAIM then, if we could not have QML views accessing them in the same way that QWidget view did.  It would be possible to plan on redesigning the widget views too, for Qt 6, and therefore design the next-gen models with that in mind; but nobody wants to spend time on the widget view code anyway.  But QAIM isn't really the best possible model for either type of view, as it stands today, so why not start over from scratch for Qt 6?  I doubt there's any point in designing next-generation views without redesigning the models too.
>>
>> If we are going to have a whole new type of model as a basis for the next-generation QtQuick model/view framework, then making new subclasses of QAIM is at best a stopgap solution.  But the API of whatever we put into that new essential module should hopefully be designed for long-term support.  It could be created now, and then completely upended again in Qt 6 if we feel the need, but that would be inconvenient for anyone who starts using it now.  That argues against getting started now.
>>
>> But I need something now.  What should I do?  Keep it private in Dialogs.Private?  That's the easiest.  That's what this does
>>
>> https://codereview.qt-project.org/#change,75025
>
> My suggestion is to add it to QtQml for now (QtQml for C++ classes,
> QtQml.Models for QML types). That would be a convenient and expedient
> "stopgap solution" until we have time to redesign the model/views
> entirely.

 This approach could also apply to the original suggestion by Alberto,
in the absence of a separate add on module (which Sean couldn't use
because of the QtQuick.Controls dependency). Just requires a higher
bar for code review/quality, but I'm currently leaning in favor of
extra models convenience classes. It's a decent hold over measure
since the new model/views have been taking so long.

--
Alan Alpert



More information about the Development mailing list