22. Model-View Programming with QAbstractListModel
This chapter introduces custom Qt models using QAbstractListModel through a series of simple demonstrations manipulating a list of company clients.
The Qt model-view architecture is Qt’s variant of the model-view-controller (MVC) design pattern where, per the documentation, the controller and view are combined. The model and view are decoupled, allowing the same model to be used with multiple views.
Qt provides several abstract classes you can subclass for custom models, such as:
QAbstractItemModelQAbstractTableModelQAbstractListModel
Qt also offers ready-to-use concrete models, including:
QStandardItemModelQFileSystemModelQSqlQueryModel
Earlier examples demonstrated some of these with Qt view classes. Here, we focus on creating custom models.
22.1 Read-only List Model
[TODO: QAbstractListModel inheritance tree]
Of the three abstract model classes above, QAbstractListModel is the easiest to subclass. It provides default implementations for several QAbstractItemModel methods and offers a specialized interface for simple, non-hierarchical sequences of items (lists).
Like other model classes, a QAbstractListModel subclass acts as an intermediary between a data source and a view:
[TODO: DATA-MODEL-VIEW DIAGRAM]
A data source can range from a simple Python list or structured text file, relational database data, and anything in between. The model’s role is to supply data in a format that a Qt view can read and display.
To create a read-only list model you must implement at least two methods:
rowCount(): Returns the number of rows in the model. (e.g.,len(lst)for a Python list, line count for a file, orcount(*)for a SQL query). Theparentparameter is for hierarchical model and unused here.data(index, role): Returns the data for the given role and item referenced by the index. For list models,indexis aQModelIndexobject withcolumn()as zero androw()indicating the position in the underlying data. Theroleis one of theQt.ItemDataRoleenumeration values(default:DisplayRole).
![]() |
You have a text file listing company clients (‘data.txt’) and you need to display them in a list view. |
To achieve this:
Subclass
QAbstractListModeland provide access to the data source. Here, read the text file entirely in__init__()and store lines in a Python list (txt_data), where each element is a single string (a client’s name and profession). For other types of data sources you can retrieve data dynamically instead.Implement
rowCount(). View classes call this method to determine the model’s length. Here, returnlen(self.txt_data). Qt list models default to one column.Implement
data(). This returns model data for a given index and role. Useindex.row()to accesstxt_data. Return data only forDisplayRole(the text for display); returnNoneotherwise.Optionally, implement
headerData()for row or column headers. Here, the single column header is ‘Clients’.QListViewdoes not display headers butQTableViewwould.

QModelIndex:This class helps views locate model items. Qt models are table-based - think of the model in this example as a one-column table with
nrows. Userow()andcolumn()to reference cells.
QAbstractItemModelTesterThis class aids model development. Pass your model instance to its constructor - it logs implementation errors to the console, helping catch issues early.
This demo is read-only; next sections cover editable and resizable models.
22.2 Editable List Model
As shown in the read-only list model example, creating a basic list model requires implementing at least rowCount() and data(). To make it editable, add two more methods:
setData(index, value, role): Sets the data for the given role (typicallyEditRole) and index, and returnsTrueif successful; otherwise returnsFalse. If set successfully, emit thedataChanged()signal.flags(index): Returns the item flags for the index. The base implementation enables and selects items. To allow editing, addQt.ItemFlags.ItemIsEditable.
![]() |
You have implemented a read-only model of a company clients backed by a text file (‘data.txt’) and you need to make it editable. |
To create an editable list model:
Create a
QAbstractListModelsubclass and the access to the data source.Implement the
rowCount()anddata()methods just as you did for the read-only model.Implement the
setData()method.setData()accepts three arguments:index,valueandrole. In the method we check if the role is equal toQt.ItemDataRole.EditRoleand, if it is, we set the model data for theindex.row()tovalueusingself.txt_data[index.row()] = value. If the data is set successfully we emit thedataChangedsignal and the method returnsTrue. Otherwise it returnsFalse.Implement the
flags()method. In this method we signal to views that the model data is editable by addingQt.ItemFlags.ItemIsEditableto the flags. Note that we can make the model items editable selectively by using theindexparameter. In the example we ignoreindexwhich means that all the model items are editable.

Now if you double-click any of the list view lines you are able to edit it and the changes are saved in the model (ie. to the TxtFileModel.txt_data list and signaled by the dataChanged signal. You can also implement the logic to update the text file from the txt_data values which we omit in this example.
22.3 Editable List Model with Data-Widget Mapping
The QDataWidgetMapper class lets you map a data model row (or column) to a set of widgets, making them data-aware. When the model’s current index changes, mapped widgets are updated with data from the model. This is useful for creating forms enhancing user experience in viewing and editing data.
![]() |
You have an editable model of a list of company clients. Editing is enabled by double-clicking a client row in the list view. Your task is to make editing more user-friendly. |
- Create a
QAbstractListModelsubclass to represent your model. Then, in the main window class:
Create your model object and the widgets for displaying and editing your model data.
Create the mapper object and use
QDataWidgetMapper.setModel()to connect your model to it.Use
QDataWidgetMapper.addMapping()to map your model columns with the widgets. The example model has only one column, which we map to aQLineEditwidget.Synchronize the view’s current item with the model’s current index so both update when the user changes the view’s selection.

In the example, we use manual submit policy, updating the model via a ’Submit` button. This syncs the line edit with the current view item, allowing easy updates by editing the line edit value and clicking ‘Submit’.
22.4 Resizable List Model
For a basic QAbstractListModel subclass, you need to implement at least two methods: rowCount() and data(). To make the model editable, you need to implement two more: setData() and flags(). To be able to add or remove rows, you need to implement insertRows() and removeRows().
![]() |
You have an editable model of a list of company clients. You need to enable the user to insert or remove clients from it. |
To make a resizable QAbstractListModel subclass:
Create a subclass of the
QAbstractListModelclass. As in previous examples, read the data from a text file and store it in a Python list namedself.txt_data.Implement the
insertRows()method. For simplicity, the example inserts rows filled with template text (“<insert row data>”). Guard the data insertion withbeginInsertRows()(to signal connected views that rows are about to be inserted) andendInsertRows(). This pair of methods ensures that views remain in a valid state.beginInsertRows()takes three arguments:parent(an invalidQModelIndex()in our case),first(the starting row number post-insertion) andlast(the ending row number post-insertion). The method returnsTrueon success orFalseotherwise.Implement the
removeRows()method. This removes rows from theself.txt_data, enclosed bybeginRemoveRows()andendRemoveRows(), and returnsTrueon success orFalseotherwise.
Then, in your main class
- Create three
QPushButtons:insert_button,append_buttonandremove_button. Handle the insert button’sclicked()signal with a slot namedon_insert()callinginsertRow()to insert a single row by invokinginsertRows(). Similarly, handle the append button’sclicked()signal withon_append()to insert a row at the end.

