How can I sort the items in a QTableWidget on multiple columns?

This will depend a little on the size of your dataset. Since we use a stable
sort you can relatively easily achieve this by keeping a list of sort columns
and sort them from last to first.

See the example below for a demonstration.

If you have a lot of records you might want to implement your own model that
will more efficiently sort based on multiple columns.

  1. #include <QtGui>
  2.  
  3. #define SETPERSON(index, first, second, salary)     setItem(index, 0, new QTableWidgetItem(first));    
  4. setItem(index, 1, new QTableWidgetItem(second));    
  5. setItem(index, 2, new QTableWidgetItem);    
  6. item(index, 2)->setData(Qt::DisplayRole, salary);
  7.  
  8. class Table : public QTableWidget
  9. {
  10.     Q_OBJECT
  11. public:
  12.     Table(QWidget *parent = 0)
  13.         : QTableWidget(6, 3, parent)
  14.     {
  15.         SETPERSON(0, "Jerry", "Springer", 1000000);
  16.         SETPERSON(1, "Foo", "Springer", 12341);
  17.         SETPERSON(2, "John", "Wayne", 12341);
  18.         SETPERSON(3, "Bob", "Carver", 80000);
  19.         SETPERSON(4, "Bob", "Carver", 81000);
  20.         SETPERSON(5, "Bob", "Ulong", 60000);
  21.         updateSortOrder();
  22.         connect(horizontalHeader(), SIGNAL(sectionClicked(int)),
  23.                 this, SLOT(onHeaderClicked(int)));
  24.         disconnect(horizontalHeader(), SIGNAL(sectionPressed(int)), this, SLOT(selectColumn(int)));
  25.  
  26.     }
  27.  
  28.     void updateSortOrder()
  29.     {
  30.         QStringList list;
  31.         list << "First name" << "Last name" << "Salary";
  32.         for (int i=0; i<sortOrder.size(); ++i)
  33.             list[sortOrder.at(i).column].append("(" + QString::number(i + 1) + ")");
  34.         setHorizontalHeaderLabels(list);
  35.         for (int i=sortOrder.size() - 1; i>=0; --i) {
  36.             sortItems(sortOrder.at(i).column, sortOrder.at(i).ascending ? Qt::AscendingOrder : Qt::DescendingOrder);
  37.         }
  38.     }
  39. public slots:
  40.     void onHeaderClicked(int section)
  41.     {
  42.         bool ascending = true;
  43.         if (!(QApplication::keyboardModifiers() & Qt::ControlModifier) || sortOrder.isEmpty()) {
  44.             if (!sortOrder.isEmpty() && sortOrder.first().column == section) {
  45.                 ascending = !sortOrder.first().ascending;
  46.             }
  47.             sortOrder.clear();
  48.         } else {
  49.             const int index = findSection(section);
  50.             if (index != -1) {
  51.                 if (index == sortOrder.size() - 1) {
  52.                     ascending = !sortOrder.last().ascending;
  53.                 }
  54.                 sortOrder.removeAt(index);
  55.             }
  56.     }
  57.         sortOrder.append(SortData(section, ascending));
  58.         updateSortOrder();
  59.     }
  60. private:
  61.     int findSection(int section) const
  62.     {
  63.         for (int i=0; i<sortOrder.size(); ++i) {
  64.             if (sortOrder.at(i).column == section)
  65.                 return i;
  66.         }
  67.         return -1;
  68.     }
  69.     struct SortData {
  70.         SortData(int sec = -1, bool asc = true) : column(sec), ascending(asc) {}
  71.  
  72.         int column;
  73.         bool ascending;
  74.     };
  75.     QList<SortData> sortOrder;
  76. };
  77.  
  78. #include "main.moc"
  79.  
  80. int main(int argc, char **argv)
  81. {
  82.     QApplication a(argc, argv);
  83.     Table w;
  84.     w.show();
  85.     return a.exec();
  86. }
  87.  

3 comments

June 11, 2014

Picture of Eko S. Wibowo Eko S. Wibowo

Lab Rat

Is this actually work? I have try the logic of this code (I am using PyQt btw), and I don’t think it works. By seeing this line of code:

for (int i=sortOrder.size() – 1; i>=0; —i) { sortItems(sortOrder.at(i).column, sortOrder.at(i).ascending ? Qt::AscendingOrder : Qt::DescendingOrder);

I realize that it will act the same with sort on series of columns, serially.
So, the effect of multi columns sort is not there.

.. I guess I must use QTableView for this.. owh no…

June 18, 2014

Picture of rherzog rherzog

Lab Rat

Hi,

Use

  1. self._form.tw_externalFolders.setSortingEnabled(True)
to enable sorting for the user.

Now the user can sort each column. If the users then sorts a column the sorting order the previously sorted column is ratained.This is called stable sorting – I’ve just tried it.

I don’t know if the sorting is also stable if you set the column sorting order manually by using:

  1. self._form.tw_externalFolders.sortItems(0); self._form.tw_externalFolders.sortItems(1)

June 18, 2014

Picture of rherzog rherzog

Lab Rat

The stable sort also work programmatically. For example if you want to sort by lastname and then by firstname you first have to sort by firstname and then by lastname.

Write a comment

Sorry, you must be logged in to post a comment.