JoseOrtiz3
JoseOrtiz3

Reputation: 1883

Read and view tab-delimited txt files in Qt C++ using QTableView

I need some general advice to point me in the correct direction of solving this problem: I have a .txt file with table data like the following:

Time    Pin
11:00   EIO4
12:55   EIO6
16:40   EIO4
20:10   EIO3

etc......

I need to be able to parse this .txt file. Then I need to display this table in Qt (perhaps in a QTableView).

I have looked into using QTableView, and it appears I pass a QAbstractItemModel* to the QTableView using QTableView::setModel(QAbstractItemModel* model), after which my QTableView will display whatever data is in the QAbstractItemModel (right?).

If that last paragraph was logical, then my next question is how do I construct a QAbstractItemModel using my .txt file? I don't see any functions in the documentation that do this? (Note: someone suggested creating a QStandardItemModel)

(Note, this paragraph is no longer relevant) QModelIndex createIndex(int row, int column, void* ptr) appears to have something to do with this, maybe? Maybe this creates a QModelIndex that can be passed to the QAbstractItemModel using QAbstractItemModel::insertRow(QModelIndex)? However I still don't know how to make these QModelIndex objects have anything to do with my .txt data.

QStandardItemModel documentation says

When you want a list or tree, you typically create an empty QStandardItemModel and use appendRow() to add items to the model, and item() to access an item. If your model represents a table, you typically pass the dimensions of the table to the QStandardItemModel constructor and use setItem() to position items into the table. You can also use setRowCount() and setColumnCount() to alter the dimensions of the model. To insert items, use insertRow() or insertColumn(), and to remove items, use removeRow() or removeColumn().

These functions that "add rows" take QList<QStandardItem*>'s as arguments, so I need to create QLists that each contain pointers QStandardItems that contain each cell's data?

So I will use

void QStandardItemModel::insertRow(int row, const QList<QStandardItem *> & items)

http://doc.qt.io/qt-5/qstandarditemmodel.html#insertRow

Along with an argument QList<QStandardItem*> generated by repeated calls of "<<" passing QStandardItem* pointers to an empty QList.

I will make QStandardItem's using the constructor

QStandardItem::QStandardItem(const QString & text)

where I get my QString text by parsing a QFile into QStrings.

Everything looking good?

Or maybe I should skip creating QLists and go directly from generating QStandardItem's to passing them into the QStandardItemModel using void QStandardItemModel::setItem(int row, int column, QStandardItem * item)

Edit: I ended up skipping creating QLists with the following process: QFile file -> QString line -> QStringList peices -> QStandardItem -> QStandardItemModel -> QTableView

Thanks to all who contributed

Upvotes: 0

Views: 1250

Answers (2)

Kevin Krammer
Kevin Krammer

Reputation: 5207

For the original question, i.e. how to create a custom model: this is fairly easy because there is a convenience base class for table models, QAbstractTableModel which implements most of the hairy methods of the general model interface.

The first step for a custom table model is to decide on a data type for a single row, e.g.

struct Data
{
    QTime time;
    QString pin;
};

The custom model can then operate on a list or vector of that type.

class MyModel : public QAbstractItemModel
{
private:
    QVector<Data> m_data;
};

The rowCount() method can then simply be implemented by returning the size/count of the list/vector, columnCount() is fixed (2 in this example) and data() can simply access the n-th entry and switch on requested column:

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    const Data &rowData = m_data[index.row()];

    switch (index.row()) {
    case 0: {
        if (role == Qt::DisplayRole) return rowData.time.toString("hh:mm"); // or whatever format you'd like
        else if (role == Qt::EditRole) return rowData.time; // return as QTime
        break;
    }
    case 1: if (role == Qt::DisplayRole || role == Qt::EditRole) return rowData.pin;
        break;
    default: break;
    }
    return QVariant();
}

Optionally implement headerData() to to get "Time" and "PIN" as the column headers

Upvotes: 0

gomons
gomons

Reputation: 1976

Your approach is correct. You can use QStandardItemModel for your data.

QStandardItemModel *model = new QStandardItemModel(2,3,this);
model->setHorizontalHeaderItem(0, new QStandardItem(QString("Column1 Header")));
model->setHorizontalHeaderItem(1, new QStandardItem(QString("Column2 Header")));
model->setHorizontalHeaderItem(2, new QStandardItem(QString("Column3 Header")));

ui->tableView->setModel(model);

Than you can add values:

QStandardItem *firstRow = new QStandardItem(QString("ColumnValue"));
model->setItem(0,0,firstRow);

Upvotes: 0

Related Questions