Results 1 to 10 of 10

Thread: Merging Rows of QStandardItemModels to Show in One QTreeView

  1. #1
    Join Date
    Sep 2016
    Posts
    5
    Thanks
    1
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Merging Rows of QStandardItemModels to Show in One QTreeView

    I have several QStandardItemModels (each wrapped in a QSortedFilterProxyModel) that are populated in a tree-like way.
    Conceptually what I'm modelling is variable values.
    These variables exist under namespaces, which are potentially nested.
    Each namespace item has 1 column (name), and is the parent item of all namespace and value items underneath it.
    Each value item has 3 columns (name, modifiable status, value), and are the leaf nodes.

    I have several of these models, one for each data type (int8_t, int16_t, float, ...), and currently I have a QTreeView and a QTab for each of them.
    The views exist in their own tabs.

    Now I want to be able to display a merged version of all the variables in a single window - assume I don't have access to the original data source, and that no fully qualified variable name conflicts.
    Since namespaces are shared between data types, ideally the children of that namespace should all belong to the same parent - rather than copies of the namespace.

    e.g. if I have foo.bar (float) and foo.baz (int), I would prefer the combined tree view to show

    foo
    ├── bar
    └── baz

    rather than

    foo
    └── bar
    foo
    └── baz

    I have already implemented this when serializing into YAML format by keeping track of the existence of namespace nodes; if they exist, attach to it; if not, create it, but I don't know if this would translate into the same solution when merging models.

    It's also important to maintain the itemChanged signals of the original models, since I have different slots depending on the type I'm changing.

    How do I achieve this sort of tree merge? I saw a post here about wrapping multiple QStandardItemModels, but no one replied to that post,
    and the result we're looking for is slightly different (they want parallel trees, I want merged trees).

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Merging Rows of QStandardItemModels to Show in One QTreeView

    My suggestion would be to model the data in a respective data structure and then use custom models on that.
    This allows you to handle the combined case just like any of the type specific cases.

    Alternatively one QStandardItemModel that contains everything and custom QSortFilterProxyModel that can additionally filter for a type.

    Cheers,
    _

  3. #3
    Join Date
    Sep 2016
    Posts
    5
    Thanks
    1
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Merging Rows of QStandardItemModels to Show in One QTreeView

    I see, thank you for the quick response!
    Option #1 isn't really doable due to the low level nature of the values (they have to stored in statically allocated arrays indexed by an enum),
    but option #2 looks workable.

    If I have a single underlying model, and a QSortFilterProxyModel for each type (which I already do, except the current mapping is 1 to 1),
    how would my connection of type-specific callbacks to QStandardItemModel::itemChanged be changed?
    Would I then have to store a type column into the leaf nodes (so the value items will be 4 columns long), then on any itemChanged
    for the whole model, I dispatch based on the type stored?

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Merging Rows of QStandardItemModels to Show in One QTreeView

    Quote Originally Posted by ThinkingShip View Post
    Option #1 isn't really doable due to the low level nature of the values (they have to stored in statically allocated arrays indexed by an enum),
    I am afraid I don't understand.

    Even if the source data structure is not capable of providing all information (which sounds doubtful), you could copy the data into a structure that does, just like you are doing now.

    Quote Originally Posted by ThinkingShip View Post
    If I have a single underlying model, and a QSortFilterProxyModel for each type (which I already do, except the current mapping is 1 to 1),
    how would my connection of type-specific callbacks to QStandardItemModel::itemChanged be changed?
    Not sure what you mean, each type's filter model will be a separate signal source.

    Quote Originally Posted by ThinkingShip View Post
    Would I then have to store a type column into the leaf nodes (so the value items will be 4 columns long), then on any itemChanged
    for the whole model, I dispatch based on the type stored?
    Don't you already know the type of an entry? How would you otherwise even filter for a type?

    Cheers,
    _

  5. #5
    Join Date
    Sep 2016
    Posts
    5
    Thanks
    1
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Merging Rows of QStandardItemModels to Show in One QTreeView

    Currently the data representation looks like this (conceptual)
    Flat array ⇋ Tree QStandardItemModel
    Where the actual data structures are these (for each parameter type)
    2 maps for QStandardItem* ⇋ parameter index
    2 maps for const char* parameter name ⇋ parameter index
    array of ParameterInfo<type> holding boundary value and write status, indexed by parameter index
    array of type holding actual value, indexed by parameter index

    If I could redesign it I'd have a single parameter data structure for all types, holding its own type information, variant value, variant value boundaries, write status, name, and parameter index, held in an array indexed by the parameter index. I still wouldn't make the base data structure a tree because there's the requirement of no dynamic memory allocation for embedded usage (data structure is unfortunately shared between PC and onboard...).

    Your suggestion of having an intermediate tree data structure would make it (conceptually)
    Flat array ⇋ Tree of a new struct holding a variant for a value ⇋ Tree QStandardItemModel
    Except here it's a many-to-1 mapping from flat array to the tree holding the data structure I mentioned above (with the addition of a list of children).
    It seems like a lot of work for not so much gain.
    If this is the minimum amount of work to merge the trees, I would be fine with a solution that presented the trees in parallel like


    foo
    └── bar
    foo
    └── baz

    Not sure what you mean, each type's filter model will be a separate signal source.
    The QSortFilterProxyModel is a signal source, but does it forward its underlying model's signals?
    What I mean is that from the documentations, there is no QSortFilterProxyModel::itemChanged signal.
    To establish context (and to set your expectation of my knowledge level so you know the intent behind my questions), I'm relatively inexperienced with Qt programming and aren't sure if the following is allowed:
    Qt Code:
    1. connect(myProxyModel[type], &QStandardItemModel::itemChanged, this, myCallback[type]);
    To copy to clipboard, switch view to plain text mode 

    Don't you already know the type of an entry? How would you otherwise even filter for a type?
    I do know the type of each entry; it's just that it's implicitly rather than explicitly encoded right now.
    Currently we know the type of an entry based on which model it's in, since each model holds only parameters of that type.
    My question relates to my previous premise that I couldn't distinguish the type based on the signal source.
    However, if the previous line of code I posted is valid (i.e. the type can still be implicitly encoded in which proxy model is the signal source),
    then this is not needed.

    I'm assuming that if the previous line is valid, the proxy model doesn't fire on items that were filtered out.

  6. #6
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Merging Rows of QStandardItemModels to Show in One QTreeView

    Quote Originally Posted by ThinkingShip View Post
    array of ParameterInfo<type> holding boundary value and write status, indexed by parameter index
    array of type holding actual value, indexed by parameter index
    So the "foo" part in your graphic comes from the ParameterInfo?

    Quote Originally Posted by ThinkingShip View Post
    I still wouldn't make the base data structure a tree because there's the requirement of no dynamic memory allocation for embedded usage (data structure is unfortunately shared between PC and onboard...).
    Shared as in same code or shared as in shared-memory?

    In any case, I am right when I assume that you treat the array of ParameterInfo for a type and the data array for a type like an array that contains a "pair" with those two entries?
    I.e. they have the same length and the same index refers to the same "pair" of info+data?

    Quote Originally Posted by ThinkingShip View Post
    Your suggestion of having an intermediate tree data structure would make it (conceptually)
    Flat array ⇋ Tree of a new struct holding a variant for a value ⇋ Tree QStandardItemModel
    No, in this scenario there is no QStandardItemModel, duplicating the data third time would be really wasteful.

    And you don't necessarily need the tree structure if you can do the mapping for a "tree node" into the flat structure on-the-fly.
    It is often just easier to have an actual tree when implementing a tree model.

    Quote Originally Posted by ThinkingShip View Post
    The QSortFilterProxyModel is a signal source, but does it forward its underlying model's signals?
    It would be useless if it didn't do that, a view working on the proxy model will need to get update notifications just like if it would work with the source model.
    A filtering proxy will obviously apply its filtering before emitting its respective signals.

    Quote Originally Posted by ThinkingShip View Post
    What I mean is that from the documentations, there is no QSortFilterProxyModel::itemChanged signal.
    Yes, that convenience signal is specific to QStandardItemModel, all models emit dataChanged(), which is what the view classes are using.

    Quote Originally Posted by ThinkingShip View Post
    To establish context (and to set your expectation of my knowledge level so you know the intent behind my questions), I'm relatively inexperienced with Qt programming and aren't sure if the following is allowed:
    Qt Code:
    1. connect(myProxyModel[type], &QStandardItemModel::itemChanged, this, myCallback[type]);
    To copy to clipboard, switch view to plain text mode 
    That would fail as the class of myProxyModel[type] is not likely a QStandardItemModel or derived from it, see above.

    Quote Originally Posted by ThinkingShip View Post
    I do know the type of each entry; it's just that it's implicitly rather than explicitly encoded right now.
    Currently we know the type of an entry based on which model it's in, since each model holds only parameters of that type.
    The combined model would need a type info per node for filtering to work.
    But of course you could alternatively use separate models for the single type views.

    Quote Originally Posted by ThinkingShip View Post
    I'm assuming that if the previous line is valid, the proxy model doesn't fire on items that were filtered out.
    Yes, see above.

    Cheers,
    _

  7. #7
    Join Date
    Sep 2016
    Posts
    5
    Thanks
    1
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Merging Rows of QStandardItemModels to Show in One QTreeView

    Quote Originally Posted by anda_skoa View Post
    So the "foo" part in your graphic comes from the ParameterInfo?
    The "foo" in the graphic is the namespace the variables bar and baz reside in.
    This information is encoded in the variable name (a separate array using same index), so that their names would be "foo.baz", "foo.bar".

    Quote Originally Posted by anda_skoa View Post
    Shared as in same code or shared as in shared-memory?
    Shared code (definitely not shared memory xD).

    Quote Originally Posted by anda_skoa View Post
    In any case, I am right when I assume that you treat the array of ParameterInfo for a type and the data array for a type like an array that contains a "pair" with those two entries?
    I.e. they have the same length and the same index refers to the same "pair" of info+data?
    Yes, it is informationally equivalent to a single array of structs with each struct holding info+data+name (info excludes name; it has just boundary information for some reason).


    So let me summarize your proposals to verify that I'm interpreting them correctly:
    1. The intermediate data structure is not necessary since we have the information to map flat array -> tree using the variable name, which is fully qualified and includes all namespaces the variable is under ("topnamespace.foo.bar"). The inverse mapping from Qt Item -> flat array could be created and cached during the creation of the items.

    2. (this point I'm not clear about) I would have 1 combined model, with an additional 4th column encoding the type information.
    You mentioned that "No, in this scenario there is no QStandardItemModel"; what would this combined model be?

    The problem comes in here, as I would like for there to be an option to have just 1 merged view, and sometimes with type specific view.
    For the 1 merged view, I can't just show the combined model in a TreeView because some variables are supposed to be hidden from view.

    Actually, I think the better option would be to just not create items for those hidden variables - if they won't be shown, then they won't be modified.
    However, I think I would still need an additional QSortProxyModel since it holds additional boundaries on the visible variables (2nd set of limits).

    So in the end, what it might look like:
    Qt Code:
    1. uint8_t ───┐
    2. uint32_t ──┤
    3. float ─────┴ Combined model (QStandardItemModel?)
    4. │(single view mode)
    5. ├ single QSortProxyModel just to apply additional bounds checking ── QTreeView
    6. │
    7. │(or type-specific view mode)
    8. ├ uint8_t QSortProxyModel filter both type and bounds ── QTreeView
    9. ├ uint32_t QSortProxyModel filter both type and bounds ── QTreeView
    10. └ float QSortProxyModel filter both type and bounds ── QTreeView
    To copy to clipboard, switch view to plain text mode 

    The main problem for me is structuring the infrastructure in such a way that there is little code replication between the two modes.

    Thanks for looking into this.

  8. #8
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Merging Rows of QStandardItemModels to Show in One QTreeView

    I would go for two type of models:
    * a model that handles a single type
    * a model that handles all types

    If I were coding this I would create two custom model classes, potentially templating the single type model for the respective type.
    But you can use QStandardItemModel if you feel that would be easier, writing a tree model is not that easy at the first time.

    Cheers,
    _

  9. The following user says thank you to anda_skoa for this useful post:

    ThinkingShip (8th September 2016)

  10. #9
    Join Date
    Sep 2016
    Posts
    5
    Thanks
    1
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Merging Rows of QStandardItemModels to Show in One QTreeView

    Quote Originally Posted by anda_skoa View Post
    I would go for two type of models:
    * a model that handles a single type
    * a model that handles all types

    If I were coding this I would create two custom model classes, potentially templating the single type model for the respective type.
    But you can use QStandardItemModel if you feel that would be easier, writing a tree model is not that easy at the first time.

    Cheers,
    _
    Ah I see, so similar to my last diagram except without the QStandardItemModel?
    What would you subclass those models off of? QAbstractItemModel?

    I think I have enough of an idea to start working on it; thanks for the help!
    Last edited by ThinkingShip; 8th September 2016 at 18:04.

  11. #10
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Merging Rows of QStandardItemModels to Show in One QTreeView

    Yes, you need to derive from QAbstractItemModel since you need a tree model.

    The model for one data type needs a reference or pointer to the respective arrays, them build some lookup.
    E.g. a map with the keys being the "namespace" parts and the value being the index into the array.

    QModelIndex for a top level item is the the "n-th" key, etc.

    Cheers,
    _

Similar Threads

  1. Replies: 4
    Last Post: 26th September 2011, 13:02
  2. Replies: 2
    Last Post: 15th August 2011, 01:26
  3. Show selected rows
    By mpi in forum Qt Programming
    Replies: 3
    Last Post: 23rd June 2010, 02:59
  4. Replies: 0
    Last Post: 20th October 2009, 01:57
  5. [Qt 4.x] Highliting rows in a QTreeView
    By Moppel in forum Qt Programming
    Replies: 3
    Last Post: 10th March 2006, 11:00

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.