HowTo: QML ComboBox with QAbstractListModel
How to combine a QComboBox with a QAbstractListModel? Difficult, probably not, is it?
In one of the recent last apps, I’ve written, I decided to use QML and PySide instead of QtWidgets as I usually do. Why? Well QML feels more modern to me, right out of the box. Additionally, you gain control over the graphical appearance of the app.
Setting the stage
First, we need a python file for the logic and a QML file to implement the frontend.
In the frontend file, we embed a ComboBox and two Buttons in a Rectangle within a Window. Set the ComboBox model with the name stringModel.
In the python file, we need a QApplication to start the App and a QQmlApplicationEngine to run the frontend. As we want to do the logic in python, we need a QObject to connect both worlds, let’s call it Bridge. This bridge is set as a property in the Context of the QmlApplicationEngine. To make life easier we also set the model, which is an attribute of the bridge as a property to the Qml context.
No deep dive on the Basics here, let’s focus on how to connect the ComboBox with a model.
Using as QStringListModel for lists of strings
During developing my app a came across the issue that I wanted to add and remove elements dynamically to and from the ComboBox. Using a Qt data model is pretty straight forward and it works easily with a QStringListModel, right out of the box.
Expose this model on the bridge as an attribute and you can insert data rows using insertRow and setData. Or remove rows via deleteRow.
Since we’ve set the model as the model parameter in the QML ComboBox, the ComboBox is populated with the strings in the QStringListModel rows.
Using a QAbstractListModel to manage more than strings
Since in most cases, it is not only strings that we need to manage, but rather whole objects, a QAbstractListModel should do the trick. No sooner said than done, didn’t work right out of the box. As I had to add multiple objects coming in form a backend at the same time, I assumed it would be clever to use the ‘insertRows’ methods and afterward set the data via setData. That did set the data, but in the length of the dropdown list in the ComboBox broke.
After some hours of testing and fiddling the solution was to use the insertRow and add one row at a time.
Therefore a minimal model based on the QAbstractListModel, that enables dynamic add and removing data implements the following methods:
- rowCount
- data
- setData
- insertRow
Everything else can be inherited from QAbstractListModel.
Add Data to the Model
However, the data gets into the App — from a DB, from a service, or via inserting by the user. In the example case, it is just a string, but it could be a complete python object.
In the example app on click on “Add One” the function bridge.add_one() is triggered. This function adds on row to the model (insertRow) and sets the data in this row via setData().
In case it is not only a string but a complete object, the complete data can be retrieved from. the model via the data() methods and the Qt.UserRole. Our QListModel keeps track of the data and what to represent.
The complete source code can be found at:
ComboBox with QStringListModel:
https://github.com/JimKnopf2034/PySideQListModels/releases/tag/StringListModel
ComboBox with QAbstractListModel:
https://github.com/JimKnopf2034/PySideQListModels/releases/tag/AbstractListModel
Was that helpful to you? When do you use QML and Python? Do you want to know more about QML and Python?
Please leave a comment or a clap or consider: