QTableWidget for List of Dict

Heads up! You've already completed this tutorial.

zadstofun | 2021-03-07 06:57:44 UTC | #1

I'm new and I need some assistance and pointers. I have gone through the turorials on QTableWidget however my data set is not List of List but List of Dicts.

My major headache is how to return values for the subclass of QAbstractTableModel Data Method.

My idea to loop through the dataset to generate the header as an ordered set of the dataset keys().

I will then can enumerate the header set to create a dict index of the headers.

Such that when I want to return values of the I cal do something like

return self._data[index.row()][header_dict[index.column()]]

My Question:

Is there a shorter way other than having to using pandas or numpy


martin | 2021-03-09 09:27:26 UTC | #2

Hi @zadstofun welcome to the forum!

You don't need to use pandas or numpy to do this, but you do need some way to map your dictionary keys to numeric values -- Qt's models work on rows and columns.

What I would do is define a list of header values and/or dictionary keys. Then you can use the column index to get your key, and use this to get the data from the dictionary. For example --

python
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt

class DictionaryTableModel(QtCore.QAbstractTableModel):
    def __init__(self, data, headers):
        super(DictionaryTableModel, self).__init__()
        self._data = data
        self._headers = headers

    def data(self, index, role):
        if role == Qt.DisplayRole:
            # Look up the key by header index.
            column = index.column()
            column_key = self._headers[column]
            return self._data[index.row()][column_key]

    def rowCount(self, index):
        # The length of the outer list.
        return len(self._data)

    def columnCount(self, index):
        # The length of our headers.
        return len(self._headers)

    def headerData(self, section, orientation, role):
        # section is the index of the column/row.
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return str(self._headers[section])

            if orientation == Qt.Vertical:
                return str(section)

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.table = QtWidgets.QTableView()

        data = [
          {'a':4, 'b':9, 'c':2},
          {'a':1, 'b':0, 'c':0},
          {'a':3, 'b':5, 'c':0},
          {'a':3, 'b':3, 'c':2},
          {'a':7, 'b':8, 'c':9},
        ]

        headers = ['a', 'b', 'c']

        self.model = DictionaryTableModel(data, headers)
        self.table.setModel(self.model)

        self.setCentralWidget(self.table)


app=QtWidgets.QApplication(sys.argv)
window=MainWindow()
window.show()
app.exec_()

If you want to show different headers to your keys you can - just create a separate list (e.g. _keys) and perform the data lookup on that instead.


zadstofun | 2021-03-11 06:20:59 UTC | #3

Hello Martin,

Thanks so much for taking timeout to respond.

I just did a little modification with permits for headers to be generated on the fly just as an added information to someone else that might need the code nothing much takes nothing away from your code.

Also for the value to return in the data I put a try except just in case the key does not exist in the row.

A dict.get could be used instead of the try except depending on preference.

from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt

class DictionaryTableModel(Qtore.QAbstractTableModel):

python
def __init__(self, data: Union[list, dict] = None):
    super(DictionaryTableModel, self).__init__()
    self.data = data or []
    self._hdr = self.gen_hdr_data() if data else []
    self._base_color = {'NewConnection': 'blue', }

def gen_hdr_data(self):
    self._hdr = sorted(list(set().union(*(d.keys() for d in self.data))))
    return self._hdr


def data(self, index: QModelIndex, role: int):
    if role == Qt.DisplayRole:
        try:
            value = self.data[index.row()][self._hdr[index.column()]]
        except KeyError:
            value = None
        return str(value) if value else ""


    if role == Qt.BackgroundRole and self._base_color.get(
            self.data[index.row()]['PRESENT_STATUS'], False):
        # noinspection PyTypeChecker
        _state = self._base_color[self.data[index.row()]['PRESENT_STATUS']]
        return QColor(self._base_color[_state])

I have a new question but then I will use another Q&A as it has to do with Qt.BackgroudRole I will open another Q&A for that


The complete guide to packaging Python GUI applications with PyInstaller.
[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]
Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

QTableWidget for List of Dict was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.