从筛选的QListView中获取所选项目

时间:2018-01-30 13:03:08

标签: python pyside qlistview qsortfilterproxymodel qabstractlistmodel

当我运行我的工具并双击项目时,它会将名称输出到控制台,在这种情况下,它会打印“注释”。但是,如果我在搜索栏中输入,过滤掉列表,然后再次双击该图像,则会返回错误的名称。我不知道我在哪里错了。以下是整个应用程序的代码。

enter image description here

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

在我的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()

1 个答案:

答案 0 :(得分:1)

当使用方法selectionModel().selectedRows()选择QModelIndex时,这将返回关于为视图建立的模型的索引,在您的情况下为SortedModel,但如果您想获取项目,必须有QModelIndex属于AssetModel,即属于源模型,为此您必须使用代理模型的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)