0

I was trying to figure out how to display a table that I had subset using the QTableView widget. I'm running into a weird error and I'm not sure if it's a bug or an issue with my code. I can load in a dataset just fine. When the program runs and the data load button is pressed, QTableView will display the proper number of rows and column names but most, if not all, of the data is missing. However, the data will appear if you try and sort the table by clicking on any of the column names. Some of the error messages when I first load int he dataset say "# we by definition only have the 0th axis" or "# we have yielded a scalar ?".

I thought this might be an issue with the csv file I was using, but I was able to easily reproduce it with a randomized table of numbers. Commenting out the sort function still results in no data displaying but now sorting can't be used to make the data appear. The code I used in my example is a modified from the answer here.

Importing necessary packages and functions.

from PyQt5 import QtCore, QtGui, QtWidgets
import pandas as pd
import numpy as np

class PandasModel(QtCore.QAbstractTableModel): 
    def __init__(self, df = pd.DataFrame(), parent=None): 
        QtCore.QAbstractTableModel.__init__(self, parent=parent)
        self._df = df

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()

        if orientation == QtCore.Qt.Horizontal:
            try:
                return self._df.columns.tolist()[section]
            except (IndexError, ):
                return QtCore.QVariant()
        elif orientation == QtCore.Qt.Vertical:
            try:
                # return self.df.index.tolist()
                return self._df.index.tolist()[section]
            except (IndexError, ):
                return QtCore.QVariant()

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()

        if not index.isValid():
            return QtCore.QVariant()

        return QtCore.QVariant(str(self._df.ix[index.row(), index.column()]))

    def setData(self, index, value, role):
        row = self._df.index[index.row()]
        col = self._df.columns[index.column()]
        if hasattr(value, 'toPyObject'):
            # PyQt4 gets a QVariant
            value = value.toPyObject()
        else:
            # PySide gets an unicode
            dtype = self._df[col].dtype
            if dtype != object:
                value = None if value == '' else dtype.type(value)
        self._df.set_value(row, col, value)
        return True

    def rowCount(self, parent=QtCore.QModelIndex()): 
        return len(self._df.index)

    def columnCount(self, parent=QtCore.QModelIndex()): 
        return len(self._df.columns)

    def sort(self, column, order):
        colname = self._df.columns.tolist()[column]
        self.layoutAboutToBeChanged.emit()
        self._df.sort_values(colname, ascending= order == QtCore.Qt.AscendingOrder, inplace=True)
        self._df.reset_index(inplace=True, drop=True)
        self.layoutChanged.emit()

Application that displays the table and handles the subsetting.

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent=None)
        vLayout = QtWidgets.QVBoxLayout(self)
        hLayout = QtWidgets.QHBoxLayout()
        self.loadBtn = QtWidgets.QPushButton("Load Table", self)
        hLayout.addWidget(self.loadBtn)
        vLayout.addLayout(hLayout)
        self.pandasTv = QtWidgets.QTableView(self)
        vLayout.addWidget(self.pandasTv)
        self.loadBtn.clicked.connect(self.loadTable)
        #self.pandasTv.setSortingEnabled(True)

    def loadTable(self):
        dfNum = pd.DataFrame(np.random.randint(0,100,size=(20, 4)), columns=list('ABCD'))
        dfNumSub = dfNum.loc[dfNum['A'].astype(str).str.contains('1')]
        model = PandasModel(dfNumSub)
        self.pandasTv.setModel(model)

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

I want all of the searched for data to show up without having to sort the columns.

EDIT: This error only occurs when subsetting the dataframe. Changing the code to display dfNum instead results in all data being displayed just fine.

wooloop
  • 41
  • 1
  • 7
  • Have you tried replacing `self._df.ix[index.row(), index.column()]` with `self._df.iloc[index.row(), index.column()]` in `PandasModel.data()`? – Heike Jul 03 '19 at 07:02

1 Answers1

1

It's not sorting the data frame itself that's making the data appear, but the call to self._df.reset_index() at the end of PandasModel.sort(). Without this statement, the call to self._df.ix() in PandasModel.data() seems to break. To solve this you could try applying dfNumSub.reset_index() before setting the model in Widget.loadTable or you could change self._df.ix() to self._df.iloc() in PandasModel.data() since DataFrame.ix is deprecated anyway.

Heike
  • 24,102
  • 2
  • 31
  • 45