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()
w.show()
sys.exit(app.exec_())
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
# 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.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)
tv.setModel(tm)
# set the minimum size
tv.setMinimumSize(400, 300)
# hide grid
tv.setShowGrid(False)
# set the font
font = QFont("Courier New", 8)
tv.setFont(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 xrange(nrows):
tv.setRowHeight(row, 18)
# enable sorting
tv.setSortingEnabled(True)
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.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__":
main()
Related posts
- PyQt: How to pass arguments while emitting a signal — posted 2008-01-29
- PyQt4 QItemDelegate example with QListView and QAbstractListModel — posted 2008-01-23
- How to install pyqt4 on ubuntu linux — posted 2008-01-15
- Python PyQt Tab Completion example — posted 2008-01-04
- How to capture the Tab key press event with PyQt 4.3 — posted 2008-01-03
- PyQt 4.3 Simple QAbstractListModel/ QlistView example — posted 2008-01-03
19
Comments
—
Comments feed for this post
#2 g.a commented on 2009-02-10:
I was looking for a simple example of QAbstractTableModel combined with QTreeView. Thanks for this example.
#4 Chris commented on 2009-04-09:
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 2009-04-12:
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 2009-07-28:
What about selection? If I select some rows and then I change sort, the selection doesn't change.
#7 threaderslash commented on 2009-10-11:
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 2009-10-29:
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)
with
self.table.resizeRowsToContents()
the resizing oberation is handeled much faster(for large n).
#9 Eliot commented on 2009-10-30:
effe, thank you for adding your optimization. I agree that my method is very inefficient for large N.
#10 Ryan commented on 2009-12-16:
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 2009-12-17:
I apologize, it was not an instance of QTableView, rather one of QTableWidget.
#13 Soyoung commented on 2010-12-16:
Thank you very much.. this is a very helpfully example for new...^^
#14 jon commented on 2011-08-12:
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 2011-08-15:
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 2011-08-27:
I do not refresh the table. How could i do this? Do you have an example code for that??Thanks for everything
#18 Jon commented on 2011-09-08:
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 2012-02-02:
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()"))
Post a comment
About
I'm Eliot and this is my notepad for programming topics such as Python, Django, Ubuntu, Emacs, etc... more »
Search Blog
Tags
-
algorithms
(5)
-
aws
(9)
-
blogproject
(20)
-
c_cplusplus
(12)
-
cardstore
(8)
-
colinux
(2)
-
concurrency
(13)
-
conkeror
(2)
-
core
(2)
-
cygwin
(17)
-
datastructures
(14)
-
datetime
(4)
-
decorators
(4)
-
django
(40)
-
emacs
(22)
-
files_directories
(11)
-
git
(5)
-
hardware
(5)
-
install_setup
(8)
-
javascript
(3)
-
keyboard
(9)
-
matplotlib
(5)
-
mercurial
(4)
-
nginx
(2)
-
persistence
(5)
-
preferences
(7)
-
processes
(4)
-
pyqt
(18)
-
python
(144)
-
ratpoison
(3)
-
regexes
(6)
-
rsync
(3)
-
softwaretools
(17)
-
sql
(14)
-
ssh
(10)
-
subversion
(6)
-
twisted
(7)
-
ubuntu
(65)
-
urxvt
(5)
-
vxworks
(25)
-
webdev
(5)
-
wmii
(7)
Blogroll
- Adam Gomaa
- Alex Clemesha
- Amir Salihefendic
- Armin Ronacher
- David Beazley
- David Ziegler
- Duncan McGreggor
- Gareth Rushgrave
- Glyph Lefkowitz
- Guido van Rossum
- Ian Bicking
- Jacob Kaplan-Moss
- James Bennett
- James Tauber
- Jesper Noehr
- Marty Alchin
- Matt Harrison
- Nikolay Kolev
- Parand Darugar
- Peter Baumgartner
- Peter Bengtsson
- Rob Hudson
- Simon Willison
- Will McGugan
#1 rodrigo commented on 2008-09-09:
great example.