从过滤后的 QListView 中获取选定的项目

Get selected item from filtered QListView

当我 运行 我的工具并双击它时,它会将名称打印到控制台,在本例中,它会打印 'comments'。但是,如果我在过滤掉列表的搜索栏中键入内容,然后再次双击该图像,return 显示我输入了错误的名称。我不确定我哪里出错了。以下是整个应用程序的代码。

要进行测试,只需将代码末尾的文件夹路径更改为计算机上包含一些 jpg 的本地文件夹。我发现这是一个非常简单的应用程序,在列表视图上只有一个搜索过滤器。我很困惑为什么它会 return 在错误的项目上。我猜这与我如何检索选择有关。

在我的 QAbstractListModel 上,我有一个传递选择的方法

def getSelectedItems(self, selection):
    objs = []
    for i, index in enumerate(selection):
        item = self.getItem(index)
        objs.append(item)
    return objs

完整代码

import sys
import os
from PySide import QtGui, QtCore
from PySide import QtGui as QtWidgets


class AssetItem(object):
    def __init__(self, filepath):
        self._name = ''
        self._filepath = ''
        self.filepath = filepath

    @property
    def filepath(self):
        return self._filepath

    @filepath.setter
    def filepath(self, value):
        self._filepath = value
        self._name, self._extension = os.path.splitext(os.path.basename(self.filepath))

    @property
    def name(self):
        return self._name


class AssetModel(QtCore.QAbstractListModel):
    NameRole = QtCore.Qt.UserRole + 1

    def __init__(self, *args, **kwargs):
        QtCore.QAbstractListModel.__init__(self, *args, **kwargs)
        self._items = []

    def rowCount(self, index=QtCore.QModelIndex()):
        return len(self._items)

    def addItem(self, assetItem):
        self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
        self._items.append(assetItem)
        self.endInsertRows()

    def getItem(self, index):
        row = index.row()
        if index.isValid() and 0 <= row < self.rowCount():
            return self._items[row]

    def getSelectedItems(self, selection):
        objs = []
        for i, index in enumerate(selection):
            item = self.getItem(index)
            objs.append(item)
        return objs

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None
        if 0 <= index.row() < self.rowCount():
            item = self._items[index.row()]
            if role == AssetModel.NameRole:
                return item.name
            elif role == QtCore.Qt.TextAlignmentRole:
                return QtCore.Qt.AlignCenter
            elif role == QtCore.Qt.ToolTipRole:
                return item.name
            elif role == QtCore.Qt.DisplayRole:
                return item.name


class SortedModel(QtGui.QSortFilterProxyModel):
    def __init__(self, *args, **kwargs):
        super(SortedModel, self).__init__(*args, **kwargs)
        self._patterns = {}

    def set_pattern(self, role, value):
        self._patterns[role] = value

    def filterAcceptsRow(self, sourceRow, sourceParent):
        sm = self.sourceModel()
        ix = sm.index(sourceRow)
        if ix.isValid():
            val = True
            for role, fvalue in self._patterns.items():
                value = ix.data(role)
                val = val and self.filter(value, fvalue, role)
            return val
        return False

    @staticmethod
    def filter(value, fvalue, role):
        '''
        fvalue: search value
        value: properties value being tested
        '''
        if role == AssetModel.NameRole:
            if fvalue == []:
                return True
            else:
                # change all to any for expanded search
                return all(any(x in y for y in value) for x in fvalue)
        else:
            return False


class ShopWidget(QtWidgets.QWidget):
    def __init__(self,parent=None, path=None):
        super(ShopWidget, self).__init__(parent)
        self.TITLE = 'Shop'
        self.VERSION = '1.0.0' # MAJOR.MINOR.PATCH
        self.setWindowTitle(self.TITLE + ' | ' + self.VERSION)
        self.resize(1000,700)

        # properties
        self.path = path

        # controls
        self.ui_search_bar = QtWidgets.QLineEdit()
        self.ui_search_bar.setPlaceholderText('Search...')

        self.ui_asset_list = QtWidgets.QListView()
        self.ui_asset_list.setViewMode(QtWidgets.QListView.IconMode)
        self.ui_asset_list.setResizeMode(QtWidgets.QListView.Adjust)
        self.ui_asset_list.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.ui_asset_list.setIconSize(QtCore.QSize(256, 256))
        self.ui_asset_list.setMovement(QtWidgets.QListView.Static)
        self.ui_asset_list.setSpacing(10)
        self.ui_asset_list.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        self.ui_asset_selection_model = self.ui_asset_list.selectionModel()
        self.ui_asset_list.setIconSize(QtCore.QSize(128,128))

        self.sorted_model = SortedModel()
        self.sorted_model.setSourceModel(AssetModel())
        self.ui_asset_list.setModel(self.sorted_model)

        # layout
        search_layout = QtWidgets.QHBoxLayout()
        search_layout.addWidget(self.ui_search_bar)

        main_layout = QtWidgets.QVBoxLayout()
        main_layout.addLayout(search_layout)
        main_layout.addWidget(self.ui_asset_list)

        self.setLayout(main_layout)

        # connections
        self.ui_search_bar.textChanged.connect(self.search_value_changed)
        self.ui_search_bar.keyPressEvent = self.search_bar_key_event
        self.ui_asset_list.doubleClicked.connect(self.on_item_double_clicked)

        # constructor
        self.path = path
        self.populate_asset_list()


    # methods
    def populate_asset_list(self):
        extensions = ['.jpg']
        directory = self.path

        if not os.path.isdir(directory):
            return

        for root, subdirs, files in os.walk(directory):
            for f in files:
                filepath = os.path.join(root, f)
                if not os.path.isfile(filepath):
                    continue
                if not os.path.splitext(filepath)[-1] in extensions:
                    continue
                self.ui_asset_list.model().sourceModel().addItem(AssetItem(filepath))


    def search_bar_key_event(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.ui_search_bar.clear()
        QtWidgets.QLineEdit.keyPressEvent(self.ui_search_bar, event)


    def search_value_changed(self, text):
        filters = filter(None, text.lower().split(' '))
        model = self.ui_asset_list.model()
        model.set_pattern(AssetModel.NameRole, filters)
        model.invalidateFilter()


    def import_assets(self):
        selection = self.ui_asset_list.selectionModel().selectedRows()
        assets = self.ui_asset_list.model().sourceModel().getSelectedItems(selection)

        for x in assets:
            print x.name


    # actions
    def on_item_double_clicked(self, index):
        self.import_assets()


# Main
# -----------------------------------------------------------------------------
def main():
    app = QtWidgets.QApplication(sys.argv)
    ex = ShopWidget(path='C:/Users/jmartini/Desktop/Temp/images')
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

当使用 selectionModel().selectedRows() 方法选择 QModelIndex 时,此 returns 索引与为视图建立的模型有关,在您的情况下为 SortedModel,但如果您想要获得一个项目你必须有一个属于 AssetModelQModelIndex,即属于源模型,为此你必须使用代理模型的 mapToSource

def import_assets(self):
    selection = self.ui_asset_list.selectionModel().selectedRows()
    selection_x = [self.ui_asset_list.model().mapToSource(index) for index in selection]
    assets = self.ui_asset_list.model().sourceModel().getSelectedItems(selection_x)

    for x in assets:
        print(x.name)