PDA

View Full Version : How to populate a QTableView using data in a .txt file



newtoQ_s
22nd September 2015, 02:52
I am new to Qt, I am trying to populate a QTableView using data entered into a .txt file. It would be great if I can see an example of the same.
The viewer should basically be able to see all the data in the file in columns and rows in the table accordingly.
Ex- Col1, Col2 with 5 rows of data corresponding to each of the columns.

jefftee
22nd September 2015, 03:11
Read the data from your file line by line, parse into the individual columns, then you will use that data to populate your model. You can use QStandardItemModel or create your own subclass of QAbstractTableModel.

newtoQ_s
22nd September 2015, 08:25
Thanks, but I am having trouble understanding how to use QstandardItem model, because I am parsing the lines in the file, but unable to decide how best to use QStandardItem model. Forgive me if it is basic, but I am new to Qt
while(!file.atEnd()){
line=file.readLine();
line_data=line.split(";", QString::SkipEmptyParts);
QStandardItem item;
}

anda_skoa
22nd September 2015, 08:55
For each item in line_data you create a QStandardItem on the heap (using "new") and put it into a QList<QStandardItem*>.
Then you append this list to the QStandardItemModel using appendRow().

Cheers,
_

newtoQ_s
22nd September 2015, 09:20
Thanks,
But while trying to add the QstandardItemModel to the QTreeWidget
I am getting an error, because the setModel() function is private.
QStandardItemModel model(10,3);
while(!file.atEnd()){
line=file.readLine();
line_data=line.split(";", QString::SkipEmptyParts);
for(int j=0;j<line_data.size();j++){
QString m_prop=line_data.at(j);
QStandardItem *item=new QStandardItem(m_prop);
model.setItem(line_count,j,item);
}
line_count++;
}
}
ui->tableWidget->setModel(model);

anda_skoa
22nd September 2015, 09:32
Thanks,
But while trying to add the QstandardItemModel to the QTreeWidget
I am getting an error, because the setModel() function is private.

No, it is not.





QStandardItemModel model(10,3);


That stack allocated instance will go out of scope as soon as the current method ends.





ui->tableWidget->setModel(model);


setModel() takes a pointer to a model, not a copy of a model.

Cheers,
_

newtoQ_s
22nd September 2015, 09:39
Thanks, but I am unable to understand as to how I can correct this error.

QStandardItemModel model(10,3);

int line_count=0;
if(file.open(QIODevice::ReadOnly)){
QString line;
QStringList line_data;

while(!file.atEnd()){
line=file.readLine();
line_data=line.split(";", QString::SkipEmptyParts);
for(int j=0;j<line_data.size();j++){
QString m_prop=line_data.at(j);
QStandardItem *item=new QStandardItem(m_prop);
model.setItem(line_count,j,item);
}
line_count++;
}
}
ui->tableWidget->setModel(&model);

I am sure it must be something I am overlooking. Its not in the main() method, I am using a separate method.

anda_skoa
22nd September 2015, 10:08
Thanks, but I am unable to understand as to how I can correct this error.

You allocate the model on the heap instead.

A stack allocated object's live time ends at the end of the block scope it was created in.

Cheers,
_

newtoQ_s
22nd September 2015, 12:29
Hi
That's true, but when I tried doing that I am getting an error.

Cannot convert from QStandardItemModel ** to QAbstractItemModel*.
Change:
QStandardItemModel model=new QStandardItemModel(10,3, this);

Thanks

Added after 4 minutes:

Correction
QStandardItemModel *model=new QStandardItemModel(10,3,this);

If I do the following; The exe stops working giving an error

if(file.open(QIODevice::ReadOnly)){
QString line;
QStringList line_data;

while(!file.atEnd()){
line=file.readLine();
line_data=line.split(";", QString::SkipEmptyParts);
for(int j=0;j<line_data.size();j++){
QString m_prop=line_data.at(j);
QTableWidgetItem *item=new QTableWidgetItem(m_prop);
ui->tableWidget->setItem(line_count,j,item);
}
line_count++;

}
}

Added after 1 14 minutes:

why do I need to create 3 different data structures just to simply acces strings from a file, parse them and display them onto a Table?

I am unfortunately still unable to find a solution to this.

anda_skoa
22nd September 2015, 12:57
If I do the following; The exe stops working giving an error


if(file.open(QIODevice::ReadOnly)){
QString line;
QStringList line_data;

while(!file.atEnd()){
line=file.readLine();
line_data=line.split(";", QString::SkipEmptyParts);
for(int j=0;j<line_data.size();j++){
QString m_prop=line_data.at(j);
QTableWidgetItem *item=new QTableWidgetItem(m_prop);
ui->tableWidget->setItem(line_count,j,item);
}
line_count++;

}
}


What kind of error?
Does it crash? When yes, where?
Is line_count initialized properly?

Otherwise this looks quite ok.

Cheers,
_

newtoQ_s
22nd September 2015, 13:41
The following is the code that I have used in the function, I have also seen the Qt documentation and numerous examples and have tried them, but none to avail. I am in a bit of a fix because of this.

void MainWindow::init()
{

QFile file("address1.txt");
if(!file.exists()){
return;
}
int line_count=0;
QString line;
QStringList line_data;

if(file.open(QIODevice::ReadOnly)){
while(!file.atEnd()){
line=file.readLine();
line_data=line.split(";", QString::SkipEmptyParts);
for(int j=0;j<line_data.size();j++){
QString m_prop=line_data.at(j);
QTableWidgetItem *item=new QTableWidgetItem(m_prop);
ui->tableWidget->setItem(line_count,j,item);
}
line_count++;
}
}
qDebug()<<line_count;
}

The Message I get is Applicationname.exe has stopped working, would you like to debug, restart or quit.

anda_skoa
22nd September 2015, 14:10
The Message I get is Applicationname.exe has stopped working, would you like to debug, restart or quit.
So you have debugged it and found the line where it crashes?

Cheers,
_

newtoQ_s
22nd September 2015, 14:19
Unfortunately the debug doesn't work in the Qt creator and even if it did, I am yet to learn how to do it.
But is the code fine?

anda_skoa
22nd September 2015, 15:32
Yes, looks ok.
You have verified that the "ui" pointer has been initialized properly?

Cheers,
_

d_stranz
22nd September 2015, 20:22
ui->tableWidget->setItem(line_count,j,item);

If you have not initialized the table widget to the total number of rows and columns before you start adding items to it, it will blow up. Because your code mixes up reading the file with adding to the table, you have no way to determine in advance what the total row count will be.

If you had followed the advice given earlier and created a QStandardItemModel instance on the heap (using "new"), then you could read each row, create a QList< QStandardItem * > for the columns in that row (as also advised), and call QStandardItemModel::appendRow() to dynamically build your model as you read the file.

After you have read the file, you can then use a QTableView (not Widget) and call its setModel() method to tell it to use your model as its data source.

If you want to do it the way you are trying to, you will have to read the file once to count the number of rows and columns, set the table widget dimensions, then rewind and read the file again, this time creating the QTableWidgetItem instances to fill the cells.

newtoQ_s
23rd September 2015, 02:46
Hi,

I have set the number of rows and columns for the tableWidget in the Qt designer itself. I am able to only see empty rows and columns with the column titles and row numbers without the data that I actually would like to see in the table.
Could I use table view to set the model to Table Widget?
If I morph the table widget to TableView, the designer doesn't have the option of defining the number of rows and columns, how do I do that? In the program?

In the method that I am using, I have read the file only once, I am simultaneously trying to populate the table with each line that is read by parsing the values read to each of the columns. The file already consists of the data and doesn't exceed the table widget's capacity.

Hi,

I have not done anything to the ui pointer because I believe that the Qt creator takes care of the initialization part. I simply use the pointer to access different elements on the screen.

d_stranz
23rd September 2015, 04:07
If I morph the table widget to TableView, the designer doesn't have the option of defining the number of rows and columns, how do I do that? In the program?

The table view uses the model to determine how many rows and columns there are. There are no setRowCount() or setColumnCount() methods for QTableView for that reason. When the model tells the table view it needs to update (because the model has changed), the table view asks the model for the number of rows and columns and uses that to configure itself.

You want to do something like this:



// Not real C++ code...
void MainWindow::init()
{
// open the file, etc.

// create the model
QStandardItemModel * pModel = new QStandardItemModel( this );

for each line in the file
{
QList< QStandardItem * > items;
parse the line into fields
for each field
{
QStandardItem * pItem = new QStandardItem( field );
items.push_back( pItem );
}
pModel->appendRow( items );
}

// close the file, etc.

// set the model on the table VIEW
ui->tableView->setMode( pModel );
}

newtoQ_s
23rd September 2015, 06:34
Thanks,
I shall try the above, as I am new, I am not sure about converting the TableWidget to TableView.

Added after 1 12 minutes:

Thanks a lot,
Your suggestion has helped me finally see the output on the table, the application not running was because I was calling it before the ui could be setup.
Just wondering as to why one needs to create 2 data structures i..e QList and QStandardItemModel in addition to the QStandardItem in order to populate the screen with String values?

anda_skoa
23rd September 2015, 09:41
Your suggestion has helped me finally see the output on the table, the application not running was because I was calling it before the ui could be setup.

:)



Just wondering as to why one needs to create 2 data structures i..e QList and QStandardItemModel in addition to the QStandardItem in order to populate the screen with String values?

You are effectively using one data structure, the QStandardItemModel.
It somehow needs to be told what data is should provide to the view, so you need API to put data into the model.

Since a node can hold various values (text, font, color, etc), each node is represented by an object, in this case an object of type QStandardItemModel.
So this is not something extra, it is part of the QStandardItemModel API, just making it more convenient to use than a bunch of setters and getters that always need to location of the cell as well as the data.

For simple views this is easier with the item widgets, say QTableWidget, since their cell value objects to not need to cover the whole range of options (e.g. QTableWidget only deals with tables, QStandardItemModel needs to be capable for holding tree data).

Ultimately writing your own model is often the least complex solution when dealing with lists or tables (deriving from QAbstractListModel or QAbstractTableModel respectively).

Cheers,
_

newtoQ_s
23rd September 2015, 14:57
Hi,

Thanks for the explanation. I am not sure I understood the entire reasoning. Another question that came to mind was:-
How do I understand which of the variables n the stack I need to explicitly delete using delete and which of them I need not delete.
In C++ the general rule to to make sure that a variable allocated in the heap is deleted and set to Null to avoid dangling pointers and memory loss.
What is the general thumb rule in Qt? Because I am not able to get my head around it, and its imperative that I understand it.

d_stranz
23rd September 2015, 15:39
What is the general thumb rule in Qt?

This is indeed one of the more confusing parts of Qt for someone coming from a traditional C++ background.

The rule of thumb is that "Anything created (using operator new() with a non-NULL parent object (QObject *, QWidget *) will be deleted when the parent is deleted." What this means in practice is:



If you create QMainWindow on the stack (in main()), all of its children will be deleted when the program exits (so you don't need to worry about deleting any child window you create with your QMainWindow as its ancestor).
Any time you add a child to another QObject, that transfers ownership and the child will be deleted when the QObject is
This rule holds for both QWidget and non-QWidget (eg. QObject) hierarchies
QObjects created on the stack are deleted (along with their children) when that part of the stack goes out of scope.


It is because of these parent-child ownership relationships that copy constructors and assignment operators are disabled for QObject-derived classes.

These same rules hold for objects in QGraphicsItem hierarchy, even though they are not QObject-based.

For QStandardItem, QTableWidgetItem, and similar classes that are not QObject-based, when you give them to an instance that *is* QObject-based, you transfer the ownership (and lifetime control) to that instance. This is why calling QStandardItemModel::appendRow() doesn't cause a memory leak.

Object instances that are designed to be shared (like QAbstractItemModel instances, for which a single instance can be shared by many views), these are usually QObject-based, and are constructed with a QObject instance as their parent. So in the code I posted earlier, the QStandardItemModel instance was created with the QMainWindow as its parent, and will be deleted along with the parent.

For GUI classes with a .ui file created in Designer, there are at least three conventions: 1) include a "ui" class pointer as a member variable, 2) multiply inherit the GUI class from both a QWidget parent and the UI class, or 3) include a ui class member variable (not a pointer to one).

In the first case, it is customary to initialize the ui pointer by calling operator new() and then ui->setupUi() in the GUI class constructor. The ui class instance has no "parent", and so it must be explicitly deleted in the GUI class destructor.

In the second and third cases, there is no operator new() involved, and the ui is initialized by calling either setupUi() (case 2) or ui.setupUi() (case 3).

anda_skoa
23rd September 2015, 16:33
Since Qt is C++, the general rules can be used.

However, any QObject derived class that has a parent, will be deleted by its parent if it is not deleted explicitly.

I.e. QObject based objects can have their life time tied to the live time of another QObject based object.

Cheers,
_

newtoQ_s
24th September 2015, 08:00
Thanks a lot for the detailed response. I am trying my best to understand Qt and to be able to update my knowledge on C++ as well.