SaltyCrane Blog — Notes on JavaScript and web development

PyQt 4.3 QTableView / QAbstractTableModel sorting example

It took me a while to figure out why QTableView's setSortingEnabled method wasn't working. It turns out the sort method in QAbstractItemModel is not implemented. So I had to implement it myself. Hence, my previous post, How to sort a table by columns in Python. I'm not sure if this is the best way to implement the sort method, but I couldn't find anything else out there, and this seems to work for me.

import re
import operator
import os
import sys 
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 
def main(): 
    app = QApplication(sys.argv) 
    w = MyWindow() 
class MyWindow(QWidget): 
    def __init__(self, *args): 
        QWidget.__init__(self, *args) 

        # create table
        table = self.createTable() 
        # layout
        layout = QVBoxLayout()

    def get_table_data(self):
        stdouterr = os.popen4("dir c:\\")[1].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) 

        # set the minimum size
        tv.setMinimumSize(400, 300)

        # hide grid

        # set the font
        font = QFont("Courier New", 8)

        # hide vertical header
        vh = tv.verticalHeader()

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

        # set column width to fit contents

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

        # enable sorting

        return tv
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.arraydata = sorted(self.arraydata, key=operator.itemgetter(Ncol))        
        if order == Qt.DescendingOrder:

if __name__ == "__main__": 


#1 rodrigo commented on :

great example.

#2 g.a commented on :

I was looking for a simple example of QAbstractTableModel combined with QTreeView. Thanks for this example.

#3 Germán commented on :

Thanks, it help me a lot.

#4 Chris commented on :

This was wonderfully helpful! I was just trying to figure out why I couldn't get headerData() to work properly, and this gave me that plus sorting. Thanks!

#5 Sharrea commented on :

Thanks very much for the example! Just about to try pyqt for the first time because I couldn't get wxPython's listctrl to do what I wanted. Hopefully pyqt will work out for me. Thanks again.

#6 borras commented on :

What about selection? If I select some rows and then I change sort, the selection doesn't change.

#7 threaderslash commented on :

Hey Man! So far, you have the best python blog tutorial that I have found all around internet. Really nice your Classes and articles. Feel free to contact me if you need some input on MySQL or C++. Cheers......

#8 effe commented on :

you might add:

def sizeHint(data):
    return 18

and add in __init__(self)

self.table.sizeHintForRow = sizeHint

if you then replace

for row in xrange(nrows):
    tv.setRowHeight(row, 18)



the resizing oberation is handeled much faster(for large n).

#9 Eliot commented on :

effe, thank you for adding your optimization. I agree that my method is very inefficient for large N.

#10 Ryan commented on :

When trying to use this concept in my own code, I am getting an error that tells me that QTableView.setMode() is a private method. I don't understand why this would throw an AttributeError exception when I call it with a QTableView instance. Any thoughts?

#11 Ryan commented on :

I apologize, it was not an instance of QTableView, rather one of QTableWidget.

#12 Holger commented on :

Indeed, it made my day, thanks


#13 Soyoung commented on :

Thank you very much.. this is a very helpfully example for new...^^

#14 jon commented on :

Your example is great but I have a problem. After double clicking on a row I want to get the data of the row.

I want to get the date with this line: selected_voc = TableModel(list, header, self).datas(index, role=QtCore.Qt.DisplayRole)

Without sorting everything works great but after sorting it returns the wrong data

For example: unsorted QtableView 1 Bern 2 Albarn 3 Cougoogh

sorted 2 Albarn 1 Bern 3 Cougoogh

After clicking on the second row (sorted QTableView)it returns: 2 Albarn

Perhaps you have an idea to fix?

#15 pradeep commented on :

thank u for the example code , i want to know how do u refresh table contents when table data is changed, assume table is in some layout.

#16 jon commented on :

I do not refresh the table. How could i do this? Do you have an example code for that??Thanks for everything

#17 Jon commented on :

You don't have an idea? what I could do?

#18 Jon commented on :

I solved my problem. It was because I opened a new model class before opening the data. So it was my fault...

#19 A commented on :

Your sort function, because it reverses the whole list when sorting in descending order, breaks "stability". I.e., if you had some other column sorted it a particular way, it no longer will be. I used this technique instead:

self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()")) reverse = False if order == QtCore.Qt.DescendingOrder: reverse = True self.arraydata = sorted(self.arraydata, key=operator.itemgetter(Ncol), reverse=reverse) self.emit(QtCore.SIGNAL("layoutChanged()"))

#20 Vijay commented on :


My QTableView has only one column and few rows, but the QTableView shows a big white space in the row section after displaying the rows and a white space after displaying a single column.How do i fix this. I just need to change the row length and column length of the QTableView to the string list which i use for the model. I have a one dimentional string array. Any suggestions.

#21 Grant commented on :

columnCount should be:

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

otherwise it errors when the table is empty

#22 wx commented on :

Thanks, it helps me finish tasks on time

#23 jr commented on :

I want to use the example in pyqt5, but i have no clue about how to change the SIGNAL part to pyqtsignal. Has anyone done this already and could help me please?