23. Model-View Programming with QAbstractTableModel
QAbstractTableModel enables you to display and edit tabular data in Qt applications.
List models organize the data as a one-dimenzional sequence of items, where each row represents a single, atomic element. In contrast, table models structure data in a two-dimensional grid, where each row represents a composite record or entity, and the columns within it hold individual fields or attributes.
In the previous chapter we used a simple text file as the data source: each file row was a single element representing a fictional company client. Here, we use a comma-separated (CSV) file where each row has four fields: client id, first name, last name, and profession. We follow the same progression as in the previous chapter:
- read-only model
- editable model
- data mapping
- resizable model
but focusing on columnar aspects.
23.1 Basic Read-Only Table Model
![]() |
You need to show data from a CSV file (data.csv) in a table view. The file contains company clients data, each row containing client id, first name, last name and profession. You decide to implement a |
To create a read-only table model:
Create a subclass of
QAbstractTableModelnamedCsvModeland make external data available to it. We read the data fromdata.csvusing Python’s csv reader and store it in a member variable namedself.csv_data.csv_datais a list and csv reader’s rows are also lists which effectively makescsv_dataa two-dimensional list suitable for use withQAbstractTableModelsubclasses.Implement the
rowCount()and thecolumnCount()methods.rowCount()is the length of theself.csv_datalist and never changes since the model is read-only.columnCount()is hard-coded to return 4, the number of columns in the CSV file.Implement the
data()method.data()expects two arguments:index, aQModelIndexinstance androle, a member of theDisplayRoleenumeration. In the method body we check ifroleis equal toDisplayRoleand if it is we return the data that the index points to. Note that we need to use bothindex.row()andindex.column()as the data has two dimensions. With this you have a functionalQAbstracttableModelsubclass.
- In your main class (
Window), create aCsvModelobject, create aQTableViewobject and set its model usingQTableView()setModel()
QAbstractTableModel subclasses are commonly used with QTableView but not necessarily so. QTableView is able to display table headers using the header data you provide to it in the model headerData() method but implementing headerData() is still not mandatory.
23.2 Making the Table Model Editable
Just as for a list model, to make a table model editable you need to implement its setData() method and modify its flags() method to return the itemIsEditable flag for each item.
![]() |
You need to make your CSV file-backed model, that contains company clients data, editable. |
To make a table model editable:
Create a
QAbstractTableModelsubclass.Implement the
rowCount(),columnCount()anddata()methods. The implementation is the same as in the read-only model example.-
Implement the
setData()method.setData()accepts three arguments:index, aQModelIndexobject that you will use to get the coordinates of the data point to be changed,valuewhich is the new data value androle, one of theQt.ItemDataRoleenumeration members, in most casesEditRole.
In the method body check if
roleis equal toEditRole, check if the new data is actually different from the current data, change the data and emit thedataChanged()signal. Implement the
flags()method. In this method you return the item flags given its index. In the example all items (ie. fields) are flagged as selectable, enabled and editable. This does not have to be the case. For instance, if you had a model based on a SQL table you would flag the primary key fields as selectable only, making only the primary keys read-only.
- Then, in the main class, create a model instance, create a
QTableViewinstance and useQTableView.setModel()to connect the two.
23.3 Using Data Widget Mapper with Table Models
We have already seen how to use a data-widget mapper in the previous chapter where we mapped a single line edit to a list model row. Here, we expand the example to map several widgets to a table model cells.
![]() |
You add a form to the GUI to make editing your CSV file-backed model user-friendly. |
To use a data-widget mapper with a table model:
Create the model class. It is the same editable table model as in the previous section.
Create the widgets. Each column in our tabele model (first name, last name and occupation) contains string data so we create three line edits, one for each column. We also add a Submit button to let the user submit changes to the current table row and add all widgets to a form layout.
Create a data-widget mapper object and add the widgets to it. Create a
QDataWidgetMapperobject and set theCsvModelas its model. Map each line edit to aCsvModelcolumn usingaddMapping()and synchronize the data in the mapper widgets with the table view currently selected row.
Now, when the user changes the data in the data-widget mapper widgets and presses the Submit button, the model is updated and the changes are propagated to the view.
23.4 Resizable Table Model
Just as with a list model, to make a table model resizable you need to implement two methods:
insertRows()removeRows()
![]() |
You need to let the users append, insert, and remove rows from your CSV file-backed model. |
To create a resizable table model:
-
Create the model. Create a subclass of
QAbstractTableModeland store the CSV data in theself.csv_datainstance field. You already implementedrowCount(),columnCount()anddata()for your read-only model; andsetData()andflags()to make it editable. To make the model resizable:- implement
insertRows(): check ifrowis within acceptable range, and insert and empty row inself.csv_data, guarding the insertion withbeginInsertRows()andendInsertRows()calls. Return true on success, and false otherwise. - implement
removeRows(). The lineself.csv_data[row:row + 1] = []removes the single element at index row from the list by assigning an empty list to that one-element slice, also guarder withbeginRemoveRows()andendRemoveRows(). Return true on success, and false otherwise.
Then, in the main window:
Create the model and view objects. Create a
CsvModelobject, initializing it with the file that contains the client data; Create aQTableViewobject and set the CSV model as its model.-
Add the Insert, Append, and Remove buttons:
- When the Insert button is pressed, get the view current index row and call
insertRows()with it to insert and empty row above the current row. - When the Append button is pressed, get the next available row number and call
insertRows()with it to append an empty row to the model. - When the Remove row is pressed, get the current view index and and call
removeRows()with its row to remove the current row from the model.
Note that both in
insertRows()andremoveRows()we cheat a bit and assume that only one row can be inserted or removed at a time. To allow for multiple rows insertion and deletion, you need to adjust the range passed tobeginInsertRows()andbeginRemoveRows()and update the insert and remove logic.Also note that
insertRows()does not let you pass initial data to it and that we just insert empty strings as the data. If you need to insert some initial data, usesetData()after the insertion completes or extend the model with a custom method (e.g.insertRowsWithData()) that accepts data parameters and calls the standardinsertRows()internally.Lastly, the documentation mentions that you can provide your own API for altering or removing the data as long as you call
beginInsertRow()orbeginRemoveRows()to notify other components that the model has changed.
