<?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[Saving ListModel to file]]></title><description><![CDATA[<p dir="auto">Hello everyone,<br />
I am looking for approaches to saving and loading ListModel to any kind of file. Currently I have a ListModel located in qml, which gets data from c++ signals as custom qml types.<br />
I thought of moving ListModel to c++ by inheriting QAbstractListModel, but then I won't be able to write custom class objects to file using QDataStream because it doesn't allow classes inherited from QObject. My custom class needs to inherit QObject so I can register it as qml type and use it on qml side.<br />
I am new to the subject, so the question may sound silly, but what are the approaches to solving my problem?</p>
]]></description><link>https://forum.qt.io/topic/126874/saving-listmodel-to-file</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 00:27:16 GMT</lastBuildDate><atom:link href="https://forum.qt.io/topic/126874.rss" rel="self" type="application/rss+xml"/><pubDate>Fri, 21 May 2021 20:42:11 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Saving ListModel to file on Sun, 23 May 2021 14:12:31 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/jeremy_k">@<bdi>jeremy_k</bdi></a><br />
Thank you for such a detailed reply!<br />
You definitely solved my problem. Let me leave minimal working code for someone who has same problem:</p>
<p dir="auto"><em>Packet</em> class:</p>
<pre><code>class Packet
{
public:
    Packet();
    int number, length;
};
</code></pre>
<p dir="auto"><em>PacketModel</em> class:</p>
<pre><code>#include &lt;QAbstractListModel&gt;
#include &lt;QObject&gt;
#include &lt;QDataStream&gt;
#include &lt;QFile&gt;
#include &lt;packet.h&gt;

class PacketModel : public QAbstractListModel
{
    Q_OBJECT
    QList&lt;Packet&gt; m_packets;
    enum roles { NumberRole = Qt::UserRole, LengthRole };
public:
    PacketModel();

    int rowCount(const QModelIndex &amp;parent) const {
        return m_packets.length();
    }

    QVariant data(const QModelIndex &amp;index, int role) const {
        auto packetsIndex = index.row();
        const Packet &amp;p = m_packets.at(packetsIndex);
        switch(role){
            case NumberRole:
                return p.number;
            case LengthRole:
                return p.length;
            default:
                return data(index, role);
        }
    }

    QHash&lt;int, QByteArray&gt; roleNames() const{
        return QHash&lt;int, QByteArray&gt; {
            { NumberRole, "number"},
            { LengthRole, "length"}
        };
    }

    Q_INVOKABLE void add(){
        // Temporary method simulating insertion from other class
        Packet p;
        p.number = 1;
        p.length = 2;
        beginInsertRows(QModelIndex(), m_packets.size(), m_packets.size());
        m_packets.push_back(p);
        endInsertRows();
    }

    Q_INVOKABLE void saveModel(){
        QFile file("snifff.txt");
        file.open(QIODevice::WriteOnly);
        QDataStream stream(&amp;file);
        for(auto i : m_packets){
            stream &lt;&lt; i.number &lt;&lt; i.length;
        }
        file.close();
    }

    Q_INVOKABLE void loadModel(){
        QFile file("snifff.txt");
        file.open(QIODevice::ReadOnly);
        QDataStream stream(&amp;file);

        beginResetModel();
        m_packets.clear();

        while(!stream.atEnd()){
            Packet p;
            stream &gt;&gt; p.number &gt;&gt; p.length;
            m_packets.push_back(p);
        }

        endResetModel();
        file.close();
    }
};
</code></pre>
<p dir="auto"><em>PacketModel</em> class needs to be registered as qml type as well:</p>
<pre><code>    qmlRegisterType&lt;PacketModel&gt;("PacketModel", 1, 0, "PacketModel");
</code></pre>
<p dir="auto">Using model in qml:</p>
<pre><code>import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import PacketModel 1.0

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    ListView{
        width: parent.width
        height: parent.height
        id: listView

        model: packetModel

        delegate: Text{
            text: number + ", " + length
        }
    }

    PacketModel{
        id: packetModel
    }

    Button {
        text: "Add"
        anchors.right: parent.right
        onClicked: {
            packetModel.add()
        }
    }

    Button {
        text: "Save"
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        onClicked: {
            packetModel.saveModel()
        }
    }

    Button{
        text: "Load"
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        onClicked: {
            packetModel.loadModel()
        }
    }
}
</code></pre>
]]></description><link>https://forum.qt.io/post/661144</link><guid isPermaLink="true">https://forum.qt.io/post/661144</guid><dc:creator><![CDATA[keesaev]]></dc:creator><pubDate>Sun, 23 May 2021 14:12:31 GMT</pubDate></item><item><title><![CDATA[Reply to Saving ListModel to file on Sun, 23 May 2021 02:14:22 GMT]]></title><description><![CDATA[<p dir="auto">Seeing the code helps a lot. Unless there is a significant portion missing, doing it this way is adding code complexity and copying overhead without a benefit. In particular, there's no need for the intermediate packet QObject representation.</p>
<p dir="auto">If this was a QML only project, using ListModel and copying data into it might be reasonable. As there's already  a C++ portion, adding a QAbstractListModel shouldn't be difficult.</p>
<p dir="auto">Here's a rough sketch of a C++ model. It won't compile, or perform even basic error checking, but will hopefully convey the general idea.</p>
<pre><code>class PacketModel : public QAbstractListModel {
    Q_OBJECT
    QVector&lt;packet&gt; m_packets;
    enum class roles { NumberRole = Qt::UserRole, LengthRole };

public:
    struct packet {
        int number;
        int length;
    };

    // Load packets into an existing model
    void restorePackets(QVector&lt;packet&gt; packets) {
        beginResetModel();
        m_packets = packets;
        endResetModel();
    }

    // Connect to a signal that delivers packet data, or directly invoked from the UI thread.
    void appendPacket(packet p) {
        beginInsertRows(QModelIndex(), packets.size(), packets.size());
        m_packets.push_back(p);
        endInsertRows();
    }

// QAbstractListModel functions
    
    int QAbstractItemModel::rowCount(const QModelIndex &amp;parent) const override {
        return m_packets.length();
    }

    QVariant data(const QModelIndex &amp;index, int role) override {
        auto packetsIndex = index.row();
        const packet &amp;p = m_packets.at(packetsIndex);
        switch (role) {
        case roles.NumberRole:
            return p.number;
        case roles.LengthRole;
            return p.length;
        default:
            return QAbstractListModel::data(index, role);
        }
    }

    QHash&lt;int, QByteArray&gt; QAbstractItemModel::roleNames() const override {
        return QHash&lt;int, QByteArray&gt; { {roles.NumberRole, "number"}, 
                                        {roles.LengthRole, "length"} };
    }
};
</code></pre>
<p dir="auto">listview.qml:</p>
<pre><code>ListView {
    model: packetModel
    delegate: Text {
        text: "packet number " + model.number + " with length " model.length
    }
}
</code></pre>
]]></description><link>https://forum.qt.io/post/661059</link><guid isPermaLink="true">https://forum.qt.io/post/661059</guid><dc:creator><![CDATA[jeremy_k]]></dc:creator><pubDate>Sun, 23 May 2021 02:14:22 GMT</pubDate></item><item><title><![CDATA[Reply to Saving ListModel to file on Sat, 22 May 2021 10:12:27 GMT]]></title><description><![CDATA[<p dir="auto">Thanks for reply!<br />
Let me provide some code:<br />
<em>PacketData</em> is a class that represents a single row in ListView:</p>
<pre><code>class PacketData : public QObject
{
    Q_OBJECT
    QML_ELEMENT
public:
    explicit PacketData(QObject *parent = nullptr);

    int number;
    QString sourceIp, destIp, protocol, length, fullData;
    QTime timestamp;
    Q_INVOKABLE int getNumber(){ return number; }
    Q_INVOKABLE QTime getTimestamp(){ return timestamp; }
    Q_INVOKABLE QString getSourceIp(){ return sourceIp; }
    Q_INVOKABLE QString getDestIp(){ return destIp; }
    Q_INVOKABLE QString getProtocol(){ return protocol; }
    Q_INVOKABLE QString getLength(){ return length; }
    Q_INVOKABLE QString getFullData() { return fullData; }
};
</code></pre>
<p dir="auto">There's a class called <em>SnifferWrapper</em> that creates an object of <em>PacketData</em> and emits a signal <em>packetDeserialized</em>, I don't think that implementation of this class is important, I will just show how I emit and connect signal:</p>
<pre><code>// signal:
void packetDeserialized(PacketData *packet);
...
// creating an object of PacketData:
    PacketData *packet = new PacketData();
// filling it with data
...
// emitting signal
    emit packetDeserialized(packet);
    packet-&gt;deleteLater(); // calling deleteLater, because later in PacketsView I will copy members of this obect to listModel
...
// somewhere in main.qml:
    SnifferWrapper{
        id: snifferWrapper

// connecting to addPacketToView slot
        onPacketDeserialized: {
            packetsView.addPacketToView(packet)
        }
    }
</code></pre>
<p dir="auto">Then I have PacketsView.qml file - here I create ListModel and ListView:</p>
<pre><code>    ListModel{
        id: listModel
    }
...
// This function acts as a slot for packetDeserialized signal: 
    function addPacketToView(packet){
        listModel.append({
                    number: packet.getNumber(),
                    timestamp: Qt.formatTime(packet.getTimestamp(), "hh:mm:ss"),
                    source: packet.getSourceIp(),
                    destination: packet.getDestIp(),
                    protocol: packet.getProtocol(),
                    length: packet.getLength(),
                    fullData: packet.getFullData()
                });
    }
...
// Later in code there's a ListView, but I don't think it's important for the topic
</code></pre>
<p dir="auto">There's a naming mistake as function addPacketTo<em>View</em> actually adds packet to <em>model</em>, please don't get confused.</p>
<p dir="auto">How exactly should I implement model in c++ and then be able to save it? For example, if I have a class with a list:</p>
<pre><code>QList&lt;PacketData *&gt; list
</code></pre>
<p dir="auto">I won't be able to use QDataStream because PacketData inherits QObject. Also if I make PacketData class not inherit QObject, I won't be able to register it as qml type and use it in qml.</p>
<p dir="auto">I might be missing or not understanding something here. Also sorry for bad english</p>
]]></description><link>https://forum.qt.io/post/660991</link><guid isPermaLink="true">https://forum.qt.io/post/660991</guid><dc:creator><![CDATA[keesaev]]></dc:creator><pubDate>Sat, 22 May 2021 10:12:27 GMT</pubDate></item><item><title><![CDATA[Reply to Saving ListModel to file on Sat, 22 May 2021 06:36:11 GMT]]></title><description><![CDATA[<p dir="auto">Can you provide some code to explain the problem?</p>
<p dir="auto">The answer generally is to save and restore the basic data types that make up the entries in the model, rather than the layout of the objects in memory. This might be through providing a function in the model that exports or imports state as a list. In cases where adding to the model implementation isn't possible, walking it with QAbstractItemModel::data() or ListMode.get() can work. For large data sets, fetching each entry in sequence can be painfully slow.</p>
<p dir="auto">Going into more detail is easier with an implementation to discuss.</p>
]]></description><link>https://forum.qt.io/post/660970</link><guid isPermaLink="true">https://forum.qt.io/post/660970</guid><dc:creator><![CDATA[jeremy_k]]></dc:creator><pubDate>Sat, 22 May 2021 06:36:11 GMT</pubDate></item></channel></rss>