'Speed differences between QStandardItemModel and QAbstractTableModel?

Can anyone explain the following: I have 2 scripts for loading a pandas dataframe in a tableview which has a filter field. The one with the standard model loads the data in the "init" section. With this one everyting is blazing fast , also the filtering. The second script works much slower but with this one i can set the background color of the cells which i need. These are the scripts:

import timeit

import pandas as pd
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtCore import QAbstractTableModel
from PyQt5.QtCore import Qt, QSortFilterProxyModel
from PyQt5.QtGui import *
from PyQt5.uic import loadUi


class PandasTableModel(QtGui.QStandardItemModel):

    def __init__(self, data, parent=None):

        QtGui.QStandardItemModel.__init__(self, parent)
        self._data = data

        for col in data.columns:
            data_col = [QtGui.QStandardItem("{}".format(x)) for x in data[col].values]
            self.appendColumn(data_col)
        return

    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def headerData(self, x, orientation, role):

        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._data.columns[x]
        if orientation == Qt.Vertical and role == Qt.DisplayRole:
            return self._data.index[x]

    def flags(self, index):
        if not index.isValid():
            return Qt.ItemIsEnabled

        return super().flags(index) | Qt.ItemIsEditable  # add editable flag.

    def setData(self, index, value, role):

        if role == Qt.EditRole:
            # Set the value into the frame.
            self._data.iloc[index.row(), index.column()] = value
            return True

        return False

class TableViewer(QtWidgets.QMainWindow):

    def __init__(self):
        super(TableViewer, self).__init__()

        self.ui = loadUi("QTableViewForm.ui", self)
        self.ui.cmdRun1.clicked.connect(self.RunFunction1)
        self.ui.cmdRun2.clicked.connect(self.RunFunction2)
        self.ui.inputFilter.textChanged.connect(self.SetFilteredView)

        self.showdata()

    def showdata(self):
        start = timeit.default_timer()
        print("Start LoadData")

        data = pd.read_pickle("productdata.pkl")

        self.model = PandasTableModel(data)
        self.ui.tableData.setModel(self.model)
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(-1)  # Search all columns.
        self.proxy_model.setSourceModel(self.model)
        self.proxy_model.sort(0, Qt.AscendingOrder)
        self.proxy_model.setFilterCaseSensitivity(False)
        self.ui.tableData.setModel(self.proxy_model)

        print("Stop LoadData")
        end = timeit.default_timer()
        print("Process Time: ", (end - start))

    def set_cell_color(self, row, column):
        self.model.change_color(row, column, QBrush(Qt.red))

    def RunFunction1(self):
        start = timeit.default_timer()
        print("Start RunFunction1")
        #Gans de rij in 't rood
        colums = self.proxy_model.columnCount()

        for c in range(colums):

            self.set_cell_color(3, c)

        print("Stop RunFunction1")
        end = timeit.default_timer()
        print("Process Time: ", (end - start))

    def RunFunction2(self):
        start = timeit.default_timer()
        print("Start RunFunction1")
        #Gans de rij in 't rood
        colums = self.proxy_model.columnCount()

        for c in range(colums):

            self.set_cell_color(3, c)

        print("Stop RunFunction1")
        end = timeit.default_timer()
        print("Process Time: ", (end - start))

    def SetFilteredView(self):

        # print("Start set_filter")
        filter_text = self.ui.inputFilter.text()
        self.proxy_model.setFilterFixedString(filter_text)
        filter_result = self.proxy_model.rowCount()
        self.ui.lblResult.setText("(" + str(filter_result) + " records)")


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    win = TableViewer()
    win.show()
    sys.exit(app.exec_())enter code here

And the slow one:

import timeit

import pandas as pd
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtCore import QAbstractTableModel
from PyQt5.QtCore import Qt, QSortFilterProxyModel
from PyQt5.QtGui import *
from PyQt5.uic import loadUi


class PandasTableModel(QAbstractTableModel):
    def __init__(self, data, parent=None):
        QAbstractItemModel.__init__(self, parent)
        self._data = data
        self.colors = dict()

    def rowCount(self, parent=None):
        return self._data.index.size
    def columnCount(self, parent=None):
        return self._data.columns.size

    def setData(self, index, value, role):

        if role == Qt.EditRole:
            # Set the value into the frame.
            self._data.iloc[index.row(), index.column()] = value
            return True

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.DisplayRole:
                return str(self._data.iloc[index.row(), index.column()])
            if role == Qt.EditRole:
                return str(self._data.iloc[index.row(), index.column()])
            if role == Qt.BackgroundRole:
                color = self.colors.get((index.row(), index.column()))
                if color is not None:
                    return color
        return None

    def headerData(self, rowcol, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._data.columns[rowcol]
        if orientation == Qt.Vertical and role == Qt.DisplayRole:
            return self._data.index[rowcol]
        return None

    def change_color(self, row, column, color):
        ix = self.index(row, column)
        self.colors[(row, column)] = color
        self.dataChanged.emit(ix, ix, (Qt.BackgroundRole,))


class TableViewer(QtWidgets.QMainWindow):

    def __init__(self):
        super(TableViewer, self).__init__()

        self.ui = loadUi("QTableViewForm.ui", self)
        self.ui.cmdRun1.clicked.connect(self.RunFunction1)
        self.ui.cmdRun2.clicked.connect(self.RunFunction2)
        self.ui.inputFilter.textChanged.connect(self.SetFilteredView)

        self.showdata()

    def showdata(self):
        start = timeit.default_timer()
        print("Start LoadData")

        data = pd.read_pickle("productdata.pkl")

        self.model = PandasTableModel(data)
        self.ui.tableData.setModel(self.model)
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(-1)  # Search all columns.
        self.proxy_model.setSourceModel(self.model)
        self.proxy_model.sort(0, Qt.AscendingOrder)
        self.proxy_model.setFilterCaseSensitivity(False)
        self.ui.tableData.setModel(self.proxy_model)

        print("Stop LoadData")
        end = timeit.default_timer()
        print("Process Time: ", (end - start))

    def set_cell_color(self, row, column):
        self.model.change_color(row, column, QBrush(Qt.red))

    def RunFunction1(self):
        start = timeit.default_timer()
        print("Start RunFunction1")
        #Gans de rij in 't rood
        colums = self.proxy_model.columnCount()

        for c in range(colums):

            self.set_cell_color(3, c)

        print("Stop RunFunction1")
        end = timeit.default_timer()
        print("Process Time: ", (end - start))

    def RunFunction2(self):
        start = timeit.default_timer()
        print("Start RunFunction1")
        #Gans de rij in 't rood
        colums = self.proxy_model.columnCount()

        for c in range(colums):

            self.set_cell_color(3, c)

        print("Stop RunFunction1")
        end = timeit.default_timer()
        print("Process Time: ", (end - start))

    def SetFilteredView(self):

        # print("Start set_filter")
        filter_text = self.ui.inputFilter.text()
        self.proxy_model.setFilterFixedString(filter_text)
        filter_result = self.proxy_model.rowCount()
        self.ui.lblResult.setText("(" + str(filter_result) + " records)")


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    win = TableViewer()
    win.show()
    sys.exit(app.exec_())

(i'm loading 2000 rows and 35 columns)

Can i have a fast one with the background color function in it ?

Cheers , Johnson



Solution 1:[1]

Instead of

def set_cell_color(self, row, column):
        self.model.change_color(row, column, QBrush(Qt.red))

use this

def set_cell_color(self, row, column):
        self.model.item(row, column).setBackground(QBrush(Qt.red))

Depending on your requirements, you may need to replace model for proxy_model, but it depends on whether row and column should refer to coordinates of the filtered or the underlying model. So it is up to you.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 HiFile the best file manager