Russian Qt Forum
Ноябрь 01, 2024, 04:27 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Сортировка в QSortFilterProxyModel по нскольким столбцам  (Прочитано 5669 раз)
Yegor
Гость
« : Март 11, 2013, 20:01 »

Здравствуйте!

Имею модель QSortFilterProxyModel. Нужно сортировать данные стразу по нескольким столбцам.
Включаю свойство dynamicSortFilter (setDynamicSortFilter(TRUE)).
В классе есть метод sort.

Сортирую сначала по первому столбцу: sort(0, Qt::AscendingOrder). Данные отсортировались.
Далее сортирую по второму столбцу: sort(1, Qt::AscendingOrder). При этом сортировка по первому столбцу сбросилась, и произошла сортирока по второму столбцу.

То есть фактически сортировка сразу по нескольким столбцам не получается, сортируется одновременно только по одному столбцу.

Подскажите, пожалуйста, где копать.
Записан
twp
Гость
« Ответ #1 : Март 11, 2013, 21:42 »

сортировка по 2-му столбца должна происходить только тогда, когда значения первого сортируемого столбца равны
Записан
derov
Гость
« Ответ #2 : Март 30, 2013, 01:08 »

Работающий, немного избыточный код (извини) приведён ниже.
Основная идея в следующем:
Представь, что в каждой ячейке строки записано по одной цифре от 0 до 9.
Тогда каждая строка таблицы соответствует какому либо числу и сортировка строк сводится к сортировке чисел.
Для сортировки единственное, что нам надо, так это указать для любых двух строк их порядок
(функция def lessThan(self, leftIndex, rightIndex)). Для простоты реализации будем работать в двоичной системе представления.   

Делаем следующее:
для двух произвольных строк сравниваем их ячейки принадлежащие одному столбцу
и определяем какая из ячеек больше (меньше) и записываем в ту ячейку либо 1 либо 0 (соответственно в другую ячейку записываем либо 0 либо 1). Если значения в ячейках равны то в обе ячейки записываем по 1.
Код
Python
       leftRow  = sourceData[leftIndex.row() ][leftIndex.column(): ]
       rightRow = sourceData[rightIndex.row()][rightIndex.column():]
 
       xLeft  = map( op.truth, map( op.le , leftRow, rightRow))
       xRight = map( op.truth, map( op.ge , leftRow, rightRow))
 
соответственно интерпретируя полученные 0 и 1 как двоичную запись числа соответствующего строке
для определения порядка строк вычисляем значения чисел и сравниваем их величины
Код
Python
       return reduce(lambda x, y: 2*x+y, xLeft) <  reduce(lambda x, y: 2*x+y, xRight)
 

Код
Python
#!/usr/bin/env python
## -*- coding: utf-8 -*-
 
import sys
import operator as op
from PyQt4 import QtGui, QtCore
 
class TableModel (QtCore.QAbstractTableModel):
   def __init__ (self, data, header, parent=None):
       QtCore.QAbstractTableModel.__init__(self, parent)
       self.data = data
       self.header = header
 
   def rowCount (self, parent=QtCore.QModelIndex()):
       return len(self.data)
 
   def columnCount (self, parent = QtCore.QModelIndex()):
       if len(self.data) != 0:
           return len(self.data[0])
       else:
           return 0
 
   def data (self, index, role = QtCore.Qt.DisplayRole):
       if index.isValid() or not 0 <= index.row() < self.rowCount():
           data = self.data[index.row()][index.column()]
 
       if role == QtCore.Qt.DisplayRole:
           return str(data)
       elif role == QtCore.Qt.EditRole:
           return str(data)
 
       return QtCore.QVariant()
 
   def flags (self, index):
       return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
 
   def setData (self, index, value, role):
       if index.isValid() and role == QtCore.Qt.EditRole:
           value_int, ok = value.toInt()
           if ok:
               self.data[index.row()][index.column()] = value_int
               self.dataChanged.emit(index, index)
               return True
           return False
 
   def sort(self, Ncol, order): # Не пременимо для QSortFilterProxyModel
       """Sort table by given column number.
       """

       self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()"))
 
       self.data = sorted(self.data, key = lambda x: x[Ncol:] )
       if order == QtCore.Qt.DescendingOrder:
           self.data.reverse()
 
       self.emit(QtCore.SIGNAL("layoutChanged()"))
 
   def headerData(self, section, orientation, role):
       if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole and section < len(self.header):
           return QtCore.QVariant(self.header[section])
       else:
           return QtCore.QVariant()
 
   def setHeaderData(self, section, orientation, role):
       if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole and section < len(self.header):
           self.emit(QtCore.SIGNAL('headerDataChanged()'))
           return QtCore.QVariant(self.header[section])
       else:
           return QtCore.QVariant()
 
   def insertRows(self, position, rows, parent=QtCore.QModelIndex()):
       self.beginInsertRows(parent, position, position + rows - 1)
       for row in range(0, rows):
           self.data.insert(position + row, self.row_data[row])
       self.endInsertRows()
 
   def removeRows(self, position, rows, parent=QtCore.QModelIndex()):
       self.beginRemoveRows(parent, position, position + rows - 1)
       del self.data[position:position + rows]
       self.endRemoveRows()
 
   def insertColumns(self, position, columns, parent=QtCore.QModelIndex()):
       self.beginInsertColumns(parent, position, position + columns - 1)
       for row in range(0, self.rowCount(parent=QtCore.QModelIndex())):
           for column in range(0, columns):
               self.data[row].insert(position + column, self.column_data[row][column])
       self.endInsertColumns()
 
   def removeColumns(self, position, columns, parent=QtCore.QModelIndex()):
       self.beginRemoveColumns(parent, position, position + columns - 1)
       for row in range(0, self.rowCount(parent=QtCore.QModelIndex())):
           del self.data[row][position:position + columns]
       self.endRemoveColumns()
 
class HeaderTableModel (TableModel):
   def __init__ (self, data, header, parent=None):
       QtCore.QAbstractTableModel.__init__(self, parent)
       self.data = data
       self.header = header
 
   def flags (self, index):
       if not index.isValid():
           return QtCore.Qt.ItemIsEnabled
       elif index.row() > 1:
           return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable
 
       return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
 
   def setData (self, index, value, role):
       if index.isValid() and role == QtCore.Qt.EditRole:
           value_int, ok = value.toInt()
           if ok:
               self.data[index.row()][index.column()] = value_int
               self.dataChanged.emit(index, index)
               self.emit(QtCore.SIGNAL('minmaxValueChange()'))
 
               print "SIGNAL Emited"
               return True
           return False
 
class ProxyModel(QtGui.QSortFilterProxyModel):
   def __init__( self, parent=None ):
       super( ProxyModel, self).__init__( parent )
       self.setDynamicSortFilter( True )
       self.setSortRole(2) # 6 - double
 
   def lessThan(self, leftIndex, rightIndex):
 
       sourceModel = self.sourceModel()
       sourceData = sourceModel.data
 
       leftRow  = sourceData[leftIndex.row() ][leftIndex.column(): ]
       rightRow = sourceData[rightIndex.row()][rightIndex.column():]
 
       xLeft  = map( op.truth, map( op.le , leftRow, rightRow))
       xRight = map( op.truth, map( op.ge , leftRow, rightRow))
 
       return reduce(lambda x, y: 2*x+y, xLeft) <  reduce(lambda x, y: 2*x+y, xRight)
 
   def filterAcceptsRow(self, sourceRow, sourceParent):
       sourceModel = self.sourceModel()
       sourceData = sourceModel.data
 
       xVal = [ sourceData[sourceRow][i] for i in self.list[0] ]
       xMax = self.list[1]
       xMin = self.list[2]
 
       print "filterAcceptsRow", xVal, self.list, op.and_(op.le(xVal, xMax), op.ge(xVal, xMin))
 
       return  op.and_(op.le(xVal, xMax), op.ge(xVal, xMin))
 
 
class MyWindow(QtGui.QWidget):
   def __init__(self, *args):
       QtGui.QWidget.__init__(self, *args)
 
       header =  ['Col0', 'Col1', 'Col2', 'Col3'] # Заголовки столбцов
       data = [
                    [0, 1, 3, 1],
                    [0, 1, 2, 3],
                    [1, 3, 1, 2],
                    [1, 2, 4, 4],
                    [2, 3, 3, 6],
                    [2, 5, 3, 5],
                    [3, 1, 3, 1],
                    [3, 4, 5, 6]
                    ]
 
       sortModel = TableModel( data, header )
       self.connect(sortModel, QtCore.SIGNAL('minmaxValueChange()'), self.myPrint)
 
       proxy = ProxyModel()
       proxy.dataValue = data
       proxy.list = [[0], [ 6 ], [ 0 ]]
       proxy.list = [ [0, 1, 3], [5, 5, 5], [2, 2, 2]]
       proxy.setSourceModel(sortModel)
       proxy.setDynamicSortFilter(True)
 
       sortView = QtGui.QTableView()
       sortView.setModel(proxy)
       sortView.setSortingEnabled(True)
 
       layout = QtGui.QVBoxLayout()
       layout.addWidget(sortView)
 
       self.setLayout(layout)
 
   def myPrint(self):
 
       print "pass the edit of min max value"
 
 
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
 
Записан
GreatSnake
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2921



Просмотр профиля
« Ответ #3 : Март 30, 2013, 11:37 »

сортировка по 2-му столбца должна происходить только тогда, когда значения первого сортируемого столбца равны
Нужно пояснить, что делать всё это нужно в перегруженном
Код
C++ (Qt)
bool QSortFilterProxyModel::lessThan ( const QModelIndex & left, const QModelIndex & right ) const [virtual protected]
Записан

Qt 5.11/4.8.7 (X11/Win)
VPS
Гость
« Ответ #4 : Март 30, 2013, 13:05 »

Если для хранения данных в исходной модели Вы используете свою собственную структуру, то можно в ней определить метод "operator <", где написать что-то типа этого:
Код:
bool operator<(const StructureItem &other) const
{
   return column1 != other.column1 ? column1 < other.column1 : column2 < other.column2;
}
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.12 секунд. Запросов: 23.