tags:

views:

59

answers:

1

i got a godgiven list of xyz (the code says int, just an example) glued into a QList (to big to move anywhere). How can I create a Model View for that? I allready read the Qt doc which tells me, I have to reimplement data, index, parent, rowCount, columnCount functions. But the preprocessor/compiler cries for more reimplemented functions? I allready read a chapter in my Qt Book but it did not help either. Here my hacked away code:

class XModel : public QAbstractListModel
{
Q_OBJECT
public:
    explicit XModel(QList<int> *valuelist, QObject *parent = 0);
    virtual ~XModel();
    int rowCount(const QModelIndex &) const;
    int columnCount(const QModelIndex &) const;
    QModelIndex index( int row, int column, const QModelIndex & parent = QModelIndex()) const;
    QModelIndex parent(const QModelIndex &index) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
private:
    QList<int>* blah;
signals:

public slots:

};



XModel::XModel(QList<int> *valuelist, QObject *parent) :
    QAbstractListModel(parent),
    blah(valuelist)
{

}




XModel::~XModel()
{

}



int XModel::rowCount(const QModelIndex &) const
{
    return blah->size();
}

int XModel::columnCount(const QModelIndex &) const
{
    return 1;
}


QModelIndex XModel::index(int row, int column, const QModelIndex &parent) const
{
    return createIndex(row, column, (void)&(blah->at(row)));
}



QModelIndex XModel::parent(const QModelIndex &index) const
{
    return createIndex(index->row(), index->column(), NULL); 
}




QVariant XModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const
{
    return QVariant(blah->at(index.row()));
}

Do I even have to use QAbstractItemModel or does QAbstractListModel work the exact same way? How do I give the Model the source of the actual data? Is this only within data function? Please tell me what I am doing wrong, I do not see it and give advice on howto do it properly, (good) howtos welcome.

This is fixed, but...

EDIT:

Widget::Widget(QWidget *parent)
    : QWidget(parent),
    valuelist(),
    xm(&valuelist) //xm = XModel
{
    valuelist.append(1);
    valuelist.append(2);
    valuelist.append(3);
    valuelist.append(4);
    valuelist.append(5);
    valuelist.append(6);
    valuelist.append(7);
    valuelist.append(8);
    valuelist.append(9);

    view = new QListView(this);
    view->setModel(&xm);
    //how to force the XModel to reread the QList`?
    view->show();
}

Thanks for any help, sry for the above confusion...

+2  A: 

Add to and remove data from XModel and have XModel modify the underlying list (reference?) for you:

Widget::Widget(QWidget *parent)
    : QWidget(parent),
    valuelist(),
    xm(&valuelist) //xm = XModel
{
    xm.append(1);
    xm.append(2);
    xm.append(3);
    xm.append(4);
    xm.append(5);
    xm.append(6);
    xm.append(7);
    xm.append(8);
    xm.append(9);

    view = new QListView(this);
    view->setModel(&xm);

    xm.append(10); // should call beginInsertRows, append to valuelist, and call endInsertRows.

    Q_ASSERT(valuelist.contains(10));

    view->show();
}

Otherwise, you could perhaps create a mix of QObject and QList that can emit signals to notify XModel of changes, but I think the first approach is better.

Edit:

Here is a silly example. It will create a list-backed model that will append more elements to the list every second:

#include <QtGui/QApplication>
#include <QtGui/QListView>
#include <QtCore/QAbstractListModel>
#include <QtCore/QTimer>

class ListBackedModel : public QAbstractListModel
{
    Q_OBJECT
    QList<int>* m_list;

public:

    ListBackedModel(QList<int>* list, QObject* parent = 0)
        : QAbstractListModel(parent)
        , m_list(list)
    {}

    ~ListBackedModel()
    {}

    int rowCount(const QModelIndex &parent = QModelIndex()) const
    {
        Q_UNUSED(parent);
        return m_list->size();
    }

    QVariant data(const QModelIndex &index, int role) const
    {
        if (index.row() >= rowCount()) { return QVariant(); }
        if (index.row() < 0) { return QVariant(); }

        int element = m_list->at(index.row());

        if (Qt::DisplayRole == role) {
            return QString::number(element);
        }

        if (Qt::ToolTipRole == role) {
            return tr("%1 is element #%2").arg(element).arg(index.row() + 1);
        }

        return QVariant();
    }

    void append(int element)
    {
        /*
            First is the new index of the first element that will be inserted.
            Last is the new index of the last element that will be inserted.
            Since we're appending only one element at the end of the list, the
            index of the first and last elements is the same, and is equal to
            the current size of the list.
        */
        int first = m_list->size();
        int last = first;

        beginInsertRows(QModelIndex(), first, last);
        m_list->append(element);
        endInsertRows();
    }

    void startAddingMoreElements()
    {
        QTimer::singleShot(1000, this, SLOT(addMoreElements()));
    }

private slots:

    void addMoreElements()
    {
        append(qrand() % 100);
        startAddingMoreElements();
    }
};


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QList<int> list;
    list << 1 << 10 << 34 << 111;
    ListBackedModel model(&list);

    QListView listView;
    listView.setModel(&model);
    listView.show();

    model.startAddingMoreElements();

    return a.exec();
}

#include "main.moc"
andref
And btw, QLists are implicitly shared... Moving them around is free and copying is free until the first change is made to the copy.
andref
penguinpower
The parent is only necessary for a tree model. For tables and lists, pass a default constructed value, QModelIndex(). The documentation for beginInsertRows has a nice illustration explaining the meaning of first and last. (http://doc.qt.nokia.com/4.6/qabstractitemmodel.html#beginInsertRows)
andref
This did not work and I have no clue why... void XModel::append(const int blah->append(t); endInsertRows(); }also tested with 1,1 for first,last
penguinpower
If you pass 0, 0, you are telling Qt that you are prepending an element to the model. But then you append an element to the internal list -- this breaks the expectations of the list view. If you pass (..., 0, 0), use QList::prepend. If you use QList::append, pass (..., blah->size(), blah->size()). See the code above.
andref