Pyside, TableView and pandas, add row

Viewed 70

I try to show and modify information of a dataframe with a QTableView. I can modify rows, and changes are ok in the model but when I try to add a new row, nothing was saved in the model... In my code, I try to add [1, 1, 1] whith the insertRow function... No warning or error... So where is my mistake?

My code :

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, v_data):
        super().__init__()
        self._data = v_data
    
    def flags(self, index):
        return Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsEditable
    
    def setData(self, index, value, role):
        if role == Qt.EditRole:
            self._data.iloc[index.row(),index.column()] = value
            return True

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.DisplayRole or role == Qt.EditRole:
                value = self._data.iloc[index.row(), index.column()]
                return str(value)

    def rowCount(self, index):
        return self._data.shape[0]

    def columnCount(self, index):
        return self._data.shape[1]

    def headerData(self, section, orientation, role):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return str(self._data.columns[section])

            if orientation == Qt.Vertical:
                return str(self._data.index[section])
    def insertRow(self, ajout):
        row = len(self._data)
        self.beginInsertRows(QtCore.QModelIndex(), row, row)
        self._data.append(ajout)
        print(self._data)
        self.endInsertRows()
        self.layoutChanged.emit()
        return row


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        
        container = QWidget()
        layout = QVBoxLayout()

        self.addButton = QPushButton("test")
        self.addButton.pressed.connect(self.add_row)
        self.search = QLineEdit()
        self.table = QtWidgets.QTableView()
        layout.addWidget(self.addButton)
        layout.addWidget(self.search)
        layout.addWidget(self.table)
        container.setLayout(layout)

        df_data = pd.DataFrame(
            [
                [1, 9, 2],
                [1, 0, -1],
                [3, 5, 2],
                [3, 3, 2],
                [5, 8, 9],
            ],
            columns=["A", "B", "C"],
            index=["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"],
        )

        self.model = TableModel(df_data)
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(0) # Search all columns.
        self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.proxy_model.setSourceModel(self.model)

        self.table.setModel(self.proxy_model)
        self.search.textChanged.connect(self.proxy_model.setFilterWildcard)


        self.setMinimumSize(QSize(1024, 600))
        self.setCentralWidget(container)
    
    def add_row(self):
        self.model.insertRow([1, 1, 1])
 
        self.model.layoutChanged.emit()
        print('success')
1 Answers

The problem of your code is in the line you try to append the data to your pd.DataFrame

self._data.append(ajout)

First of all, as pandas says this is a deprecated function so if you want to add some data you should use concat().

Anyway, since you want to keep the name of the indexes, you must replace that line with this one:

self._data.loc["Row" + str(row+1)] = ajout

This specifies that you want the next row to have the data you intend to add.

Here is the whole code:

from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import Qt, QSortFilterProxyModel, QSize
from PySide2.QtWidgets import QWidget, QPushButton, QVBoxLayout, QLineEdit, QApplication
import sys
import pandas as pd

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, v_data):
        super().__init__()

        self._data = v_data
    
    def flags(self, index):
        return Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsEditable
    
    def setData(self, index, value, role):
        if role == Qt.EditRole:
            self._data.iloc[index.row(),index.column()] = value
            return True

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.DisplayRole or role == Qt.EditRole:
                value = self._data.iloc[index.row(), index.column()]
                return str(value)

    def rowCount(self, index):
        return self._data.shape[0]

    def columnCount(self, index):
        return self._data.shape[1]

    def headerData(self, section, orientation, role):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return str(self._data.columns[section])

            if orientation == Qt.Vertical:
                return str(self._data.index[section])
    def insertRow(self, ajout):
        row = len(self._data)
        self.beginInsertRows(QtCore.QModelIndex(), row, row)
        print(self._data, "\n\n") # Before adding
        self._data.loc["Row" + str(row+1)] = ajout
        print(self._data, "\n\n") # After
        self.endInsertRows()
        self.layoutChanged.emit()
        return row


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        
        container = QWidget()
        layout = QVBoxLayout()

        self.addButton = QPushButton("test")
        self.addButton.clicked.connect(self.add_row)
        self.search = QLineEdit()
        self.table = QtWidgets.QTableView()
        layout.addWidget(self.addButton)
        layout.addWidget(self.search)
        layout.addWidget(self.table)
        container.setLayout(layout)

        df_data = pd.DataFrame(
            [
                [1, 9, 2],
                [1, 0, -1],
                [3, 5, 2],
                [3, 3, 2],
                [5, 8, 9],
            ],
            columns=["A", "B", "C"],
            index=["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"],
        )
        self.model = TableModel(df_data)
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(0) # Search all columns.
        self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.proxy_model.setSourceModel(self.model)

        self.table.setModel(self.proxy_model)
        self.search.textChanged.connect(self.proxy_model.setFilterWildcard)


        self.setMinimumSize(QSize(1024, 600))
        self.setCentralWidget(container)
    
    def add_row(self):
        self.model.insertRow([1, 1, 1])
 
        self.model.layoutChanged.emit()
        print('success')




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

    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

I hope this fix your problem, tell me if it doesn't work for you.

Related