PyQt 4.3 QTableView / QAbstractTableModel sorting example
It took me a while to figure out why
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:\\").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) 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()
- 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
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)
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
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()"))
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) return 0
otherwise it errors when the table is empty
Thanks, it helps me finish tasks on time
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?