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
Comments
great example.
I was looking for a simple example of QAbstractTableModel combined with QTreeView. Thanks for this example.
Thanks, it help me a lot.
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!
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.
What about selection? If I select some rows and then I change sort, the selection doesn't change.
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......
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).
effe, thank you for adding your optimization. I agree that my method is very inefficient for large N.
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?
I apologize, it was not an instance of QTableView, rather one of QTableWidget.
Indeed, it made my day, thanks
Holger
Thank you very much.. this is a very helpfully example for new...^^
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?
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.
I do not refresh the table. How could i do this? Do you have an example code for that??Thanks for everything
You don't have an idea? what I could do?
I solved my problem. It was because I opened a new model class before opening the data. So it was my fault...
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()"))
Hi,
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.
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
Thanks, it helps me finish tasks on time
Hi,
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?
disqus:1869147284