PyQt5 QTableView with QAbstractTableModel along with QSortFilterProxyModel

255 views Asked by At

I have implemented a QTableView with QAbstractTableModel along with QSortFilterProxyModel

After adding the QSortFilterProxyModel, I am not able to print the Model's data. I am adding the code below.

In the below code I have added comments like "with proxy model" and "without proxy model"

If you comments the lines "with proxy model" and uncomment "without proxy model", run the code and click on "Export" button, then the data in the model in printed in the terminal

But if you uncomments the lines "with proxy model" and comment "without proxy model", run the code and click on "Export" button then error message shown as

line 235, in export_tv self.tableview.model().print_arraydata() AttributeError: 'QSortFilterProxyModel' object has no attribute 'print_arraydata'

I want export feature to work along with the QSortFilterProxyModel

Add / Delete button also does not work with QSortFilterProxyModel. What am I missing here?

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox
from datetime import datetime
import traceback
import sys

class Utils:
    @staticmethod
    def showMsgOnScreen(pFunctionName, pErrorMessage):
        try:
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Critical)
            msg.setText(pFunctionName)
            msg.setInformativeText(pErrorMessage)
            msg.setWindowTitle("Error")
            msg.exec_()
        except Exception as e:
            errmsg = "".join(traceback.format_exception(type(e), e, e.__traceback__))
            raise Exception(f"Function: Utils.showMsgOnScreen\n{errmsg}")


class Model(QtCore.QAbstractTableModel):
    ActiveRole = QtCore.Qt.UserRole + 1
    def __init__(self, datain, columnHeaders, columnDataType, columneditlist, parent=None):
        try:
            super().__init__()
            self._data = datain
            self._cols = columnHeaders
            self.r = len(self._data)
            self.c = len(self._data[0])
            self.columnDataType = columnHeaders
            self.columneditlist = columneditlist
        except Exception as e:
            Utils.showMsgOnScreen("Model.__init__", "".join(traceback.format_exception(type(e), e, e.__traceback__)))
    
    def headerData(self, column, orientation, role):
        try:
            if role == QtCore.Qt.DisplayRole:
                if orientation == QtCore.Qt.Horizontal:
                    return QtCore.QVariant(self._cols[column])
                else:
                    return super().headerData(column, orientation, role)
                return QtCore.QVariant()
        except Exception as e:
            Utils.showMsgOnScreen("Model.headerData", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def rowCount(self, parent=QtCore.QModelIndex()):
        try:
            if parent.isValid(): return 0
            return len(self._data)
        except Exception as e:
            Utils.showMsgOnScreen("Model.rowCount", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def columnCount(self, parent=QtCore.QModelIndex()):
        try:
            if parent.isValid(): return 0
            if len(self._data) > 0:
                return len(self._data[0])
            return 0
        except Exception as e:
            Utils.showMsgOnScreen("Model.columnCount", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def flags(self, index):
        try:
            if self.columneditlist[index.column()] == False:
                return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
            elif self.columneditlist[index.column()] == True:
                return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEnabled
        except Exception as e:
            Utils.showMsgOnScreen("Model.flags", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def data(self, index, role):
        try:
            if not index.isValid():
                return QtCore.QVariant()
            if index.isValid():
                col = index.column()
                if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
                    if self.columnDataType[col] == "INT":
                        return QtCore.QVariant(int(self._data[index.row()][index.column()]))
                    else:
                        return QtCore.QVariant(self._data[index.row()][index.column()])
                elif role == QtCore.Qt.TextAlignmentRole:
                    if self.columnDataType[col] == "INT":
                        return int(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
                    if self.columnDataType[col] == "STR":
                        return int(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
                    if self.columnDataType[col] == "COMBOBOX":
                        return int(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
                elif role == QtCore.Qt.BackgroundRole:
                    if self.columneditlist[index.column()] == False:
                        return QtCore.QVariant(QtGui.QColor(QtCore.Qt.gray))
                elif role != QtCore.Qt.DisplayRole:
                    return QtCore.QVariant()
                return QtCore.QVariant(self._data[index.row()][index.column()])
        except Exception as e:
            Utils.showMsgOnScreen("Model.data", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        try:
            if role == QtCore.Qt.EditRole:
                self._data[index.row()][index.column()] = value
                self.dataChanged.emit(index, index, (QtCore.Qt.DisplayRole,))
                return True
            return False
        except Exception as e:
            Utils.showMsgOnScreen("Model.setData", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def print_arraydata(self):
        try:
            print()
            for rownum in range(len(self._data)):
                print(self._data[rownum])
        except Exception as e:
            Utils.showMsgOnScreen("Model.print_arraydata", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def insert_row(self, position, rows=1):
        try:
            self.beginInsertRows(QtCore.QModelIndex(), position, (position+1) + rows - 1)
            prevrowdata = self._data[position].copy()
            self._data.insert(position+1, prevrowdata)
            self.endInsertRows()
            return True
        except Exception as e:
            Utils.showMsgOnScreen("Model.insert_row", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def remove_row(self, position, rows=1):
        try:
            self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)
            self._data = self._data[:positio] + self._data[position + rows:]
            self.endRemoveRows()
            return True
        except Exception as e:
            Utils.showMsgOnScreen("Model.remove_row", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def append_row(self, crow):
        try:
            self.insert_row(crow)
        except Exception as e:
            Utils.showMsgOnScreen("Model.append_row", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

class Main(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        try:
            super().__init__(parent)

            self.get_table_data()
            self.tableview, self.proxymodel = self.createTable()
            self.tableview.model().rowsInserted.connect(lambda: QtCore.QTimer.singleShot(0, self.tableview.scrollToBottom))

            # Set the maximum value of row to the selected row
            self.selectrow = self.tableview.model().rowCount()

            # create buttons
            self.addbtn = QtWidgets.QPushButton("Add")
            self.addbtn.clicked.connect(self.insert_row)
            self.deletebtn = QtWidgets.QPushButton("Delete")
            self.deletebtn.clicked.connect(self.remove_row)
            self.exportbtn = QtWidgets.QPushButton("Export")
            self.exportbtn.clicked.connect(self.export_tv)
            self.computebtn = QtWidgets.QPushButton("Compute")
            self.enablechkbox = QtWidgets.QPushButton("Completed")

            # create label
            self.searchbar = QtWidgets.QLineEdit()
            self.searchbar.setFont(QtGui.QFont("Arial", 10))
            self.searchbar.textChanged.connect(self.proxymodel.setFilterFixedString)

            # create gridlayout
            grid_layout = QtWidgets.QGridLayout()
            grid_layout.addWidget(self.exportbtn, 2, 2, 1, 1)
            grid_layout.addWidget(self.computebtn, 2, 3, 1, 1)
            grid_layout.addWidget(self.addbtn, 2, 4, 1, 1)
            grid_layout.addWidget(self.deletebtn, 2, 5, 1, 1)
            grid_layout.addWidget(self.enablechkbox, 2, 6, 1, 1, QtCore.Qt.AlignCenter)
            grid_layout.addWidget(self.tableview, 1, 0, 1, 7)
            grid_layout.addWidget(self.searchbar, 0, 3, 1, 1, QtCore.Qt.AlignCenter)

            # initializing layout
            self.title = "Data Visualization Tool"
            self.setWindowTitle(self.title)
            self.setGeometry(0, 0, 1024, 576)
            self.showMaximized()
            self.centralwidget = QtWidgets.QWidget()
            self.centralwidget.setLayout(grid_layout)
            self.setCentralWidget(self.centralwidget)
        except Exception as e:
            Utils.showMsgOnScreen("Main.__init__", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def get_table_data(self):
        try:
            self.tabledata = [
                [1, 'type_1', 21, 'RESOLVED', 'Jack', '2023-01-01'], 
                [2, 'type_2', 22, 'UNRESOLVED', 'Roger', '2023-01-02'], 
                [3, 'type_3', 55, 'XYZ', 'Ethan', '2023-01-03'], 
                [4, 'type_4', 66, 'abc', 'Ivan', '2023-01-04'], 
                [5, 'type_5', 111, 'abc', 'Rob', '2023-01-05'], 
                [6, 'type_6', 60, 'abc', 'Rachel', '2023-01-06'], 
                [7, 'type_7', 12, 'abc', 'Stacie', '2023-01-07']
            ]
        except Exception as e:
            Utils.showMsgOnScreen("Main.get_table_data", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def createTable(self):
        try:
            tv = QtWidgets.QTableView()

            datadict = {
                "columnheaders": ['SRNO', 'TYPE', 'ID', 'STATUS', 'NAME', 'START_DATE'],
                "columndatatype": ['INT', 'COMBOBOX', 'INT', 'STR', 'STR', 'DATE'],
                "columnedittype": [False, True, True, True, True, True]
            }

            tablemodel = Model(self.tabledata,
                                datadict['columnheaders'],
                                datadict['columndatatype'],
                                datadict['columnedittype'],
                                self)
            proxy_model = QtCore.QSortFilterProxyModel()
            proxy_model.setFilterKeyColumn(-1)                              # with proxy model
            proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) # with proxy model
            proxy_model.setSourceModel(tablemodel)                          # with proxy model
            proxy_model.sort(0, QtCore.Qt.AscendingOrder)                   # with proxy model
            tv.setModel(proxy_model)                                        # with proxy model
            # tv.setModel(tablemodel)                                       # without proxy model

            tv.resizeRowsToContents()

            return tv, proxy_model
        except Exception as e:
            Utils.showMsgOnScreen("Main.createTable", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def export_tv(self):
        try:
            self.tableview.model().print_arraydata()
        except Exception as e:
            Utils.showMsgOnScreen("Main.export_tv", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def remove_row(self):
        try:
            currrow = self.tableview.currentIndex().row()
            if currrow != -1:
                self.tableview.model().remove_row(currrow)
        except Exception as e:
            Utils.showMsgOnScreen("Main.remove_row", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

    def insert_row(self):
        try:
            currrow = self.tableview.currentIndex().row()
            if currrow != -1:
                self.tableview.model().append_row(currrow)
        except Exception as e:
            Utils.showMsgOnScreen("Main.insert_row", "".join(traceback.format_exception(type(e), e, e.__traceback__)))

if __name__ == '__main__':
    try:
        app = QtWidgets.QApplication(sys.argv)
        main = Main()
        main.show()
        sys.exit(app.exec_())
    except Exception as e:
        errmsg = "".join(traceback.format_exception(type(e), e, e.__traceback__))
        errorstring = f"Function: __main__\n{str(errmsg)}"
        print("Error Message!", f"{errorstring}", "Critical")
0

There are 0 answers