Is it possible to involve a search bar on a DataFrame in Pandas? - python-3.x

I have a DataFrame in Pandas which collects some data from an Excel document. I created a GUI with PyQt5 in order to make it look more interesting but here is the thing.
Is it possbile to make a dynamic search bar in order to search through that DataFrame? For example, my DataFrame has over 3k+ rows and I wanna search for John Doe, then the results will come up on the GUI. As far as I know, QLineEdit is used for this but I can't seem to implement it on my code.
Is it me that is doing wrong or it is not possible to do it on a DataFrame? And if anyone wanna help me, just let me know, I would be so grateful and thankful, I guess it'll only take 10-15 minutes. I can also post the code here, but talking on Discord and explaining you in detail and also sharing screens would be a lot easier.

This can be done by subclassing QAbstractTableModel to create a custom table model that uses the underlying dataframe for supplying data to a QTableView. This custom model can then be combined with a QProxyFilterSortModel to filter the data in the table. To create a custom non-editable model from QAbstractTableModel you need to implement rowCount, columnCount, data, and headerData at the very least. In this case, minimal implemetation could be something like this:
class DataFrameModel(QtCore.QAbstractTableModel):
def __init__(self, data_frame, parent = None):
super().__init__(parent)
self.data_frame = data_frame
def rowCount(self, index):
if index.isValid():
return 0
return self.data_frame.shape[0]
def columnCount(self, index):
if index.isValid():
return 0
return self.data_frame.shape[1]
def data(self, index, role):
if not index.isValid() or role != QtCore.Qt.DisplayRole:
return None
return str(self.data_frame.iloc[index.row(), index.column()])
def headerData(self, section, orientation, role=None):
if role != QtCore.Qt.DisplayRole:
return None
if orientation == QtCore.Qt.Vertical:
return self.data_frame.index[section]
else:
return self.data_frame.columns[section]
To show and filter the data in a table you could do something like this:
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent = None):
super().__init__(parent)
self.table_view = QtWidgets.QTableView()
self.proxy_model = QtCore.QSortFilterProxyModel()
# by default, the QSortFilterProxyModel will search for keys in the first column only
# setting QSortFilterProxyModel.filterKeyColumn to -1 will match values in all columns
self.proxy_model.setFilterKeyColumn(-1)
self.table_view.setModel(self.proxy_model)
# line edit for entering (part of) the key that should be searched for
self.line_edit = QtWidgets.QLineEdit()
self.line_edit.textChanged.connect(self.filter_text)
vlayout = QtWidgets.QVBoxLayout(self)
vlayout.addWidget(self.line_edit)
vlayout.addWidget(self.table_view)
def filter_text(self, text):
self.proxy_model.setFilterFixedString(text)
def set_data(self, data):
self.model = DataFrameModel(pd.DataFrame(data))
self.proxy_model.setSourceModel(self.model)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
win = MyWidget()
win.set_data({'a':['apple', 'banana', 'cherry'], 'b':[4,5,6], 'c':['green', 'yellow', 'red']})
win.show()
app.exec()
Of course this is a very basic implementation but I might help you get started.

I think the library pandas_ui is what you are looking for here.
It allows you to play with your dataframe interactively, i.e. in a microsoft excel fashion. Particularly, the column filtering option should do what you want.

Related

PyQt5 - update QAbstractTableModel and retrieve Data

I'm currently building a GUI Application to assist me in searching for Peaks in a Spectrum. I have implemented the Matplotlib in such a matter, that i can click onto the Peaks to load them into a list.
This List I want to display next to the Graph. To do that I try to utilize the Table View Widget of PyQt.
I have no Problem populating the Widget but I would like to automatically update the widget when the dictionary changes and automatically update the dictionary when I edit the Data in the widget.
So far I use this custom Model to populate the Widget and make it editable:
class DictModel(QtCore.QAbstractTableModel):
"""
Custom Table Model to handle RUS Data
"""
def __init__(self, data):
QtCore.QAbstractItemModel.__init__(self)
self.user_data = data
self.columns = list(self.user_data.keys())
def flags(self, index): #, index):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable
def rowCount(self, *args, **kwargs):
return max(len(val) for val in self.user_data.values())
def columnCount(self, *args, **kwargs):
return(len(self.columns))
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.columns[section]
def data(self, index, role):
column = self.columns[index.column()]
#set the value for the row but return None if the Index is out of range
row = self.user_data[column][index.row()] if index.row() < len(self.user_data[column]) else ""
try:
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
return str(row)
except KeyError:
return None
def setData(self, index, value, role=QtCore.Qt.EditRole):
if index.isValid():
selected_column = self.columns[index.column()]
self.user_data[selected_column][index.row()] = value
return True
when I display a little bit of data like so it works fine
self.testData = {
'C1':[1, 2, 3, 4, 5],
'C2':[2, 4, 6, 8, 10],
'C3':[4, 8, 16, 32,64],
}
def testTable(self, data):
self.testModel = DictModel(data)
self.tableViewTest.setModel(testModel)
# I suspect something along the lines of this should update my model? This doesnt change it
def testEditData(self):
self.testModel.layoutAboutToBeChanged.emit()
self.testData['new'] = ['new']
self.testModel.layoutChanged.emit()
I can call the testTable funciton again with a new Dataset but I guess this would not be the Ideal way to go about it, right?
Does anyone of you have a hint on how to sync the dictionary and the View with each other? I have searched for quite some time now and there seems to be no clear example on how to do that. I did not find any well understandable guides to the model.layoutChanged.emit() methods or any Information on how to get the Information back out of the model into a dataframe or dictionary.

PySide2 QListView.setRootIndex with customModel not working as expected

I am pretty new to QT and I am using PySide2 (latest version) with Python 3.9.6.
I want to use a CustomModel via QAbstractItemModel on a QtreeView and at the same time with a QListView.
I have a CustomModel with a two-level hierarchy data.
I want to see the full data in the treeview (working).
At the beginning I show the same model in the QListView. It shows only the top level items.
So far so good.
Now I connected the setRootIndex fn from the QListView to the clicked signal of the QTreeView.
I want to be able to click on a root level item and see only the children in the QListView.
I thought the .setRootIndex should do the trick, but its weirdly offsetting the shown children.
And it's showing only ONE of the children and offsetted by the index count of the first level item.
Please see the gif:
First both views show the same model.
Then I click the first root element in the left treeView.
It updates the right ListView, but only the first children is shown.
And the second item shows its child but the second and with one gap in the listView
Here is a (almost) working example.
I really hope someone can spot the mistake or my misconception of things..
The .setRootIndex on the QListView is confusing me.
I tried approaching it differntly in the .index and .parent and .rowCount functions of the CustomModel. But like this it somehow works at least. I have the feeling I am doing something wrong somewhere or the QListView wants things differntly like the QTreeView.
Is it even possible and a good idea to use the same model in two views?
I really thought so and this is the hole point of a model/viewcontroller approach, isn't it?
# -*- coding: utf-8 -*-
from typing import *
from PySide2 import QtWidgets
from PySide2.QtCore import QAbstractItemModel, QModelIndex
from PySide2.QtGui import Qt
from PySide2.QtWidgets import QListView, QTreeView
class FirstLevelItem:
def __init__(self, name) -> None:
self.name = name
self.children = []
class SecondLevelItem:
def __init__(self, name, parent) -> None:
self.name = name
self.parent = parent
class CustomModel(QAbstractItemModel):
def __init__(self, root_items, parent=None):
super().__init__(parent)
self.root_items = root_items
def rowCount(self, itemIndex):
"""Has to return the number of children of the itemIndex.
If its not a valid index, its a root item, and we return the count of all root_items.
If its a valid one and can have children, return the number of children.
This makes the Model to ask for more indexes for each item.
Only works if parent is set properly"""
if itemIndex.isValid():
item = itemIndex.internalPointer()
if isinstance(item, FirstLevelItem):
return len(item.children)
else:
return 0
else:
return len(self.root_items)
def columnCount(self, parent=None):
return 1
def parent(self, child_index):
"""Has to return an index pointing to the parent of the current index."""
if child_index.isValid():
# get the item of this index
item = child_index.internalPointer()
# check if its one with a parent
if isinstance(item, SecondLevelItem):
# get the parent obj from the item
parent_item = item.parent
# now we have to find the parents row index to be able to create the index pointing to it
parent_row = parent_item.children.index(item)
# create an index with the parent row and column and the parent item itself
return self.createIndex(parent_row, 0, parent_item)
else:
return QModelIndex()
else:
return QModelIndex()
def data(self, index, role):
if not index.isValid():
return None
item = index.internalPointer()
if role == Qt.DisplayRole:
return item.name
return None
def index(self, row, column, parentIndex):
if parentIndex.isValid():
parent_item = parentIndex.internalPointer()
return self.createIndex(row, column, parent_item.children[row])
else:
return self.createIndex(row, column, self.root_items[row])
class ModelTestDialog(QtWidgets.QDialog):
window_instance = None
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowFlags(self.windowFlags() ^ Qt.WindowContextHelpButtonHint)
# self.setMinimumSize(1024, 1024)
self.setWindowTitle("ModelTestDialog")
rootItems = []
for i in range(0, 3):
name = ["FirstLevel_A", "FirstLevel_B", "FirstLevel_C"][i]
rootItem = FirstLevelItem(name)
rootItems.append(rootItem)
for j in range(0, 3):
name = ["SecondLevel_A", "SecondLevel_B", "SecondLevel_C"][j]
childItem = SecondLevelItem(name, rootItem)
rootItem.children.append(childItem)
self.model = CustomModel(rootItems)
self.treeView = QTreeView()
self.treeView.setModel(self.model)
self.listView = QListView()
self.listView.setModel(self.model)
self.main_layout = QtWidgets.QVBoxLayout(self)
self.listViews_layout = QtWidgets.QHBoxLayout()
self.main_layout.addLayout(self.listViews_layout)
self.listViews_layout.addWidget(self.treeView)
self.listViews_layout.addWidget(self.listView)
self.treeView.clicked[QModelIndex].connect(self.listView.setRootIndex)
if __name__ == "__main__":
app = QtWidgets.QApplication()
form = ModelTestDialog()
form.show()
app.exec_()
There is absolutely nothing wrong about using the same model in multiple views.
That is the whole concept behind the model/view paradigm (which relies on the principle of separation of concerns): the same model can be shared amongs multiple views, even if they show the content of that model in different ways.
That is completely respected by Qt (as long as the model is properly implemented, obviously); this also happens for similar concepts in Qt, like the QTextDocument interface used in QTextEdit (the same document can be shown on different QTextEdit instances), or the QGraphicsScene shown in a QGraphicsView (each view can show a different portion of the same scene).
The actual issue
You're using the wrong row for the parent:
parent_row = parent_item.children.index(item)
The above returns the index (row) of the child item, but you need to use createIndex() as a reference for the parent, because parent() has to return the row/column of the parent, not that of the child.
In this simple case, just return the index within the root_items:
parent_row = self.root_items.index(parent_item)
A better approach
I would suggest a more flexible structure, where a single base class is used for all items, and it always has a parent attribute. To do this, you need to also create a "root item" which contains all top level items.
You can still create subclasses for items if you need more flexibility or specialization, but the default behavior remains unchanged, making the implementation simpler especially in the case you need further levels within the structure.
The major benefit of this approach is that you never need to care about the item type to know its level: you know that you need to access the root item when the given index is invalid, and for any other case (like index creation, parent access, etc), the implementation is much more easy and readable. This will automatically make easier to add support for other features, like moving items and drag&drop.
class TreeItem:
parent = None
def __init__(self, name='', parent=None):
self.name = name
self.children = []
if parent:
parent.appendChild(self)
def appendChild(self, item):
self.insertChild(len(self.children), item)
def insertChild(self, index, item):
self.children.insert(index, item)
item.parent = self
def row(self):
if self.parent:
return self.parent.children.index(self)
return -1
class CustomModel(QAbstractItemModel):
def __init__(self, root_items=None, parent=None):
super().__init__(parent)
self.root_item = TreeItem()
if root_items:
for item in root_items:
self.root_item.appendChild(item)
def rowCount(self, itemIndex):
if itemIndex.isValid():
return len(itemIndex.internalPointer().children)
else:
return len(self.root_item.children)
def columnCount(self, parent=None):
return 1
def parent(self, child_index):
if child_index.isValid():
item = child_index.internalPointer()
if item.parent:
return self.createIndex(item.parent.row(), 0, item.parent)
return QModelIndex()
def data(self, index, role):
if not index.isValid():
return None
item = index.internalPointer()
if role == Qt.DisplayRole:
return item.name
def index(self, row, column, parentIndex=QModelIndex()):
if parentIndex.isValid():
parent_item = parentIndex.internalPointer()
return self.createIndex(row, column, parent_item.children[row])
else:
return self.createIndex(row, column, self.root_item.children[row])
class ModelTestDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowFlags(self.windowFlags() ^ Qt.WindowContextHelpButtonHint)
self.setWindowTitle('ModelTestDialog')
rootItems = []
for i in range(0, 3):
name = 'FirstLevel {}'.format('ABC'[i])
rootItem = TreeItem(name)
rootItems.append(rootItem)
for j in range(0, 3):
name = 'SecondLevel {} (child of {})'.format('ABC'[j], 'ABC'[i])
TreeItem(name, rootItem)
# or, alternatively:
# rootItem.appendChild(TreeItem(name))
self.model = CustomModel(rootItems)
self.treeView = QTreeView()
self.treeView.setModel(self.model)
self.listView = QListView()
self.listView.setModel(self.model)
self.main_layout = QVBoxLayout(self)
self.listViews_layout = QHBoxLayout()
self.main_layout.addLayout(self.listViews_layout)
self.listViews_layout.addWidget(self.treeView)
self.listViews_layout.addWidget(self.listView)
self.treeView.clicked.connect(self.listView.setRootIndex)
As you can see, the whole model code is much simpler and cleaner: there is no need to check for item level/type, as the concept of the structure makes that automatically immediate.
Further notes:
the Qt API suggests that the parent argument of index() should be optional; while it's common to use None for that, a default (and invalid) QModelIndex() is preferable, as I did above;
python implicitly returns None if no other return value is given;
in the last few years, Qt has been in the process of removing all overloaded signals, replacing them with more verbose and unique ones; in general, it's unnecessary to specify them, especially where no overload actually exists (self.treeView.clicked);

How to make qtableview display a cell with icon and text

It's OK to just show the image
# L.append([QIcon(r"favicon.ico"), "file", "absolute_path", "modifytime"])
But can't show images and text
# L.append([QTableWidgetItem(QIcon(r"favicon.ico"), "file"), "file", "absolute_path", "modifytime"])
This is my model,I am using qtableview.
I'm using the setmodel method, and I want to add data dynamically.
class FileModel(QAbstractTableModel):
def __init__(self, data, header, *args, **kwargs):
super(FileModel, self).__init__()
self.datalist = data
self.header = header
def rowCount(self, parent=None, *args, **kwargs):
return len(self.datalist)
def columnCount(self, parent=None, *args, **kwargs):
return len(self.header)
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
if role == Qt.DisplayRole or role == Qt.DecorationRole:
return self.datalist[index.row()][index.column()]
else:
return None
def headerData(self, col, orientation, role=None):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.header[col]
return None
def append_data(self, x):
self.datalist.append(x)
self.layoutChanged.emit()
def remove_row(self, row):
self.datalist.pop(row)
self.layoutChanged.emit()
def remove_all(self):
self.datalist.clear()
self.layoutChanged.emit()
help me,can show images and text
The problem is with how you are storing your data and accessing it in your data method. I am having a difficult time trying to describe it so instead I am just going to show examples.
If you want a cell to contain both a icon and text then you need to return the icon when the role is the decoration role and the text when it is the display roll.
For example:
def data(self, index, role=Qt.DisplayRole):
if index.isValid():
row = index.row()
if role == Qt.DisplayRole:
return 'some text here'
elif role == Qt.DecorationRole:
return QIcon('path/to/icon')
return None
Since it appears that you are storing your data in a 2dim list this is a problem because you want every index to store the data for it's own cell.
So one possible solution is to store tuples at each index. So your dataList would have to look like this:
[(text, QIcon()), (text, QIcon), (text,)]
In which case your data method would do:
...
if role == Qt.DecorationRole:
return self.dataList[index.row()][index.column()][1]
elif role == Qt.DisplayRole:
return self.dataList[index.row()][index.column()][0]
...
or you could create your own item class that would work like the QTableWidgetItem.
class TableItem:
def __init__(self, text, icon=None):
self.icon = icon
self.text = text
...
then your data method would look like this:
...
item = self.dataList[index.row()][index.column()]
if role == Qt.DecorationRole:
return item.icon
elif role == Qt.DisplayRole:
return item.text
...
and you would need to construct the dataList like this:
dataList.append([TableItem('text', QIcon('/patth')), TableItem('text'), TableItem('Text')])
Hopefully those examples will help make up for my terrible explanation.
musicmante cleverly suggested using a dictionary where the keys are the different data roles in the comments.
For example:
Your data list rows would look like:
[{Qt.DisplayRole: 'text', Qt.DecorationRole: QIcon('/path')},
{Qt.DisplayRole: 'other text', Qt.DecorationRole: None},
{Qt.DisplayRole: 'more text', 'Qt.DecorationRole': None},....]
and the data method would do something like this.
...
item = self.dataList[index.row()][index.column()]
if role in [Qt.DisplayRole, Qt.DecorationRole]:
return item[role]
...

PyQt - QCombobox in QTableview

I am displaying data from an SQLite database in a QTableView using a QSqlTableModel. Letting the user edit this data works fine. However, for some columns I want to use QComboboxes instead of free text cells, to restrict the list of possible answers.
I have found this SO answer and am trying to implement it on my model/view setting, but I'm running into problems (so this is a follow-up).
Here's a full mini-example:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5 import QtSql
from PyQt5.QtWidgets import (QWidget, QTableView, QApplication, QHBoxLayout,
QItemDelegate, QComboBox)
from PyQt5.QtCore import pyqtSlot
import sys
class ComboDelegate(QItemDelegate):
"""
A delegate that places a fully functioning QComboBox in every
cell of the column to which it's applied
source: https://gist.github.com/Riateche/5984815
"""
def __init__(self, parent, items):
self.items = items
QItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
combo = QComboBox(parent)
li = []
for item in self.items:
li.append(item)
combo.addItems(li)
combo.currentIndexChanged.connect(self.currentIndexChanged)
return combo
def setEditorData(self, editor, index):
editor.blockSignals(True)
# editor.setCurrentIndex(int(index.model().data(index))) #from original code
editor.setCurrentIndex(index.row()) # replacement
editor.blockSignals(False)
def setModelData(self, editor, model, index):
model.setData(index, editor.currentIndex())
#pyqtSlot()
def currentIndexChanged(self):
self.commitData.emit(self.sender())
class Example(QWidget):
def __init__(self):
super().__init__()
self.resize(400, 150)
self.createConnection()
self.fillTable() # comment out to skip re-creating the SQL table
self.createModel()
self.initUI()
def createConnection(self):
self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("test.db")
if not self.db.open():
print("Cannot establish a database connection")
return False
def fillTable(self):
self.db.transaction()
q = QtSql.QSqlQuery()
q.exec_("DROP TABLE IF EXISTS Cars;")
q.exec_("CREATE TABLE Cars (Company TEXT, Model TEXT, Year NUMBER);")
q.exec_("INSERT INTO Cars VALUES ('Honda', 'Civic', 2009);")
q.exec_("INSERT INTO Cars VALUES ('VW', 'Golf', 2013);")
q.exec_("INSERT INTO Cars VALUES ('VW', 'Polo', 1999);")
self.db.commit()
def createModel(self):
self.model = QtSql.QSqlTableModel()
self.model.setTable("Cars")
self.model.select()
def initUI(self):
layout = QHBoxLayout()
self.setLayout(layout)
view = QTableView()
layout.addWidget(view)
view.setModel(self.model)
view.setItemDelegateForColumn(0, ComboDelegate(self, ["VW", "Honda"]))
for row in range(0, self.model.rowCount()):
view.openPersistentEditor(self.model.index(row, 0))
def closeEvent(self, e):
for row in range(self.model.rowCount()):
print("row {}: company = {}".format(row, self.model.data(self.model.index(row, 0))))
if (self.db.open()):
self.db.close()
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
In this case, I want to use a QCombobox on the "Company" column. It should be displayed all the time, so I'm calling openPersistentEditor.
Problem 1: default values I would expect that this shows the non-edited field's content when not edited (i.e. the company as it is listed in the model), but instead it apparently shows the ith element of the combobox's choices.
How can I make each combobox show the model's actual content for this field by default?
Problem 2: editing When you comment out "self.fill_table()" you can check whether the edits arrive in the SQL database. I would expect that choosing any field in the dropdown list would replace the original value. But (a) I have to make every choice twice (the first time, the value displayed in the cell remains the same), and (b) the data appears in the model weirdly (changing the first column to 'VW', 'Honda', 'Honda' results in ('1', 'VW', '1' in the model). I think this is because the code uses editor.currentIndex() in the delegate's setModelData, but I have not found a way to use the editor's content instead. How can I make the code report the user's choices correctly back to the model? (And how do I make this work on first click, instead of needing 2 clicks?)
Any help greatly appreciated. (I have read the documentation on QAbstractItemDelegate, but I don't find it particularly helpful.)
Found the solution with the help of the book Rapid GUI Programming with Python and Qt:
createEditor and setEditorData do not work as I expected (I was misguided because the example code looked like it was using the text content but instead was dealing with index numbers). Instead, they should look like this:
def setEditorData(self, editor, index):
editor.blockSignals(True)
text = index.model().data(index, Qt.DisplayRole)
try:
i = self.items.index(text)
except ValueError:
i = 0
editor.setCurrentIndex(i)
editor.blockSignals(False)
def setModelData(self, editor, model, index):
model.setData(index, editor.currentText())
I hope this helps someone down the line.

PyQt 5 update list model

I am trying to update listview model in QML quick controls with python. Every item in this model contains address and status property. Right now my code is this:
ListModel:
class ServersModel(QAbstractListModel):
def __init__(self, parent=None):
super(ServersModel, self).__init__(parent)
self.list = []
for address, status in (
("server 1", "green"),
("server 2", "green"),
("server 3", "red")):
self.list.append(server(address, status))
def rowCount(self, parent=QModelIndex()):
return len(self.list)
def data(self, index, role=None):
if role == Qt.DisplayRole: #show just the name
person = self.list[index.row()]
return QVariant(person.name)
elif role == Qt.UserRole: #return the whole python object
person = self.list[index.row()]
return person
return QVariant()
def removeRow(self, position):
self.list = self.list[:position] + self.list[position+1:]
self.reset()
ServerObject:
class server(object):
'''
a custom data structure, for example purposes
'''
def __init__(self, address, status):
self.address = address
self.status = status
def __repr__(self):
return "%s\n%s"% (self.address, self.status)
Python connection handler:
class ServersListViewHandler(QObject):
def __init__(self):
QObject.__init__(self)
listLoaded = pyqtSignal(QAbstractListModel, arguments=['model'])
data_changed = pyqtSignal(QModelIndex, QModelIndex)
# Slot for detecting clicks in listView
#pyqtSlot(str)
def listViewItemClicked(self, name):
self.listLoaded.emit(ServersModel())
And this is my connectin at qml:
Connections {
target: serversListViewHandler
onListLoaded: {
serversListView.model = model
}
}
Address property should go to text as value and status is color of circle. This works fine if I implement model directly to QML but I want to update it dynamically and load it form python. Any ideas on how to accomplish this? I am probably doing it totally wrong but there are almost none tutorial or references about pyqt 5
I'm working on a similar experiment at the moment. I succeeded doing this by using dedicated role names for columns (in my case in the list view).
For your example this would kinda look like this:
class ServersModel(QAbstractListModel):
AddressRole = Qt.UserRole + 1
StatusRole = Qt.UserRole + 2
_roles = {AddressRole: b"address", StatusRole: b"status"}
...
def roleNames(self):
return self._roles
def data(self, index, role=None):
if role == AddressRole: #show just the name
person = self.list[index.row()]
return QVariant(person.name)
elif role == StatusRole: #return the whole python object
person = self.list[index.row()]
return person
return QVariant()
Though this may only target the displaying part, not the complete update procedure. Would be interesting to see the QML where you access the model.
Also I'm not really sure if there is some kind of best practice for this kind of thing.

Resources