QStandardItemModel in QTableView



  • Hi,

    I'm wondering if it's possible to retrieve the item selected in a QTableView (not just the row, the real object)
    I'll explain a little bit.

    I'm building a QTableView using the model/view architecture. I add a few object in the model and this is shown in the view.
    Here is an example of the view populated with 2 objects:
    https://www.dropbox.com/s/94sdayh0q5y4vfz/workoutList.png

    When an item is double clicked in this view, I want to fire an action with the item that has been selected. My question is, when i'm populating the listView, I only add QString in it, never add the reference or the "real" object. How can I retrieve the object after that? Is there a way to add the real object in the Model, not just QString of the attributes?
    Here is the corresponding code.
    Sorry if it's a newbie question, I'm a Java guy... In java we could do this with annotation in the object class and no need to specify number of row, column as it is detected it with the object proprieties and list size..

    Thanks !

    @//------
    void MainWindow::on_tableView_workout_doubleClicked(const QModelIndex &index) {

    // get Workout selected (object)
    // open Workout window with this object
    
    qDebug() << "Clicked!" << QString::number(index.row());
    qDebug() << index.model();
    

    }

    // ----- Building the QTableView/Model
    QList<Workout*> lstWork = new QList<Workout>();

    double timeInterval[5] = {1,1,1,1,1};
    int powerInterval[5] = {150,200,175,300,250};
    Workout *workout = new Workout(5, timeInterval, powerInterval, "KillerWorkout", "PowerVelo", Workout::Endurance);
    lstWork->append(workout);
    
    double timeInterval2[6] = {1,1,1,1,1,2};
    int powerInterval2[6] = {150,200,175,300,250,240};
    Workout *workout2 = new Workout(6, timeInterval2, powerInterval2, "testMax", "PowerVelo", Workout::Tempo);
    lstWork->append(workout2);
    
    
    QStringList headerList;
    headerList << "Name" << "Created by" << "Type" << "Length (minutes)" << "Max power";
    modelListWorkout = new QStandardItemModel(lstWork->count(), headerList.count(), this);
    
    for (int row = 0; row < lstWork->count(); row++) {
    
        modelListWorkout->setItem(row, 0, new QStandardItem(lstWork->at(row)->getName()));
        modelListWorkout->setItem(row, 1, new QStandardItem(lstWork->at(row)->getCreatedBy()));
        modelListWorkout->setItem(row, 2, new QStandardItem(lstWork->at(row)->getTypeToString()));
        modelListWorkout->setItem(row, 3, new QStandardItem(QString::number(lstWork->at(row)->getTotalLength())));
        modelListWorkout->setItem(row, 4, new QStandardItem(QString::number(lstWork->at(row)->getMaxPower())));
    
        for (int column = 0; column &lt; headerList.count(); column++) {
            modelListWorkout-&gt;setHorizontalHeaderLabels(headerList);
        }
    }
    
    
    ui->tableView_workout->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableView_workout->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableView_workout->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableView_workout->setModel(modelListWorkout);@

  • Lifetime Qt Champion

    Hi,

    You could use "QStandardItem::setData":http://qt-project.org/doc/qt-4.8/qstandarditem.html#setData to keep your data close to your items

    Hope it helps



  • Thanks for your answer,

    setData use a QVariant not a QObject?
    Guess I have to use something else. Because my Data is a custom class (Workout)

    Maybe I could add a item key in the row (not show it), and this key would be a pointer to my object, kind of like a QHashMap.
    I'll try that and keep you updated, thanks for your help!
    @
    QStandardItem *item = new QStandardItem();
    item->setData(lstWork->at(row)); //not legal@



  • Hi,
    Maybe it's just asking for you to subclass the QAbstractTableModel and control the data yourself instead of relying on the QStandardItemModel to do this. That way you are able to do all you want to achieve within your model. It's a natural next step in exploring the model/view capabilities.


  • Lifetime Qt Champion

    QVariant is designed to store custom type (as long as you provide the needed code)

    Have a look at QVariant::fromValue().

    As Jeroen@home wrote, implementing your own model is also a good alternative to have control over your data.



  • Thanks for your help guys,

    I already started implementing my own model. it's not 100% complete but seems to work fine.
    One last question about this, I would like my model to be generic, so I can re-use it with different object
    Because now my model is specific to <Workout> :
    @QList<Workout> lstWorkout; // not generic@

    I would like it to be generic so I don't need to do 4-5 implementations of QAbstractTableModel. Example: I'll use a table of <User>, a table of <Profile>, etc..
    The way I have coded MyTableModel.cpp, I can't seem to find a way to do it generic, as a lot of the function are hard-coded ; headerData,() data()..
    If anyone ever experienced this problem feel free to share, thanks ;)

    MyTableModel.H
    @#ifndef MYTABLEMODEL_H
    #define MYTABLEMODEL_H

    #include <QAbstractTableModel>
    #include "workout.h"

    class MyTableModel : public QAbstractTableModel {
    Q_OBJECT

    public:
    MyTableModel(QObject *parent = 0);

    void setListWorkout(QList<Workout>);
    
    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
    
    Workout getWorkoutAtRow(const QModelIndex &index);
    

    private:
    QList<Workout> lstWorkout;

    };

    #endif // MYTABLEMODEL_H@

    MyTableModel.cpp

    @#include "mytablemodel.h"
    #include "workout.h"
    #include <QDebug>

    MyTableModel::MyTableModel(QObject *parent) : QAbstractTableModel(parent) {
    }

    // -----------------------------------------------------------------
    QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const {

    if (role != Qt::DisplayRole)
        return QVariant();
    
    if (orientation == Qt::Vertical) {
        return QString::number(section);
    }
    else {
        switch (section) {
        case 0 :
            return "Name";
            break;
        case 1:
            return "Created by";
            break;
        case 2:
            return "Type";
            break;
        case 3:
            return "Length (minutes)";
            break;
        case 4:
            return "Max power";
            break;
        default :
            return "horizontal";
    
        }
    }
    

    }
    // -----------------------------------------------------------------
    void MyTableModel::setListWorkout(QList<Workout> lstWorkout) {

    beginResetModel();
    this->lstWorkout = lstWorkout;
    endResetModel();
    

    }

    // -----------------------------------------------------------------
    Workout MyTableModel::getWorkoutAtRow(const QModelIndex &index) {

    //    if (index.row() > lstWorkout.size()) {
    //        return NULL;
    //    }
    return lstWorkout.at(index.row());
    

    }

    // -----------------------------------------------------------------
    int MyTableModel::rowCount(const QModelIndex &parent) const {

    if (lstWorkout.isEmpty()) {
        return 0;
    }
    else {
        return lstWorkout.size();
    }
    

    }

    // -----------------------------------------------------------------
    int MyTableModel::columnCount(const QModelIndex &parent) const {

    if (lstWorkout.isEmpty()) {
        return 0;
    }
    else {
        return 5;  //TODO: count object private attributes
    }
    

    }

    // -----------------------------------------------------------------
    QVariant MyTableModel::data(const QModelIndex &index, int role) const {

    if (!index.isValid())
        return QVariant();
    
    if (index.row() >= lstWorkout.size())
        return QVariant();
    
    if (role == Qt::DisplayRole) {
    
        Workout work = lstWorkout.at(index.row());
    
        switch (index.column()) {
        case 0 :
            return work.getName();
            break;
        case 1:
            return work.getCreatedBy();
        case 2:
            return work.getTypeToString();
        case 3 :
            return work.getTotalLength();
        case 4:
            return work.getMaxPower();
        default:
            return "default";
    
        }
    }
    else
        return QVariant();
    

    }@



  • Hi,
    You codes looks the most basic way you need it to be. When getting deeper in it, you will increase functionality and control even further.
    Maybe just a small tip, but do not place so many return statements in a single function. The code is correct, but for reading it it's a bit confusing. Where will the code break, will allocated data be deleted properly etc.

    To answer your question, it's complicated to implement a 'standard' model to control custom data. Maybe there is a way to implement it with template classes, but I think the specifics of every custom model will not be able to be implemented.
    Also, why would you want a single model to control different user models? Not every model has the same headers/data etc. When you want this, it's better to stick to the QStandardItemModel. That's setup independant of custom models, but then again, your option for QObject * control etc will not work, but QVariant is able to do hold the QObject * or a pointer to any other class.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.