Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort QTableView in pyqt5

I want to sort a QTableView in PyQT5. I found an example that uses PyQT4, but in PyQT5 SIGNALs are not existing anymore. This is my example code

class MainWindow(QWidget):
def __init__(self, parent=None):
    super(MainWindow, self).__init__(parent)
    # create table
    self.get_table_data()
    table = self.createTable() 

    # layout
    layout = QVBoxLayout()
    layout.addWidget(table) 
    self.setLayout(layout) 

def get_table_data(self):
    stdouterr = os.popen("dir c:\\").read()
    lines = stdouterr.splitlines()
    lines = lines[5:]
    lines = lines[:-2]
    self.tabledata = [re.split(r"\s+", line, 4)
                 for line in lines]

def createTable(self):
    # create the view
    tv = QTableView()

    # set the table model
    header = ['date', 'time', '', 'size', 'filename']
    tm = MyTableModel(self.tabledata, header, self) 
    tv.setModel(tm)
    # set the minimum size
    tv.setMinimumSize(400, 300)

    # hide grid
    tv.setShowGrid(False)

    tv.setSelectionBehavior(QAbstractItemView.SelectRows)
    # set the font

    # hide vertical header
    vh = tv.verticalHeader()
    vh.setVisible(False)

    # set horizontal header properties
    hh = tv.horizontalHeader()
    hh.setStretchLastSection(True)

    # set column width to fit contents
    tv.resizeColumnsToContents()

    # set row height
    nrows = len(self.tabledata)
    for row in range(nrows):
        tv.setRowHeight(row, 18)

    # enable sorting
    tv.setSortingEnabled(True)

    return tv
    self.setWindowTitle("Finance")

class MyTableModel(QAbstractTableModel): 
def __init__(self, datain, headerdata, parent=None, *args): 
    """ datain: a list of lists
        headerdata: a list of strings
    """
    QAbstractTableModel.__init__(self, parent, *args) 
    self.arraydata = datain
    self.headerdata = headerdata

def rowCount(self, parent): 
    return len(self.arraydata) 

def columnCount(self, parent): 
    return len(self.arraydata[0]) 

def data(self, index, role): 
    if not index.isValid(): 
        return QVariant() 
    elif role != Qt.DisplayRole: 
        return QVariant() 
    return QVariant(self.arraydata[index.row()][index.column()]) 

def headerData(self, col, orientation, role):
    if orientation == Qt.Horizontal and role == Qt.DisplayRole:
        return QVariant(self.headerdata[col])
    return QVariant()
def sort(self, Ncol, order):
    """Sort table by given column number.
    """
    self.emit(SIGNAL("layoutAboutToBeChanged()"))
    self.arraydata = sorted(self.arraydata, key=operator.itemgetter(Ncol))        
    if order == Qt.DescendingOrder:
        self.arraydata.reverse()
    self.emit(SIGNAL("layoutChanged()"))

if __name__ == '__main__':

from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)

helloPythonWidget = MainWindow()
helloPythonWidget.show()

sys.exit(app.exec_())

I tried many different ways to use self.layoutAboutToBeChanged() and pyqtSignal, but to be honest, I don't understand it, since I'm new to python and PyQT in general yet. I tried to get infos from the Documentation, but i didn't get a clue from documentation and didn't found an good example on the web.

UPDATE:

I solved the puzzle:

self.layoutAboutToBeChanged.emit() emits the signal (codecompletion in eclipse is a bit misleading)

def sort(self, Ncol, order):
    """Sort table by given column number.
    """
    self.layoutAboutToBeChanged.emit()
    self.arraydata = sorted(self.arraydata, key=operator.itemgetter(Ncol))        
    if order == Qt.DescendingOrder:
        self.arraydata.reverse()
    self.layoutChanged.emit()

This is the solution

like image 289
Netbus Avatar asked Feb 22 '15 16:02

Netbus


1 Answers

for those of you guys who wants to import pandas dataframe into qt model try this:

class PandasModel(QtCore.QAbstractTableModel):

    def __init__(self, data, parent=None):
        """

        :param data: a pandas dataframe
        :param parent: 
        """
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._data = data
        # self.headerdata = data.columns


    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid():
            if role == QtCore.Qt.DisplayRole:
                return str(self._data.values[index.row()][index.column()])
        return None

    def headerData(self, rowcol, orientation, role):
        # print(self._data.columns[rowcol])
        # print(self._data.index[rowcol])
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return self._data.columns[rowcol]
        if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
            return self._data.index[rowcol]
        return None

    def flags(self, index):
        flags = super(self.__class__, self).flags(index)
        flags |= QtCore.Qt.ItemIsEditable
        flags |= QtCore.Qt.ItemIsSelectable
        flags |= QtCore.Qt.ItemIsEnabled
        flags |= QtCore.Qt.ItemIsDragEnabled
        flags |= QtCore.Qt.ItemIsDropEnabled
        return flags

    def sort(self, Ncol, order):
        """Sort table by given column number.
        """
        try:
            self.layoutAboutToBeChanged.emit()
            self._data = self._data.sort_values(self._data.columns[Ncol], ascending=not order)
            self.layoutChanged.emit()
        except Exception as e:
            print(e)

I have forgotten where I got the base pandasmodel, probably from here

like image 158
Yikang Luo Avatar answered Oct 19 '22 20:10

Yikang Luo