从过滤后的 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
,但如果您想要获得一个项目你必须有一个属于 AssetModel
的 QModelIndex
,即属于源模型,为此你必须使用代理模型的 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)
当我 运行 我的工具并双击它时,它会将名称打印到控制台,在本例中,它会打印 '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
,但如果您想要获得一个项目你必须有一个属于 AssetModel
的 QModelIndex
,即属于源模型,为此你必须使用代理模型的 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)