22. Model-View Programming with QAbstractListModel
This chapter introduces custom Qt models using QAbstractListModel through a series of simple demonstrations manipulating a bank’s client roster.
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
While QStandardItemModel requires you to copy your data into QStandardItem objects one by one, a custom QAbstractListModel subclass takes a different approach. It wraps data that already exists somewhere else and exposes it to Qt’s views without duplicating it into a parallel set of item objects. This distinction matters if your data set is large or already has its own representation you don’t want to mirror in the model.
22.1 Read-only List Model
Of the three abstract model classes above, QAbstractListModel is the easiest to subclass. It provides default implementations for several QAbstractItemModel methods (most notably columnCount(), which always returns 1), 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:
A data source can range from a simple Python list or structured text file to relational database data. 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 models 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 your bank’s client roster (‘data.txt’) and you need to display it in a list view. |
To use a QAbstractListModel subclass in your application:

Subclass
QAbstractListModeland provide access to the data source. In the example we read the text file entirely in__init__()and store its lines in a Python list (self.txt_data), where each element is a single string (a client’s name and account type). 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 we return the data list length withlen(self.txt_data).-
Implement
data(). This method returns model data for a given index and data role. Useindex.row()to accesstxt_data. Return data only forDisplayRole(the text for display) and returnNoneotherwise.A view will always ask for data associated with a specific data role. Qt has several built-in roles and you can define your own:
Constant Value Description DisplayRole0 The key data to be rendered in the form of text. ( QString)DecorationRole1 The data to be rendered as a decoration in the form of an icon. ( QColor,QIconorQPixmap)EditRole2 The data in a form suitable for editing in an editor. ( QString)ToolTipRole3 The data displayed in the item’s tooltip. ( QString)StatusTipRole4 The data displayed in the status bar. ( QString)WhatsThisRole5 The data displayed for the item in “What’s This?” mode. ( QString)FontRole6 The font used for items rendered with the default delegate. ( QFont)TextAlignmentRole7 The alignment of the text for items rendered with the default delegate. ( Qt::Alignment)BackgroundRole8 The background brush used for items rendered with the default delegate. ( QBrush)ForegroundRole9 The foreground brush (text color, typically) used for items rendered with the default delegate. ( QBrush)CheckStateRole10 This role is used to obtain the checked state of an item. ( CheckState)AccessibleTextRole11 The text to be used by accessibility extensions and plugins, such as screen readers. ( QString)AccessibleDescriptionRole12 A description of the item for accessibility purposes. ( QString)SizeHintRole13 The size hint for the item that will be supplied to views. ( QSize)InitialSortOrderRole14 This role is used to obtain the initial sort order of a header view section. ( Qt::SortOrder)UserRole0x0100 The first role that can be used for application-specific purposes. It might be tempting to skip the role check and simply return the
txt_dataelement for all roles, but this will lead to invalid/zero-sized items.SizeHintRoleexpects aQSizevalue. Sending a text value to the view for this role will break the view’s display. Optionally, implement
headerData()for row or column headers. Here, the single column header is ‘Clients’.QListViewdoes not display headers butQTableViewwould.
The QModelIndex class helps views locate model items. All Qt models are table-based: you can think of the model in this example as a one-column table with n rows. You can use row() and column() to reference this table’s cells, and in this chapter column() will always be zero, since a list model has only one column.

In the main window code we import TxtFileModel and create an instance of it, create a QListView() instance and assign the model to it.

QAbstractItemModelTester aids model development. If you 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
Creating a basic list model requires implementing at least rowCount() and data(). To make it editable, you need to implement two more:
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 your bank’s client roster, backed by a text file (‘data.txt’) and you need to make it editable. |
To create an editable list model:

Create a
QAbstractListModelsubclass and provide access to the data source.Implement the
rowCount()anddata()methods 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. Take care that all code paths in this method return a boolean value.-
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.The
Qt.ItemFlagsenum has these values:Constant Value Description Qt::NoItemFlags0 It does not have any properties set. Qt::ItemIsSelectable1 It can be selected. Qt::ItemIsEditable2 It can be edited. Qt::ItemIsDragEnabled4 It can be dragged. Qt::ItemIsDropEnabled8 It can be used as a drop target. Qt::ItemIsUserCheckable16 It can be checked or unchecked by the user. Qt::ItemIsEnabled32 The user can interact with the item. Qt::ItemIsAutoTristate64 The item’s state depends on the state of its children. Qt::ItemNeverHasChildren128 The item never has child items. Qt::ItemIsUserTristate256 The user can cycle through three separate states. Note that in our
flags()implementation we returnNoItemFlagsfor an invalid index (return Qt.ItemFlags()). If we didn’t, the model tester would print a warning:qt.modeltest: FAIL! flags == Qt::ItemIsDropEnabled || flags == 0 () returned FALSE (C:\Users\qt\work\qt\qtbase\src\testlib\qabstractitemmodeltester.cpp:377)
The model tester calls
flags()with an invalid index and expects the result to be either0/NoItemFlagsor exactly the drop-enabled flag. If you always addItemIsEditable(even for that invalid index) the test fails.An invalid index does not point to a real item, so it can not be edited. The tester is simply checking that the model does not claim that something that does not exist can be edited.

The main window code is the same as in the previous section, except that we add a method to handle the model’s dataChanged signals to show that they are indeed emitted in setData().

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 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 mapper’s current index changes, mapped widgets are automatically updated with data from the model. This is useful for creating forms that enhance user experience when viewing and editing data.
![]() |
You have an editable model of your bank’s client roster. Editing is enabled by double-clicking a client row in the list view, but your staff wants to edit a client’s details through a dedicated form field instead of double-clicking into the list. |

- 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’s columns with the widgets. The example model has only one column, which we map to aQLineEditwidget.Synchronize the view’s current item with the mapper’s current index so both update when the user changes the view’s selection. Connect the view’s selection model
currentChangedsignal directly to the mapper’ssetCurrentModelIndex()slot.

The mapper updates its widgets automatically whenever its own current index changes. It does not watch the view’s selection, so you must tell it which row to display.
In the example, we use manual submit policy, updating the model via a ‘Submit’ button. This keeps the line edit in sync 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 also need to implement insertRows() and removeRows().
![]() |
You have an editable model of your bank’s client roster. You need to let staff add new clients to the roster or remove existing ones. |
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).insertRows()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.

Throughout this chapter, TxtFileModel went from a two-method read-only stub to a model that a user can edit through the view or a mapped form field, and resize with insert and remove buttons, all without ever reimplementing the parent() method. Every item in a QAbstractListModel is conceptually a child of the model’s invisible root index (an invalid QModelIndex()):
