<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Populating a TabBar from a model]]></title><description><![CDATA[<p dir="auto">Hi all -</p>
<p dir="auto">I'd like to create a TabBar with buttons derived from a C++ model. Each button would get its text from a property in the model.</p>
<p dir="auto">Doable? Perhaps with addItem()?</p>
<p dir="auto">Thanks...</p>
]]></description><link>https://forum.qt.io/topic/145973/populating-a-tabbar-from-a-model</link><generator>RSS for Node</generator><lastBuildDate>Sun, 25 Oct 2026 02:15:28 GMT</lastBuildDate><atom:link href="https://forum.qt.io/topic/145973.rss" rel="self" type="application/rss+xml"/><pubDate>Fri, 23 Jun 2023 23:25:12 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Populating a TabBar from a model on Mon, 26 Jun 2023 10:18:20 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mzimmers">@<bdi>mzimmers</bdi></a> sorry, there is more than one thing going on here. My original suggestion was to use the <code>Repeater</code> which you have indeed followed. But then you asked about the model exposure in QML.</p>
<p dir="auto">The usual way to expose a <code>QAbstractListModel</code> to QML is to override the <code>data</code> function. This returns a <code>QVariant</code> so whatever you return needs to be storable in a <code>QVariant</code>.</p>
<p dir="auto">If you have a model that has a number of properties at each index, let's say "name", "age", "occupation", probably the most natural way to expose this is to override <code>roleNames()</code> and <code>data()</code>:</p>
<pre><code>class PersonListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    PersonListModel (QObject* parent);

    enum MyRoles {
        NameRole = Qt::UserRole + 1,
        AgeRole,
        OccupationRole
    };
    ...
    private:
        QHash&lt;int, QString&gt; m_roles;
</code></pre>
<pre><code>PersonListModel ::PersonListModel (QObject* parent) {
    // init roles
    m_roles[NameRole] = "name";
    m_roles[AgeRole] = "age";
    m_roles[OccupationRole] = "occupation";
    ...
}

// Override QAbstractItemModel function...
QHash&lt;int, QByteArray&gt; PersonListModel ::roleNames() const
{
    return m_roles;
}

// Override QAbstractItemModel function...
QVariant PersonListModel::data(const QModelIndex &amp;index, int role) const 
{
    if (!index.isValid())
        return QVariant();

    // For illustration only, the below assumes your actual data is stored
    // in something like a list of structs with `name`, `age` fields etc. 
   // Obviously you can implement this in any way you like as long as you
   // can take an index and return the requested value

    if (index.row() &lt; 0 || index.row() &gt;= m_myModelData.size())
        return QVariant();

    switch (role) {
    case NameRole:
        return m_myModelData[index.row()].name;
    case AgeRole:
        ...
}
</code></pre>
<p dir="auto">Now, the point about <code>roleNames()</code> in the above is that those strings that you associated with the enum values are what get exposed as property names on the QML side.</p>
<p dir="auto">The QML does a lot of work for you given the above.  In a delegate that references the model, you can directly refer to <code>name</code>, <code>age</code> etc., and under the hood, Qt will be calling <code>data</code> with the current <code>index</code> and  the requested role ID and will be extracting the value from the returned <code>QVariant</code> into a QML-compatible type.</p>
<p dir="auto">Relating this to your case, there are two approaches you could take. One is to "flatten" the model in a sense so that you don't need <code>Zone</code> as a type in this particular part of the QML code. So, what you might do is to define a <code>ZoneNameRole</code> mapped to <code>"zoneName"</code>. You would implement your <code>data()</code> method something like this:</p>
<pre><code>        case ZoneNameRole:
            return getZone(index.row()).name;
        ...
</code></pre>
<p dir="auto">Then in your QML, you could assign your button text simply as:</p>
<pre><code>Repeater {
    model: zoneModel
    delegate: TabButton {
        text: zoneName
    }
}
</code></pre>
<p dir="auto">The other option is to expose <code>Zone</code> explicitly from the model as a role in its own right. This is possible because <code>Zone</code> is a <code>Q_GADGET</code> so is itself a type that interoperates with QML. The only thing you need to do if you aren't doing so already is to have a line like this in the header where you define <code>Zone</code>:</p>
<pre><code>Q_DECLARE_METATYPE(Zone)
</code></pre>
<p dir="auto">This allows a <code>Zone</code> to be stored in a <code>QVariant</code>.</p>
<p dir="auto">With this approach your code might be more like this:</p>
<pre><code>class ZoneModel : public QAbstractListModel
{
    Q_OBJECT
public:
    ZoneModel (QObject* parent);

    enum MyRoles {
       ZoneRole = Qt::UserRole + 1,
    };
    ...
    private:
        QHash&lt;int, QString&gt; m_roles;
</code></pre>
<pre><code>ZoneModel ::ZoneModel (QObject* parent) {
    // init roles
    m_roles[ZoneRole] = "zone";
    ...
}

// Override QAbstractItemModel function...
QHash&lt;int, QByteArray&gt; ZoneModel ::roleNames() const
{
    return m_roles;
}

// Override QAbstractItemModel function...
QVariant ZoneModel::data(const QModelIndex &amp;index, int role) const 
{
    // ... check preconditions etc as before ...

    switch (role) {
    case ZoneRole:
        return getZone(index.row());
        ...
}
</code></pre>
<p dir="auto">And in the QML:</p>
<pre><code>Repeater {
    model: zoneModel
    delegate: TabButton {
        text: zone.name
    }
}
</code></pre>
<p dir="auto">The official documentation is here:</p>
<p dir="auto"><a href="https://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel-subclass" target="_blank" rel="noopener noreferrer nofollow ugc">https://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel-subclass</a></p>
]]></description><link>https://forum.qt.io/post/762627</link><guid isPermaLink="true">https://forum.qt.io/post/762627</guid><dc:creator><![CDATA[Bob64]]></dc:creator><pubDate>Mon, 26 Jun 2023 10:18:20 GMT</pubDate></item><item><title><![CDATA[Reply to Populating a TabBar from a model on Mon, 26 Jun 2023 15:08:50 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/bob64">@<bdi>Bob64</bdi></a> first, thank you <strong>very</strong> much for the detailed answer. This very nicely corroborates and augments <a href="https://www.youtube.com/watch?v=9BcAYDlpuT8" target="_blank" rel="noopener noreferrer nofollow ugc">the Qt tutorial</a> on this subject.</p>
<p dir="auto">As it turns out, I had already reimplemented data(), setData() and roleNames() in my model. Everything was there; I just wasn't using it (properly) in my QML. This is my roleNames:</p>
<pre><code>QHash&lt;int, QByteArray&gt; ZoneModel::roleNames() const
{
    QHash&lt;int, QByteArray&gt; zones;
    zones[NameRole] = "name";
    ...
    return zones;
}
</code></pre>
<p dir="auto">and my data() (error checking removed):</p>
<pre><code>QVariant ZoneModel::data(const QModelIndex &amp;index, int role) const
{
    QVariant qv = QVariant();

    Zone item = m_list-&gt;zones().at(index.row());
    switch (role) {
    case NameRole:
        qv = item.name();
        break;
    default:
        qv = QVariant();
    }
    return qv;
}
</code></pre>
<p dir="auto">(The additional level of indirection is that my actual list is defined in its own class (not the model class); again, this is from the tutorial.</p>
<p dir="auto">So, all I had to do in my QML is:</p>
<pre><code>Repeater {
    model: zoneModel
    delegate: TabButton {
        text: name
    }
}
</code></pre>
<p dir="auto">Very easy, once one understands all the pieces of the model and how they work together.</p>
<p dir="auto">Thank you again for the great explanation.</p>
]]></description><link>https://forum.qt.io/post/762650</link><guid isPermaLink="true">https://forum.qt.io/post/762650</guid><dc:creator><![CDATA[mzimmers]]></dc:creator><pubDate>Mon, 26 Jun 2023 15:08:50 GMT</pubDate></item><item><title><![CDATA[Reply to Populating a TabBar from a model on Mon, 26 Jun 2023 10:18:20 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mzimmers">@<bdi>mzimmers</bdi></a> sorry, there is more than one thing going on here. My original suggestion was to use the <code>Repeater</code> which you have indeed followed. But then you asked about the model exposure in QML.</p>
<p dir="auto">The usual way to expose a <code>QAbstractListModel</code> to QML is to override the <code>data</code> function. This returns a <code>QVariant</code> so whatever you return needs to be storable in a <code>QVariant</code>.</p>
<p dir="auto">If you have a model that has a number of properties at each index, let's say "name", "age", "occupation", probably the most natural way to expose this is to override <code>roleNames()</code> and <code>data()</code>:</p>
<pre><code>class PersonListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    PersonListModel (QObject* parent);

    enum MyRoles {
        NameRole = Qt::UserRole + 1,
        AgeRole,
        OccupationRole
    };
    ...
    private:
        QHash&lt;int, QString&gt; m_roles;
</code></pre>
<pre><code>PersonListModel ::PersonListModel (QObject* parent) {
    // init roles
    m_roles[NameRole] = "name";
    m_roles[AgeRole] = "age";
    m_roles[OccupationRole] = "occupation";
    ...
}

// Override QAbstractItemModel function...
QHash&lt;int, QByteArray&gt; PersonListModel ::roleNames() const
{
    return m_roles;
}

// Override QAbstractItemModel function...
QVariant PersonListModel::data(const QModelIndex &amp;index, int role) const 
{
    if (!index.isValid())
        return QVariant();

    // For illustration only, the below assumes your actual data is stored
    // in something like a list of structs with `name`, `age` fields etc. 
   // Obviously you can implement this in any way you like as long as you
   // can take an index and return the requested value

    if (index.row() &lt; 0 || index.row() &gt;= m_myModelData.size())
        return QVariant();

    switch (role) {
    case NameRole:
        return m_myModelData[index.row()].name;
    case AgeRole:
        ...
}
</code></pre>
<p dir="auto">Now, the point about <code>roleNames()</code> in the above is that those strings that you associated with the enum values are what get exposed as property names on the QML side.</p>
<p dir="auto">The QML does a lot of work for you given the above.  In a delegate that references the model, you can directly refer to <code>name</code>, <code>age</code> etc., and under the hood, Qt will be calling <code>data</code> with the current <code>index</code> and  the requested role ID and will be extracting the value from the returned <code>QVariant</code> into a QML-compatible type.</p>
<p dir="auto">Relating this to your case, there are two approaches you could take. One is to "flatten" the model in a sense so that you don't need <code>Zone</code> as a type in this particular part of the QML code. So, what you might do is to define a <code>ZoneNameRole</code> mapped to <code>"zoneName"</code>. You would implement your <code>data()</code> method something like this:</p>
<pre><code>        case ZoneNameRole:
            return getZone(index.row()).name;
        ...
</code></pre>
<p dir="auto">Then in your QML, you could assign your button text simply as:</p>
<pre><code>Repeater {
    model: zoneModel
    delegate: TabButton {
        text: zoneName
    }
}
</code></pre>
<p dir="auto">The other option is to expose <code>Zone</code> explicitly from the model as a role in its own right. This is possible because <code>Zone</code> is a <code>Q_GADGET</code> so is itself a type that interoperates with QML. The only thing you need to do if you aren't doing so already is to have a line like this in the header where you define <code>Zone</code>:</p>
<pre><code>Q_DECLARE_METATYPE(Zone)
</code></pre>
<p dir="auto">This allows a <code>Zone</code> to be stored in a <code>QVariant</code>.</p>
<p dir="auto">With this approach your code might be more like this:</p>
<pre><code>class ZoneModel : public QAbstractListModel
{
    Q_OBJECT
public:
    ZoneModel (QObject* parent);

    enum MyRoles {
       ZoneRole = Qt::UserRole + 1,
    };
    ...
    private:
        QHash&lt;int, QString&gt; m_roles;
</code></pre>
<pre><code>ZoneModel ::ZoneModel (QObject* parent) {
    // init roles
    m_roles[ZoneRole] = "zone";
    ...
}

// Override QAbstractItemModel function...
QHash&lt;int, QByteArray&gt; ZoneModel ::roleNames() const
{
    return m_roles;
}

// Override QAbstractItemModel function...
QVariant ZoneModel::data(const QModelIndex &amp;index, int role) const 
{
    // ... check preconditions etc as before ...

    switch (role) {
    case ZoneRole:
        return getZone(index.row());
        ...
}
</code></pre>
<p dir="auto">And in the QML:</p>
<pre><code>Repeater {
    model: zoneModel
    delegate: TabButton {
        text: zone.name
    }
}
</code></pre>
<p dir="auto">The official documentation is here:</p>
<p dir="auto"><a href="https://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel-subclass" target="_blank" rel="noopener noreferrer nofollow ugc">https://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel-subclass</a></p>
]]></description><link>https://forum.qt.io/post/762627</link><guid isPermaLink="true">https://forum.qt.io/post/762627</guid><dc:creator><![CDATA[Bob64]]></dc:creator><pubDate>Mon, 26 Jun 2023 10:18:20 GMT</pubDate></item><item><title><![CDATA[Reply to Populating a TabBar from a model on Sun, 25 Jun 2023 19:58:41 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/bob64">@<bdi>Bob64</bdi></a> said in <a href="/post/762577">Populating a TabBar from a model</a>:</p>
<blockquote>
<p dir="auto">isn't going to work as seamlessly on the QML side as the approach I mentioned</p>
</blockquote>
<p dir="auto">I thought I <em>was</em> using the approach you mentioned. Where did I deviate from your suggestion?</p>
<p dir="auto">Thanks...</p>
]]></description><link>https://forum.qt.io/post/762578</link><guid isPermaLink="true">https://forum.qt.io/post/762578</guid><dc:creator><![CDATA[mzimmers]]></dc:creator><pubDate>Sun, 25 Jun 2023 19:58:41 GMT</pubDate></item><item><title><![CDATA[Reply to Populating a TabBar from a model on Sun, 25 Jun 2023 19:48:00 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mzimmers">@<bdi>mzimmers</bdi></a> ok, I see. The approach you have taken is a way to expose a backend C++ model to QML but it probably isn't going to work as seamlessly on the QML side as the approach I mentioned, which essentially involves overriding <code>roleNames</code> and <code>data</code>.</p>
<p dir="auto">Another approach is a hybrid, where you define a <code>"zone"</code> role and have <code>data</code> return a <code>Zone</code> corresponding to the requested index. Note that <code>data</code> returns a <code>QVariant</code> but it should be possible to arrange for your <code>Zone</code> type to be storable in a <code>QVariant</code> (it might already just work with it being a <code>Q_GADGET</code> - I forget the details at the moment and can't look them up). On the QML side, you should then be able to do something like <code>text: zone.name</code>.</p>
]]></description><link>https://forum.qt.io/post/762577</link><guid isPermaLink="true">https://forum.qt.io/post/762577</guid><dc:creator><![CDATA[Bob64]]></dc:creator><pubDate>Sun, 25 Jun 2023 19:48:00 GMT</pubDate></item><item><title><![CDATA[Reply to Populating a TabBar from a model on Sun, 25 Jun 2023 14:27:52 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/piervalli">@<bdi>piervalli</bdi></a> I tried that:</p>
<pre><code>Repeater {
    model: zoneModel
    delegate: TabButton {
        text: getZone(index).name
    }
}
</code></pre>
<p dir="auto">Still yet runtime error not defined.</p>
<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/bob64">@<bdi>Bob64</bdi></a> my ZoneModel class contains a list of Zone objects. In order to get the names of the individual zones, I use getZone() to return the zone, then get its name from one of its properties.</p>
<pre><code>class ZoneModel: public QAbstractListModel
{
    Q_OBJECT
    ZoneList *m_list;
public:
    Q_INVOKABLE Zone getZone(int index);
...

class Zone {
    Q_GADGET
    QString m_name;
public:
    Q_PROPERTY(QString name MEMBER m_name)
    QString name() const { return m_name; }
...
</code></pre>
]]></description><link>https://forum.qt.io/post/762569</link><guid isPermaLink="true">https://forum.qt.io/post/762569</guid><dc:creator><![CDATA[mzimmers]]></dc:creator><pubDate>Sun, 25 Jun 2023 14:27:52 GMT</pubDate></item><item><title><![CDATA[Reply to Populating a TabBar from a model on Sun, 25 Jun 2023 08:42:17 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mzimmers">@<bdi>mzimmers</bdi></a> can you add a <code>zoneName</code> (or maybe just <code>name</code>) role to your model? That would automatically expose <code>zoneName</code> as a property available to your delegate and allow you to do <code>text: zoneName</code> in your button without even having to mention the index.</p>
]]></description><link>https://forum.qt.io/post/762544</link><guid isPermaLink="true">https://forum.qt.io/post/762544</guid><dc:creator><![CDATA[Bob64]]></dc:creator><pubDate>Sun, 25 Jun 2023 08:42:17 GMT</pubDate></item><item><title><![CDATA[Reply to Populating a TabBar from a model on Sun, 25 Jun 2023 02:10:01 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/bob64">@<bdi>Bob64</bdi></a> yep...that did the trick:</p>
<pre><code>TabBar {
width: parent.width

Repeater {
    model: zoneModel
    delegate: TabButton {
        text: zoneModel.getZone(index).name
    }
}
</code></pre>
<p dir="auto">This works just fine. I wonder, though...is there a way for me to eliminate the inner reference to zoneModel? I tried just model.getZone() but it says it's not a function. There's probably another way to form that line but I don't know what it is.</p>
<p dir="auto">Thanks...</p>
]]></description><link>https://forum.qt.io/post/762539</link><guid isPermaLink="true">https://forum.qt.io/post/762539</guid><dc:creator><![CDATA[mzimmers]]></dc:creator><pubDate>Sun, 25 Jun 2023 02:10:01 GMT</pubDate></item><item><title><![CDATA[Reply to Populating a TabBar from a model on Sat, 24 Jun 2023 19:52:27 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mzimmers">@<bdi>mzimmers</bdi></a> you could use a <code>Repeater</code> in your <code>TabBar</code> with a delegate that creates a button. It could be driven by something based on <code>QAbstractItemModel</code> or <code>QAbstractListModel</code>.</p>
<p dir="auto">You could mock it up using a QML <code>ListModel</code> to start with.</p>
]]></description><link>https://forum.qt.io/post/762534</link><guid isPermaLink="true">https://forum.qt.io/post/762534</guid><dc:creator><![CDATA[Bob64]]></dc:creator><pubDate>Sat, 24 Jun 2023 19:52:27 GMT</pubDate></item><item><title><![CDATA[Reply to Populating a TabBar from a model on Sat, 24 Jun 2023 19:27:53 GMT]]></title><description><![CDATA[<p dir="auto">Hello. I read your requirements for desired widget, but didn't understand it completely. Can you do it more clear? I mean more description and visual presentation of it.</p>
<p dir="auto">Is it something like this?<br />
<img src="https://ddgobkiprc33d.cloudfront.net/c1c4caab-01f7-43ae-8b12-679a35e76942.png" alt="Screenshot_1.png" class=" img-fluid img-markdown" /></p>
]]></description><link>https://forum.qt.io/post/762532</link><guid isPermaLink="true">https://forum.qt.io/post/762532</guid><dc:creator><![CDATA[tzorake]]></dc:creator><pubDate>Sat, 24 Jun 2023 19:27:53 GMT</pubDate></item></channel></rss>