Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Nicest way to have QListWidgetItem with extra data?



  • I would like the experts' opinion on how I should implement the following in the "nicest" way....

    I have a list of items to present to the user to choose from. The items just present text to the user, but --- as in many situations --- they also need a value (integer) associated with each item, which I will to retrieve when items are selected. So I start with a list of items (Python, not a QList, nor model rows, so we don't start from anything particular suitable for Qt) with text & value, like:

    "Some Item", 3
    "Different", 4
    ...
    "Another Item", 1
    

    Now, if the list were single-selection, I would be using a QComboBox. That allows for items having a QVariant userData, and nice functions like https://doc.qt.io/qt-5/qcombobox.html#itemData or https://doc.qt.io/qt-5/qcombobox.html#findData. I have used that elsewhere.

    The problem is that the list is multi-selection. That rules out QComboBox, and I do not wish to display with a table-type widget only showing one column, I wish to use a list-type widget. That has led me to QListWidget & QListWidgetItem. I then see about 3 ways I could store my data:

    1. Sub-class QListWidgetItem and store the value in an explicit member variable.

    2. Sub-class QListWidgetItem and store the value via QListWidgetItem::setData() with some role. (I'm not sure I actually need to sub-class here: I think I can just use that for my purpose on a QListWidgetItem...?)

    3. Decide that I'd be better in this case moving to a QListView. Create a model with 2 columns (text & value), and use QListView::setModelColumn() to display the text column.

    Which would you do (and why)? My definition of "nicest" usually means "least lines of code for me to add", but for example if trying to use QListWidget(Item) for this is really pushing a round peg into a square hole I'm willing to move off that.


  • Moderators

    hi @JonB

    even so I personally don't do it, most of the time, option 3 is the way to go :)

    Not the fastest, least amount if code, but the cleanest and most fool proof one.

    Alternativ, you could use the Text as Key for a QHash, or normal hash, and go that way -> most likely the least amount of work.



  • @J.Hilk
    Thanks for your prompt :)

    Alternativ, you could use the Text as Key for a QHash, or normal hash, and go that way -> most likely the least amount of work.

    At least theoretically, I can't do that just in case a given text is duplicated with different values. Yes I know that's "dodgy", but it's not "right" to do it by look-up....

    So you'd rather create an explicit model than a special member variable or data role.... I suppose QListWidget actually just creates a model for you and attaches a QListView behind the scenes?


  • Moderators

    @JonB said in Nicest way to have QListWidgetItem with extra data?:

    So you'd rather create an explicit model than a special member variable or data role.... I suppose QListWidget actually just creates a model for you and attaches a QListView behind the scenes?

    I'm unsure, models and Views were always and still are an area I've the least experience in.

    That said, looking into the QListWidgetItem constructor, there's actually an constructor overload that accepts an int as 3rd parameter

    QListWidgetItem::QListWidgetItem(const QIcon &icon, const QString &text, QListWidget *parent = nullptr, int type = Type)

    and you can access it via type(), maybe you don't need to do anything extra?



  • @J.Hilk

    QListWidgetItem::QListWidgetItem(const QIcon &icon, const QString &text, QListWidget *parent = nullptr, int type = Type)

    No, I looked at that already, you can't store arbitrary data against each item via the int type = Type. That is used to indicate only the type of the item (custom item types, if you have need for that), it can only store values greater than QListWidgetItem::UserType.



  • No need to subclass anything or do anything fancy. The functionality is already implemented

    // QListWidgetItem* item
    item->setData(Qt::DisplayRole,QStringLiteral("Some Item"));
    item->setData(Qt::UserRole,3);
    

    now you can use item->data(Qt::UserRole).toInt() to retrieve it.

    As you QComboBox, it uses this method internally



  • @VRonin
    Yeah, that was indeed one of my proposed methods above, #2. As you say, I came to the conclusion that it didn't require sub-classing. Very interesting to hear that's the approach QComboBox is taking, gives me a warm, wet feeling :)

    Can I just ask you if you have a comment on the alternative approach #1 above, whereby I would sub-class QListWidgetItem to add a value member? Makes it so neat to access from code. It's the way my beloved ASP.NET does its ListItems --- they have text & (optional) value members --- whether these are in a combobox, list or whatever, and kind of feels "clean". What do you think of adding explicit member fields to derived-QListWidgetItems compared to going down the data() route? (BTW, I will probably use whichever solution generically to cover other places I will want this functionality, not just the particular case I have now, if that makes any difference.)



  • @JonB said in Nicest way to have QListWidgetItem with extra data?:

    What do you think of adding explicit member fields

    🤮

    How a class stores its data is an implementation detail and should not be exposed to the outside.
    I'd be 100% on board with subclassing and adding something like

    public:
    int type() const { return data(Qt::UserRole).toInt(); }
    void setType(int val) { setData(Qt::UserRole,val);}
    

    But as a C++ programmer everytime I see a:

    public:
    int type;
    

    I shoot a bunny. So up to you if you want the soul of an innocent rabbit to haunt you forever for your sins



  • @VRonin
    Well.... I'm a C/C#/JS/Python/Prolog programmer. C++ gives me headaches :) Of course I can wrap that public variable in a set/getter function (give me C# for this as a property any time...). So we'll be exposing int type() const & void setType(int val) from a sub-class in either approach.

    Now without that as an issue, I'm still really interested to hear whether you, as the implementer, want to store the value in a member variable or in the data() "table"/"property"/whatever you wish to call it?

    P.S.
    Absolutely no bunnies were harmed in composing this email.



  • @JonB said in Nicest way to have QListWidgetItem with extra data?:

    whether you, as the implementer, want to store the value in a member variable or in the data()

    I would use data but only because that way you can pass that value to whatever other tool that uses the QAbstarctItemModel API. But this is really down to the programmer, if you prefer a personalised private integer you are welcome to use it



  • @VRonin
    Perfect answer, thank you! Your point about doing it the data() way allowing it to be accessible to others who only know about QAbstractItemModel is an interesting one.


Log in to reply