Search code examples
pythonpython-2.7qtableviewpyside2qsortfilterproxymodel

QSortFilterProxyModel by column value


Example of table model data

I have a gui with a tabWidget and inside each I have a tableview. Each tab describes a folder (see column type) with subdirs from which I pull the data. I want to have one main model that drives all views by attaching a QSortFilterProxy Model inbetween each tableview that filters the main model for the "type" of each individual TableView (passed into the subclass RenderTypeProxyModel). Bonus: Ideally, they should be sorted as well, so that the most recent view (see date column) comes out on top. Here is my current version but the table remains blank for a reason I can't figure out:

import sys
import os
from datetime import datetime
from pprint import pprint
from PySide2 import QtCore, QtGui, QtWidgets

#To be replaced by env variable
pathToProject = "/run/media/centos7/Data/Projects/Programming/Pipeline/SampleProject"
allowedExportTypes = ["img-prv", "img-final", "img-cg", "img-src", "camera"]


class ExportTableModel(QtCore.QAbstractTableModel):
    def __init__(self, exportData, horizontalHeaders, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.__exportData = exportData
        self.__horizontalHeaders = horizontalHeaders

    def rowCount(self, parent):
        return len(self.__exportData)
    def columnCount(self, parent):
        return len(self.__horizontalHeaders)

    def data(self, index, role): #Returns the data stored under the given role for the item referred to by the index.
        if role == QtCore.Qt.DisplayRole:
            row = index.row()
            column = index.column()
            value = self.__exportData[row][column]
            return value

    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            if orientation == QtCore.Qt.Horizontal:
                if section < len(self.__horizontalHeaders):
                    return self.__horizontalHeaders[section]
                else:
                    return "not implemented"

def tableSetup(tableView):
    tableView.setAlternatingRowColors(True)
    tableView.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
    tableView.setSortingEnabled(True)

class RenderTypeProxyModel (QtCore.QSortFilterProxyModel): #Custom Proxy Model
    def __init__(self, type, parent=None):
        super(RenderTypeProxyModel,self).__init__(parent)
        self.__type = type

    def filterAcceptsRow(self, row, parent): #returns true if the given row should be included in the model
        model = self.sourceModel()
        index = model.index(row, 3, parent)
        if model.data(index,QtCore.Qt.DisplayRole) == type:
            return True
        else:
            return False

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    horizontalHeaders = ["status", "comment", "task", "type", "version", "user", "date", "filepath"] #horizontal header data
    #exportData = #insert sample data list of lists here for testing

    tableView = QtWidgets.QTableView()
    tableView.show()

    model = ExportTableModel(exportData,horizontalHeaders)
    proxyModel = RenderTypeProxyModel("img-prv")
    proxyModel.setSourceModel(model)
    tableView.setModel(proxyModel)

    tableSetup(tableView)
    sys.exit(app.exec_())

Here is sample data to use for debugging: https://pastebin.com/AB1XvKju Just feed it into the exportData variable inside the __main__ method.


Solution

  • The error is caused because type is a reserved word in python:

    if model.data(index,QtCore.Qt.DisplayRole) == type:
    

    you must use self.__type.

    On the other hand if you want to sort the data by date it is not necessary to enable setSortingEnabled(), just use sort().

    from PySide2 import QtCore, QtGui, QtWidgets
    
    #To be replaced by env variable
    allowedExportTypes = ["img-prv", "img-final", "img-cg", "img-src", "camera"]
    
    
    class ExportTableModel(QtCore.QAbstractTableModel):
        def __init__(self, exportData, horizontalHeaders, parent=None):
            super(ExportTableModel, self).__init__(parent)
            self.__exportData = exportData
            self.__horizontalHeaders = horizontalHeaders
    
        def rowCount(self, parent=QtCore.QModelIndex()):
            return len(self.__exportData)
    
        def columnCount(self, parent=QtCore.QModelIndex()):
            return len(self.__horizontalHeaders)
    
        def data(self, index, role=QtCore.Qt.DisplayRole):
            if role == QtCore.Qt.DisplayRole:
                row = index.row()
                column = index.column()
                value = self.__exportData[row][column]
                return value
    
        def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
            if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal:
                if section < self.columnCount():
                    return self.__horizontalHeaders[section]
                return "not implemented"
    
    def tableSetup(tableView):
        tableView.setAlternatingRowColors(True)
        tableView.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
    
    class RenderTypeProxyModel (QtCore.QSortFilterProxyModel): #Custom Proxy Model
        def __init__(self, _type, parent=None):
            super(RenderTypeProxyModel,self).__init__(parent)
            self.__type = _type
    
        def filterAcceptsRow(self, row, parent):
            _type = self.sourceModel().index(row, 3, parent).data()
            return _type == self.__type
    
        def lessThan(self, left, right):
            fmt = "yyyy-MM-dd hh:mm:ss"
            left_data = self.sourceModel().data(left)
            right_data = self.sourceModel().data(right)
            return QtCore.QDateTime.fromString(left_data, fmt) < QtCore.QDateTime.fromString(right_data, fmt)
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
    
        horizontalHeaders = ["status", "comment", "task", "type", "version", "user", "date", "filepath"] #horizontal header data
        exportData = # ...
        model = ExportTableModel(exportData, horizontalHeaders)
        tab_widget = QtWidgets.QTabWidget()
        for _type in allowedExportTypes:
            tableView = QtWidgets.QTableView()
            c = horizontalHeaders.index("date")
            tableSetup(tableView)
            proxy = RenderTypeProxyModel(_type, tableView)
            proxy.setSourceModel(model)
            proxy.sort(c, QtCore.Qt.AscendingOrder)
            tableView.setModel(proxy)
            tab_widget.addTab(tableView ,_type)
    
        tab_widget.show()
        sys.exit(app.exec_())
    

    Update:

    In the next part I have made improvements to your original code avoiding overwriting unnecessary methods and adding a delegate:

    from PySide2 import QtCore, QtGui, QtWidgets
    
    #To be replaced by env variable
    allowedExportTypes = ["img-prv", "img-final", "img-cg", "img-src", "camera"]
    
    
    class DateDelegate(QtWidgets.QStyledItemDelegate):
        def initStyleOption(self, option, index):
            super(DateDelegate, self).initStyleOption(option, index)
            option.text = index.data().toString("yyyy-MM-dd hh:mm:ss")
    
    class ExportTableModel(QtCore.QAbstractTableModel):
        def __init__(self, exportData, horizontalHeaders, parent=None):
            super(ExportTableModel, self).__init__(parent)
            self.__exportData = exportData
            self.__horizontalHeaders = horizontalHeaders
    
        def rowCount(self, parent=QtCore.QModelIndex()):
            return len(self.__exportData)
    
        def columnCount(self, parent=QtCore.QModelIndex()):
            return len(self.__horizontalHeaders)
    
        def data(self, index, role=QtCore.Qt.DisplayRole):
            if role == QtCore.Qt.DisplayRole:
                row = index.row()
                column = index.column()
                value = self.__exportData[row][column]
                header = self.headerData(column, QtCore.Qt.Horizontal)
                if header == "date":
                    value = QtCore.QDateTime.fromString(value, "yyyy-MM-dd hh:mm:ss")
                return value
    
        def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
            if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal:
                if section < self.columnCount():
                    return self.__horizontalHeaders[section]
                return "not implemented"
    
    def create_tableview():
        tableView = QtWidgets.QTableView()
        tableView.setAlternatingRowColors(True)
        tableView.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
        return tableView
    
    class RenderTypeProxyModel (QtCore.QSortFilterProxyModel):
        def __init__(self, c_type, _type, c_date, model, parent=None):
            super(RenderTypeProxyModel,self).__init__(parent)
            self.setSourceModel(model)
            self.setFilterKeyColumn(c_type)
            self.setFilterFixedString(_type)
            self.sort(c_date, QtCore.Qt.AscendingOrder)
    
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
    
        horizontalHeaders = ["status", "comment", "task", "type", "version", "user", "date", "filepath"] #horizontal header data
        exportData = # ...
        c_date = horizontalHeaders.index("date")
        c_type = horizontalHeaders.index("type")
        tab_widget = QtWidgets.QTabWidget()
        for _type in allowedExportTypes:
            tableView = create_tableview()
            tab_widget.addTab(tableView ,_type)
            proxy = RenderTypeProxyModel(c_type, _type, c_date, model, tableView)
            tableView.setModel(proxy)
            delegate = DateDelegate(tableView)
            tableView.setItemDelegateForColumn(c_date, delegate)
    
        tab_widget.show()
        sys.exit(app.exec_())