mvc(QSortFilterProxyModel)中的自定义排序仅适用一次

Posted

技术标签:

【中文标题】mvc(QSortFilterProxyModel)中的自定义排序仅适用一次【英文标题】:custom sorting in mvc (QSortFilterProxyModel) only works once 【发布时间】:2012-06-26 07:22:39 【问题描述】:

我正在尝试在 PySide 中为我的模型/视图结构设置自定义排序行为。 所有项目都是字典,我需要按它们的键进行过滤(我不是在查看这些字典的表格视图,而是需要将它们分别呈现为一个实体)。

我继承自 QSortFilterProxyModel 并重新实现了 lessThan 方法。第一次更改排序小部件时它工作正常(触发代理的 sort() 方法),但之后不再调用 lessThan 方法。我不知道为什么,希望在这里得到一些帮助。如果这是解决方案所在,我非常乐意考虑如何以不同方式解决此问题的任何建议。

这是我的代理:

class ProxyModel ( QSortFilterProxyModel ):
    def __init__( self, parent=None ):
        super( ProxyModel, self).__init__( parent )
        self.setFilterCaseSensitivity( Qt.CaseInsensitive )
        self.setSortCaseSensitivity( Qt.CaseInsensitive )
        self.setDynamicSortFilter( True )

    def sortBy( self, attr ):
        print 'sorting by', attr
        self.__sortBy = attr
        self.sort( 0, Qt.AscendingOrder ) # THIS DOES NOT GET CALLED WHEN THE COMBO BOX CHANGES A SECOND TIME

    def lessThan( self, left, right ):
        '''Custom sorting behaviour'''
        leftTool = ( self.sourceModel().itemFromIndex( left ) )
        rightTool = ( self.sourceModel().itemFromIndex( right ) )
        leftData = leftTool.data()[ self.__sortBy ]
        rightData = rightTool.data()[ self.__sortBy ]
        return leftData < rightData

这是完整的测试代码: 导入系统 从 PySide.QtGui 导入 * 从 PySide.QtCore 导入 *

class MainWidget( QWidget ) :
    def __init__( self, parent=None ):
        super( MainWidget, self ).__init__()

        self.listView = MyListView()
        model = MyModel()

        # MODELS AND VIEWS
        self.proxyModel = ProxyModel()
        self.proxyModel.setSourceModel( model )
        self.listView.setModel(self.proxyModel)

        # LAYOUTS
        verticalLayout = QVBoxLayout()
        filterLayout = QHBoxLayout()

        # SORTING WIDGET
        sortLayout = QHBoxLayout()
        sortLabel = QLabel( 'sort:' )
        self.sortWidget = QComboBox()
        self.sortWidget.addItems( ['title', 'author', 'downloads'] )
        self.sortWidget.currentIndexChanged.connect( self.sortTools )

        sortLayout.addWidget( sortLabel )
        sortLayout.addWidget( self.sortWidget )

        verticalLayout.addLayout( filterLayout )
        verticalLayout.addLayout( sortLayout )
        verticalLayout.insertWidget(0, self.listView)

        self.setLayout( verticalLayout )

    def sortTools( self ):
        text = self.sortWidget.currentText()
        self.proxyModel.sortBy( text )

class ProxyModel ( QSortFilterProxyModel ):
    def __init__( self, parent=None ):
        super( ProxyModel, self).__init__( parent )
        self.setFilterCaseSensitivity( Qt.CaseInsensitive )
        self.setSortCaseSensitivity( Qt.CaseInsensitive )
        self.setDynamicSortFilter( True )

    def sortBy( self, attr ):
        print 'sorting by', attr
        self.__sortBy = attr
        self.sort( 0, Qt.AscendingOrder ) # THIS DOES NOT GET CALLED WHEN THE COMBO BOX CHANGES A SECOND TIME

    def lessThan( self, left, right ):
        '''Custom sorting behaviour'''
        leftTool = ( self.sourceModel().itemFromIndex( left ) )
        rightTool = ( self.sourceModel().itemFromIndex( right ) )
        leftData = leftTool.data()[ self.__sortBy ]
        rightData = rightTool.data()[ self.__sortBy ]
        return leftData < rightData


class MyListView( QListView ):
    def __init__( self, parent=None ):
        super( MyListView, self).__init__( parent )
        self.setEditTriggers( QListView.NoEditTriggers )
        self.setViewMode( QListView.IconMode )
        self.setMovement( QListView.Static )
        self.setResizeMode( QListView.Adjust )
        self.setDragEnabled( True )


class MyModel( QStandardItemModel ):
    def __init__( self, parent=None ):
        super( MyModel, self).__init__( parent )
        self.init_data()

    def init_data(self):
        row = 0
        toolData = [ 'title':'ToolA', 'author':'John Doe', 'downloads':123, 'category':'color',
                     'title':'ToolB', 'author':'me', 'downloads':13, 'category':'color',
                     'title':'ToolC', 'author':'you', 'downloads':321, 'category':'transform',
                     'title':'ToolD', 'author':'unknown', 'downloads':2, 'category':'transform']

        for tool in toolData:
            item = QStandardItem( '%(title)s by %(author)s (%(category)s) - %(downloads)s downloads' % tool )
            item.setData( tool )
            self.setItem( row, 0, item )
            row += 1




if __name__ == '__main__':
    app = QApplication( sys.argv )

    mainWidget = MainWidget()
    mainWidget.resize( 400, 400 )
    mainWidget.show()

    sys.exit( app.exec_() )

【问题讨论】:

更改组合框值后尝试使用model.invalidate() 【参考方案1】:

基本上Qt“认为”你的代理已经排序。准确地说,它在 C++ 中检查是否:

(d->dynamic_sortfilter && 
 d->proxy_sort_column == column && 
 d->sort_order == order)

因此,要解决您的问题,您可以将 dynamic_sortfilter 设置为 False(它会产生副作用),或者更好的是,使您的排序无效:

def sortBy( self, attr ):
    print 'sorting by', attr
    self.__sortBy = attr
    self.invalidate() #invalidate helps 
    self.sort( 0, Qt.AscendingOrder )

【讨论】:

以上是关于mvc(QSortFilterProxyModel)中的自定义排序仅适用一次的主要内容,如果未能解决你的问题,请参考以下文章

QSortFilterProxyModel 仅对获取的数据进行排序

链式 QSortFilterProxyModel

QSortFilterProxyModel 不更新 QTableview

QSortFilterProxyModel 打破 columnWidths

QSortFilterProxyModel.mapToSource 崩溃。没有信息为啥

为视图创建多个 QSortFilterProxyModel 实例