Различия в скорости между QStandardItemModel и QAbstractTableModel?

Может ли кто-нибудь объяснить следующее: У меня есть 2 скрипта для загрузки кадра данных pandas в табличном представлении с полем фильтра. Тот, что со стандартной моделью, загружает данные в раздел «init». С этим все молниеносно, даже фильтрация. Второй скрипт работает намного медленнее, но с ним я могу установить цвет фона ячеек, который мне нужен. Это скрипты:

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

И медленный:

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_())

(я загружаю 2000 строк и 35 столбцов)

Могу ли я получить быструю версию с функцией цвета фона?

Привет, Джонсон

Может быть, я неправильно понял проблему, но почему бы вам не установить цвет фона с помощью doc.qt.io/qt-5/qstandarditem.html#setBackground для стандартной модели?

V.K. author of HiFile 06.05.2022 12:10

Если бы это было так просто, было бы здорово ;-) Могу ли я сделать это для определенной ячейки или строки? Можете ли вы привести пример синтаксиса?

Johnson 06.05.2022 12:41

Хорошо, я добавил этот код к ответу. Кстати. Странно, что модель, унаследованная от QAbstractTableModel, как вы говорите, медленнее. Я бы ожидал обратного, чтобы быть правдой. Возможно, это имеет место только в Python, где больше кода вызывается в Python и меньше в базовой библиотеке C++.

V.K. author of HiFile 06.05.2022 12:57
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
21
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вместо

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

использовать это

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

В зависимости от ваших требований вам может потребоваться заменить model на proxy_model, но это зависит от того, должны ли строка и столбец ссылаться на координаты отфильтрованной или базовой модели. Так что это зависит от вас.

Это было действительно так просто... Большое спасибо!

Johnson 06.05.2022 12:52

Готово ! Еще раз спасибо, вы не хотите знать, сколько времени я трачу на поиски в неправильном направлении ... ;-)

Johnson 06.05.2022 13:25

Мне знакомо это чувство... :)

V.K. author of HiFile 06.05.2022 13:39

Другие вопросы по теме