QListWidget::addItems() Data type question
-
@Chris-Kawa Thank you really much for this detailed support. Now I think it is clear for me.
What do you think is the best way to manage multidimensional string-lists that have to be viewed in 3 seperate QListWidgets that depend on each other?
Example:
Main QListWidget (#1) has 3 items. When the user clicks on one of those items, the second QListWidget displays the associated items, let's say again 3 items. When the user clicks on one of those 3 "sub-items", again 3 more items should be displayed in the third QListWidget.Till now, I'm using complicated, multi-dimensional QLists like this:
A simple QStringList for level #1, a QList<QStringList> for level #2 and a QList<QList<QStringList>> for level #3.
I catch user selection using "currentRowChanged", start a complicated for-loop to walk through the levels and choose the associated QStringList, clear the QListWidget and add the new List.Is there an easier and more performant way to manage this? Would QVector be a better solution for that? Or is a completely different approach better?
@Binary91-0 To avoid linear search you can use a QMap<QString, QstringList> for second and third list widgets. Key is the string selected in the previous list widget, value is the list of strings asotiated with that key.
-
@jsulm is probably the quickest/simplest suggestion to suffice you. However, being purist, be aware that as it stands it "cuts a corner": if you can have the same string text at different levels of the tree with different children (i.e. the fact that they are the same string is a "coincidence"), it will not work without some further massaging.
You could just invent a proper data structure to hold you data correctly. Create a
class
/struct
, namedNode
, which holds aQString text
for its string plus aQList<Node>
for its children. That is what your data really is. Finding a node by text at a given level is a linear search through its parent's child list (only).I would start by looking at whatever data structure you currently have for your items and their sub-items and take it from there.
-
@jsulm This sounds great! I did not test it so far, but after reading the documentation part, it seems to exactly fit my intentions.
@JonB Good aspect. Fortunatelly, no literally identical values will appear in the lists. The node system is very interessting. I think, for this purpose with only displaying the next level list-widget, maybe the node system is not needed, but it sounds like a good way to automatically walk through complex lists.
In addition, I will maybe use a combination of both. What do you think about inheriting QString in a new class and adding a specifier like a pointer to the next level string-list to it. Then, on the one hand, I get my fast interaction by QMap and as a backup (e. g. for storage and loading procedures of the whole data) I will always have unique connections.How would you guys manage the local storage of such three-dimensional data structures? I know database management from PHP server-based, but I have never done such stuff with Qt in a local directory. Is it a possible and "easy to learn" method for this purpose or do you suggest any other storage method?
-
@jsulm This sounds great! I did not test it so far, but after reading the documentation part, it seems to exactly fit my intentions.
@JonB Good aspect. Fortunatelly, no literally identical values will appear in the lists. The node system is very interessting. I think, for this purpose with only displaying the next level list-widget, maybe the node system is not needed, but it sounds like a good way to automatically walk through complex lists.
In addition, I will maybe use a combination of both. What do you think about inheriting QString in a new class and adding a specifier like a pointer to the next level string-list to it. Then, on the one hand, I get my fast interaction by QMap and as a backup (e. g. for storage and loading procedures of the whole data) I will always have unique connections.How would you guys manage the local storage of such three-dimensional data structures? I know database management from PHP server-based, but I have never done such stuff with Qt in a local directory. Is it a possible and "easy to learn" method for this purpose or do you suggest any other storage method?
@Binary91-0 said in QListWidget::addItems() Data type question:
What do you think about inheriting QString in a new class and adding a specifier like a pointer to the next level string-list to it.
No! Your structure, with a string text plus children, is not itself a
QString
, and does not behave anything like one. So inheritance here would be wrong, use encapsulation (memberQString
plus children, as I wrote). -
Ok, I understand.
But if I am right, there is still no direct way to get a connection from a selected list item to the node, right? For example, I create a complex node hirarchy and pass the string-values of those nodes to the QListWidget, including duplicates. How is it possible now to directly associate a selected list item with the corresponding node? Do I simultaniously have to mirror the list structure in my nodes and catch the "currentRow" signal to walk through my node structure till I found the corresponding item?
If this was the way I need to go, I'm asking myself how to easily react to changes in the list widget, e.g. changing the sort-mode from ascending alphabetically to descending alphabetically. Do I have to start a complex sort mechanism that walks through my nodes and sorts them by a given identifier or stuff like that?I mean, the perfect way would be to directly inherit from a list widget item or from QString to "infiltrate" the list so I can directly react to user selection with the corresponding node.
Or am I missing the point?
-
One possible answer is that a list widget is not the best tool for this task. QListWidget is sort of a entry level widget if you don't need anything fancy. Usually when whatever customization needs to be done going for a lower level solution turns out to be far more suitable. You could implement an item model with a tree structure of nodes and then have 3 QListViews that point to different levels of the tree. On selection change you would simply get the selected row index and switch the views to point to the right tree node. I discourage you from using strings as indices. It's slow and gets complicated with non-unique strings. It's better to just deal with QModelIndexes. Handling sorting and filtering in this case could be done via QSortFilterProxyModel, which would handle the translation of indices from what's in the view to what's in the model. See Model/View Programming for details and specifically Proxy Models section for handling filters and sorting.
-
Ok, sorry for the belated reply, but it took me some hours to carefully read the whole topic to Model/View Programming. Thank you for that hint, I think I know understood the basic principle of this concept!
I'm trying to solve my issue with a tree model now, what gives me most flexibility. For this, I'm coding an example as described in the link you posted (Simple Tree Model Example).
Here, I have a problem understanding what we already discussed eralier: The syntax of a function call with a const reference of X within this function call:int TreeItem::row() const { if (m_parentItem) return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this)); return 0; }
What is that "const_cast" good for? My google research told me that, in a const function like the above, the "this"-pointer is a const pointer to a CONST object. Instead, in a non-const function, the "this"-pointer was a const pointer to a NON-CONST object. Am I right?
Why do I have to use this const_cast now to call a const function? I mean, the "indexOf"-function is const, so why shouldn't it be possible to pass a const object?Sorry, but I really try to understand that stuff..
-
@Binary91-0 said in QListWidget::addItems() Data type question:
I mean, the "indexOf"-function is const, so why shouldn't it be possible to pass a const object?
Because m_childItems contains TreeItem pointers, not const TreeItem pointers.
-
@Binary91-0 said in QListWidget::addItems() Data type question:
I mean, the "indexOf"-function is const, so why shouldn't it be possible to pass a const object?
Because m_childItems contains TreeItem pointers, not const TreeItem pointers.
@Christian-Ehrlicher So, it is not possible to pass either TYPE &var or const TYPE &var to a function that declares its argument itsself as const? What is the sense of that?
-
@Binary91-0 said in QListWidget::addItems() Data type question:
that declares its argument itsself as const?
I don't understand - m_childItems is of type QList<TreeItem*> - so indexOf() expects a TreeItem pointer, not a const TreeItem pointer. Therefore you have to cast the const this pointer to a non-const pointer. An automatic conversion from a const to a non-const value is not allowed (otherwise const would be completely useless when you think about it)
-
@Binary91-0 said in QListWidget::addItems() Data type question:
that declares its argument itsself as const?
I don't understand - m_childItems is of type QList<TreeItem*> - so indexOf() expects a TreeItem pointer, not a const TreeItem pointer. Therefore you have to cast the const this pointer to a non-const pointer. An automatic conversion from a const to a non-const value is not allowed (otherwise const would be completely useless when you think about it)
@Christian-Ehrlicher Ok, I know that I am wrong, but I try to explain what I do not understand:
The syntax of indexOf is:indexOf(const T &value) const
In my case, "T" is a TreeItem*, so the argument would be a "const TreeItem* &" what means a reference to a pointer of a const TreeItem, or not?
EDIT:
Ah, no. "T" is a Pointer to a TreeItem, and the indexOf()-function requires a const T &, so that means it requires a reference to a const pointer to a TreeItem, BUT NOT a const pointer to a CONST TreeItem, right?
And what does the const ensure in this function? Does it ensure that the pointer will not point to another QTreeItem after the function call or does it ensure that the QTreeItem will not be modified in any way? Or both? -
@Binary91-0 said in QListWidget::addItems() Data type question:
Does it ensure that the pointer will not point to another QTreeItem
The const means, as already explained from others above, that the value is not modified inside the function.
-
@Binary91-0 said in QListWidget::addItems() Data type question:
Does it ensure that the pointer will not point to another QTreeItem
The const means, as already explained from others above, that the value is not modified inside the function.
@Christian-Ehrlicher And the "value" in this example is the const pointer "this", right? So the only thing, the function guarantees is, that this pointer can't be changed, right? Does that mean, that the object it points to can be changed?
-
@Binary91-0 said in QListWidget::addItems() Data type question:
that the object it points to can be changed?
No, not in this case since the pointer itself is not const.
-
@Binary91-0 said in QListWidget::addItems() Data type question:
that the object it points to can be changed?
No, not in this case since the pointer itself is not const.
@Christian-Ehrlicher Oh, I don't know what's wrong actually but I simply do not understand it.
Look at this example that I found on my google research. It exactly explains this situation.
There is written, that a "this"-pointer is a const pointer, hence T* const this.
Now, in a const function, the object, this const pointer points to will also get const, hence const T* const thisSo why do you say that in this case, the pointer itsself was not const?
-
Sorry for double posting, but this may exceed the threads topic now.
I created an example application with a test class to try all possible combinations of pointers and classic variables in const member functions and non-const member functions.
class myQTest { private: int iPriv = 1, *iPPriv = nullptr; void fPriv(); public: int iPub = 2, *iPPub = nullptr; myQTest(); ~myQTest(); void fPub1(int); void fPub2(int) const; void fPub3(int*); void fPub4(int*) const; void fPub5(int *const); void fPub6(int *const) const; void fPub7(const int); void fPub8(const int) const; void fPub9(const int*); void fPub10(const int*) const; void fPub11(const int *const); void fPub12(const int *const) const; };
The first thing I realized is, that a "this"-pointer isn't const. So the explanation in the external link topic I mentioned above is either wrong or I missunderstood it.
The second thing I do not understand is, that I can write the following code without compiler error:
void myQTest::fPub3(int *const i) // The declaration was int*, NOT int const*! { }
How is it possible to define this function with a const pointer to int although it was declared as a non-const pointer to int?
-
@Christian-Ehrlicher Oh, I don't know what's wrong actually but I simply do not understand it.
Look at this example that I found on my google research. It exactly explains this situation.
There is written, that a "this"-pointer is a const pointer, hence T* const this.
Now, in a const function, the object, this const pointer points to will also get const, hence const T* const thisSo why do you say that in this case, the pointer itsself was not const?
@Binary91-0 I think it would be good to take a step back and go through it step by step, going back to your example:
int TreeItem::row() const { if (m_parentItem) return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this)); return 0; }
We're inside a const function, so
this
is of typeconst TreeItem*
, so pointer to constant TreeItem.m_childItems
is of typeQVector<TreeItem*>
, so vector'sT
in this case isTreeItem*
- non-const pointer to non-const TreeItem.QVector has a
int QVector::indexOf(const T & value, int from) const
method, so substitutingT
it is:int QVector::indexOf(TreeItem * const & value, int from) const
meaning it takes a reference to constant pointer to non-const TreeItem. The
const
on the function means it won't change any member of QVector. It has nothing to do with the parameter. And it makes sense, because finding an index of an item has no business modifying the container.Btw.
const
position in C++ syntax is a bit crazy so be careful not to fall into the trap. If you just naively replaceT
like this:const T&
->const TreeItem* &
then you've done it wrong. This would give you a reference to non-const pointer to const TreeItem, which is not what is happening here. Look up the "east const vs west const" to have a peek at the war that is going on in the language lawyers world :)Ok, so we have a
this
pointer which is a non-const pointer to const TreeItem and we have a function that expects a reference to const pointer to non const TreeItem. We have two mismatches here - first is on the type of the pointer and the second is on the item. The first one is easy because, as we previously discussed ,const can be added implicitly. The second one needs to take const off the item, and, as we also discussed, this can't be done automatically, so aconst_cast
is required to take the const off.So to summarize:
this -> const TreeItem* const_cast<TreeItem*>(this) -> TreeItem* indexOf(const_cast<TreeItem*>(this)) -> TreeItem* const &
that const in last type evaluation says that the function promises not to change the value of TreeItem*, so the pointer. It could, if it wanted, change the value of TreeItem or call a non-const members on it, but this is a generic container and it won't even know what T is, so it doesn't know T is a pointer and so it won't try to dereference that pointer.
-
Sorry for double posting, but this may exceed the threads topic now.
I created an example application with a test class to try all possible combinations of pointers and classic variables in const member functions and non-const member functions.
class myQTest { private: int iPriv = 1, *iPPriv = nullptr; void fPriv(); public: int iPub = 2, *iPPub = nullptr; myQTest(); ~myQTest(); void fPub1(int); void fPub2(int) const; void fPub3(int*); void fPub4(int*) const; void fPub5(int *const); void fPub6(int *const) const; void fPub7(const int); void fPub8(const int) const; void fPub9(const int*); void fPub10(const int*) const; void fPub11(const int *const); void fPub12(const int *const) const; };
The first thing I realized is, that a "this"-pointer isn't const. So the explanation in the external link topic I mentioned above is either wrong or I missunderstood it.
The second thing I do not understand is, that I can write the following code without compiler error:
void myQTest::fPub3(int *const i) // The declaration was int*, NOT int const*! { }
How is it possible to define this function with a const pointer to int although it was declared as a non-const pointer to int?
@Binary91-0 said:
The first thing I realized is, that a "this"-pointer isn't const
this
is a prvalue. Yes, it's complicated, but one of the things it means is that, while you can't put a const (or volatile) on it, it is still non-modifiable, likenullptr
or the result of built-in expressions. You can only assign it to stuff.To make it a little more confusing (because why not :P) some compilers (e.g. msvc and gcc in the past) implemented
this
asT* const
in non-const methods andconst T* const
in const methods to ensure that non-modifiability, but that is not standard conforming and it kinda blew up when C++11 introduced r-value references. Oh well, it's a subtle corner case I guess. -
Sorry for double posting, but this may exceed the threads topic now.
I created an example application with a test class to try all possible combinations of pointers and classic variables in const member functions and non-const member functions.
class myQTest { private: int iPriv = 1, *iPPriv = nullptr; void fPriv(); public: int iPub = 2, *iPPub = nullptr; myQTest(); ~myQTest(); void fPub1(int); void fPub2(int) const; void fPub3(int*); void fPub4(int*) const; void fPub5(int *const); void fPub6(int *const) const; void fPub7(const int); void fPub8(const int) const; void fPub9(const int*); void fPub10(const int*) const; void fPub11(const int *const); void fPub12(const int *const) const; };
The first thing I realized is, that a "this"-pointer isn't const. So the explanation in the external link topic I mentioned above is either wrong or I missunderstood it.
The second thing I do not understand is, that I can write the following code without compiler error:
void myQTest::fPub3(int *const i) // The declaration was int*, NOT int const*! { }
How is it possible to define this function with a const pointer to int although it was declared as a non-const pointer to int?
@Binary91-0 said in QListWidget::addItems() Data type question:
How is it possible to define this function with a const pointer to int although it was declared as a non-const pointer to int?
The standard says that cv-qualifiers (const and volatile) of function parameters don't affect function type, meaning that you can drop const and volatile keywords in the declaration. IMO this is silly and unnecessary, but it doesn't harm anyone (apart from confusion and readability I guess). The interface says it might modify the parameter (no const in declaration) but it doesn't (const in definition).
Btw. sorry for tripple posting. I wanted to answer each issue separately not to mix things up.
-
First of all, thank you really much again for this detailed support. It made things much clearer!
We're inside a const function, so this is of type const TreeItem*, so pointer to constant TreeItem.
The const on the function means it won't change any member of QVector. It has nothing to do with the parameter. And it makes sense, because finding an index of an item has no business modifying the container.
Ok, this is clear for me now. The declaration of a member function to be const just tells the compiler to ensure that no object members are changed inside of it.
Btw. const position in C++ syntax is a bit crazy so be careful not to fall into the trap. If you just naively replace T like this: const T& -> const TreeItem* & then you've done it wrong. This would give you a reference to non-const pointer to const TreeItem, which is not what is happening here. Look up the "east const vs west const" to have a peek at the war that is going on in the language lawyers world :)
That's it! Thank you so much! This was the main thing I did not understand. But after thinking about it, it makes sense. The function wants to tell the user that the "thing" he passes to it will be const. By passing a pointer, hence the pointer will be const.
Ok, so we have a this pointer which is a non-const pointer to const TreeItem and we have a function that expects a reference to const pointer to non const TreeItem. We have two mismatches here - first is on the type of the pointer and the second is on the item.
The first one is easy because, as we previously discussed ,const can be added implicitly.
What do you mean with
"implicitly"
? Do you mean that the compiler does an implicit type conversion ofT
toT const
because we call the functionby reference
? If yes, then compiler had to do a re-conversion back tonon-const
after returning from the function to ensure not to have changed the source, right? What I mean: Will the source (i.e. theQTreeItem*
) be re-declared as aQTreeItem *const
inside the function body as I call itby reference
or is it again just a hint for the compiler to handle thenon-const QTreeItem pointer
as aconst pointer
without "really/physically" changing anything of the source/source type?What would happen in a
call-by-value
condition? As it is possible to copy variables or pointers of the same typeT
into variables or pointers of typeT const
, it should be no problem at all, right? In this case, compiler shouldn't have to do any "implicit type conversions" as there will only be a copy created. On the other hand, in acall-by-value
condition, I can't see any sense in declaring the function parameter as const, because no changes can be done to the origin source, right? (This is just for me to see if I slowly get the point of this stuff or if I'm still far away from understanding the usability of usingconst
andcall-by-ref
vsnon-const
andcall-by-val
).The standard says that cv-qualifiers (const and volatile) of function parameters don't affect function type, meaning that you can drop const and volatile keywords in the declaration.
Interestingly, I can't do this arbitrary adding of "const" in every of the functions mentioned above.
Origin:void myQTest::fPub1(int i) { } void myQTest::fPub3(int *i) { }
These definition changes are possible:
void myQTest::fPub1(const int i) { } void myQTest::fPub3(int *const i) { }
But this one gives a compiler error:
void myQTest::fPub3(const int *i) { }
As I know now, that
const
can be added "arbitrarily", I wonder why the last example dosn't work. I mean, it is possible to initialize anon-const pointer to a const int
with anon-const pointer to a non-const int
, because the following code works:int i1 = 1; int *iP1 = &i1; int const *icP1 = iP1;
What's the reason for this behavior?