PDA

View Full Version : Parsing a CSV File into a QTableView



admkrk
14th May 2017, 03:06
I have this pretty much working and am storing the data in a QList<QString>. When I print it out with qDebug() though, the strings have additional quotes, along with escaped quotes. This makes me think I need to erase the original quotes, when I split the strings?

QTextStream bomIn(&bomFile);
QList<QStringList> bomData;
while(!bomIn.atEnd())
{
QString line = bomIn.readLine();
QStringList rowList = line.split(';');
bomData.append(rowList);
}
bomFile.close();

// for testing
QStringList list;
QString string;
foreach (list, bomData)
{
foreach(string, list)
{
qDebug() << string;
}
}
// end testing
I also need to display the data in a QTableView, and use one of the columns to run queries in my database. Maybe the quote issue will go away in the view?

Subclassing QAbstractItemModel looks like the best way to go, but I am having trouble figuring out a couple things. From the examples I have seen, it looks like I need to pass my QList to the model, in order to implement rowCount(), which should be size() - 1, since the first is the headers, I think. I then need a way to set the header data, somehow, the examples were all hard coded. The CSV files could be coming from different sources, and have different headers so that is not an option. I might be able to think of a way to loop through the first QStringList and do it, but am not sure if that is the way to go? I think I figured out the last question I had, while I was writing this.

jefftee
14th May 2017, 08:53
In my opinion, you should strip the leading and trailing double quotes from your data before storing in your QStringList. Subclassing QAbstractItemModel would allow you to strip the quotes before displaying the data, but why go through that every single time your data needs to be displayed?

Edit: The QDebug output stream will automatically add quotes to string data as you have observed, so that's another reason you should strip the quotes from your data.

d_stranz
14th May 2017, 16:40
It would be easier for you to store your headers and data in separate string lists. This would be easiest to accomplish when you read it in:



QTextStream bomIn(&bomFile);

QList<QStringList> bomData;
QStringList headers;
int lineNo = 0;

while(!bomIn.atEnd())
{
QString line = bomIn.readLine();
QStringList rowList = line.split(';');

if ( lineNo > 0 )
bomData.append(rowList);
else
headers = rowList;
++lineNo;
}
bomFile.close();


If you subclass QAbstractItemModel, then you will reimplement the headerData() method to return the appropriate entry from "headers" and data() to return the appropriate entry from the given row in "bomData". rowCount() is bomData.size().

admkrk
14th May 2017, 23:46
I am not sure why I thought the quotes would not be included in the strings, unless it is because I am just used to thinking they should be there, when I add a string. Once I found QStringList::replaceInStrings(), the problem was solved.

while(!bomIn.atEnd())
{
QString line = bomIn.readLine();
QStringList rowList = line.split(';');
rowList.replaceInStrings("\"", "");
bomData.append(rowList);
}

I originally thought to use separate lists, for the headers and data, but that would have meant adding two arguments to the constructor, instead of just one, plus adding another structure to keep track of. I ended up implementing data() like this:

QVariant BomTableMode::data(const QModelIndex &index, int role) const
{
if(role == Qt::DisplayRole)
{
QStringList row = rowList.at(index.row() + 1);
return row.at(index.column());
}
return QVariant();
}
and headerData() like this:

QVariant BomTableMode::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role != Qt::DisplayRole)
return QVariant();

if(orientation == Qt::Horizontal)
{
QStringList column = rowList.at(0);
return column.at(section);
}

return QVariant();
}
I did similar with rowCount() and columnCount(). I think magic numbers are OK here, since it is easy to understand that the first list is the headers. Except for forgetting to type an "l" at the end of my class name :o, it seems to be working. Thanks, both of you.

I did notice at least one of the fields can end up being too long, so I am going to have to implement word wrapping somehow. I assume that is either the view, or the delegate.