如何使用PySide在QListView中显示多个标签?如下图所示,我想显示图像的名称以及一些不使用粗体文本的图像的其他信息。
为每个图像显示的第二行文本反映了该类对象的属性,称为'标签'这是用于该图像的描述性词语列表。我最终会添加一个lineedit,用户可以在其中添加其他标签到所选的listview项目。
import os,sys
from PySide import QtGui, QtCore
class AssetItem(object):
def __init__(self, filepath):
self._name = ''
self._extension = ''
self._tags = []
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(value))
self.tags = self.name.split('_')
class AssetItemModel(QtCore.QAbstractListModel):
def __init__(self, *args, **kwargs):
QtCore.QAbstractListModel.__init__(self, *args, **kwargs)
self._items = []
self._icons = {}
self._iconSize = QtCore.QSize(96, 96)
def setIconSize(self, size):
self._iconSize = size
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 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 == QtCore.Qt.DecorationRole:
ix_p = QtCore.QPersistentModelIndex(index)
value = self.get_icon(ix_p)
if value is None:
value = QtGui.QIcon(item.filepath)
self._icons[ix_p] = value
return value
elif role == QtCore.Qt.SizeHintRole:
return self._iconSize
elif role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignLeft
elif role == QtCore.Qt.DisplayRole:
return ', '.join(item.tags)
def get_icon(self, ix):
if ix in self._icons.keys():
return self._icons[ix]
class AssetImporterWindow(QtGui.QMainWindow):
def __init__(self):
super(AssetImporterWindow, self).__init__()
self.resize(500, 400)
self.setWindowTitle('Import New Assets')
self.ui_asset_viewer = QtGui.QListView()
self.ui_asset_viewer.setResizeMode(QtGui.QListView.Adjust)
self.ui_asset_viewer.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.ui_asset_viewer.setIconSize(QtCore.QSize(96, 96))
self.ui_asset_viewer.setSpacing(5)
self.ui_asset_viewer.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
# self.ui_asset_viewer.setModel(AssetItemModel())
sorted_model = QtGui.QSortFilterProxyModel()
sorted_model.setSourceModel(AssetItemModel())
self.ui_asset_viewer.setModel(sorted_model)
self.ui_add_tags = QtGui.QPushButton('Add Tags')
self.ui_add_tags.clicked.connect(self.add_tags)
lay = QtGui.QVBoxLayout()
lay.addWidget(self.ui_asset_viewer)
lay.addWidget(self.ui_add_tags)
widget = QtGui.QWidget()
widget.setLayout(lay)
self.setCentralWidget(widget)
self.populate()
def populate(self):
root = 'images/'
for img in os.listdir(root):
path = os.path.abspath(os.path.join(root, img))
item = AssetItem(path)
self.ui_asset_viewer.model().sourceModel().addItem(item)
def add_tags(self):
indexes = self.ui_asset_viewer.selectedIndexes()
for i in indexes:
if i.isValid():
item = self.ui_asset_viewer.model().sourceModel().getItem(i)
item.tags.extend(['Red','Green'])
def main():
app = QtGui.QApplication(sys.argv)
ex = AssetImporterWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
更新01
建议的答案产生这个结果,它做了我想要的一切,但外观有点奇怪。我认为这是因为它使用HTML文档来显示文本。也许富文本框或qlabel会更好:
更新2
这是解决问题的其他随机尝试。我制作了一个自定义小部件,但我不知道我在这里做了什么。这只是一个想法。我不知道我是否正确地做了,也不确定ListWidget是否是一个很好的解决方案。
import sys
from PySide import QtGui, QtCore
class AssetWidget(QtGui.QWidget):
def __init__ (self, parent=None, title='Title', subtitle='Subtitle', icon=None):
super(AssetWidget, self).__init__(parent)
self.resize(300,300)
px = QtGui.QPixmap(64,64)
px.fill(QtGui.QColor(QtCore.Qt.red))
self.ui_icon = QtGui.QLabel('Icon')
self.ui_icon.setPixmap(px)
self.ui_title = QtGui.QLabel(title)
fnt = self.ui_title.font()
fnt.setBold(True)
self.ui_title.setFont(fnt)
self.ui_subtitle = QtGui.QLabel(subtitle)
vertical_lay = QtGui.QVBoxLayout()
vertical_lay.addWidget(self.ui_title)
vertical_lay.addWidget(self.ui_subtitle)
main_layout = QtGui.QHBoxLayout()
main_layout.addWidget(self.ui_icon)
main_layout.addLayout(vertical_lay, QtCore.Qt.AlignLeft)
self.setLayout(main_layout)
class exampleQMainWindow (QtGui.QMainWindow):
def __init__ (self):
super(exampleQMainWindow, self).__init__()
self.resize(300,300)
# Create QListWidget
self.myQListWidget = QtGui.QListWidget()
for i in range(5):
# Create QCustomQWidget
myQCustomQWidget = AssetWidget()
myQListWidgetItem = QtGui.QListWidgetItem(self.myQListWidget)
myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
self.myQListWidget.addItem(myQListWidgetItem)
self.myQListWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)
self.setCentralWidget(self.myQListWidget)
app = QtGui.QApplication([])
window = exampleQMainWindow()
window.show()
sys.exit(app.exec_())
答案 0 :(得分:0)
您必须实现委托,为此我们将使用QTextDocument
。
class AssetDocument(QtGui.QTextDocument):
def __init__(self, title, content, font):
QtGui.QTextDocument.__init__(self)
cursor = QtGui.QTextCursor(self)
fmt = QtGui.QTextCharFormat()
fmt.setFont(font)
cursor.setCharFormat(fmt)
self.setPlainText(title+"\n"+", ".join(content))
cursor.movePosition(QtGui.QTextCursor.Start);
cursor.movePosition(QtGui.QTextCursor.EndOfLine, QtGui.QTextCursor.KeepAnchor)
fmt.setFontWeight(QtGui.QFont.Bold)
cursor.mergeCharFormat(fmt)
class AssetItemDelegate(QtGui.QStyledItemDelegate):
def paint(self, painter, option, index):
options = QtGui.QStyleOptionViewItemV4(option)
self.initStyleOption(options,index)
style = QtGui.QApplication.style() if options.widget is None else options.widget.style()
name = index.data(AssetItemModel.NameRole)
tags = index.data(AssetItemModel.TagRole)
doc = AssetDocument(name, tags, options.font)
style.drawControl(QtGui.QStyle.CE_ItemViewItem, options, painter)
ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, options, options.widget)
painter.save()
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
doc.documentLayout().draw(painter, ctx)
painter.restore()
class AssetItemModel(QtCore.QAbstractListModel):
NameRole, TagRole = range(QtCore.Qt.UserRole, QtCore.Qt.UserRole+2)
[...]
def data(self, index, role=QtCore.Qt.DisplayRole):
[...]
elif role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignLeft
elif role == AssetItemModel.NameRole:
return item.name
elif role == AssetItemModel.TagRole:
return item.tags
[...]
class AssetImporterWindow(QtGui.QMainWindow):
def __init__(self):
[..]
self.ui_asset_viewer = QtGui.QListView()
self.ui_asset_viewer.setItemDelegate(AssetItemDelegate())
[...]
您的代码存在的另一个问题是,在添加新信息时它没有更新,这是因为您没有通知模型存在更改,因此您必须使用dataChanged信号:
def add_tags(self):
indexes = self.ui_asset_viewer.selectedIndexes()
for i in indexes:
if i.isValid():
item = self.ui_asset_viewer.model().sourceModel().getItem(i)
item.tags.extend(['Red','Green'])
self.ui_asset_viewer.model().sourceModel().dataChanged.emit(i, i)
完整代码:
import os,sys
from PySide import QtGui, QtCore
class AssetItem(object):
def __init__(self, filepath):
self._name = ''
self._extension = ''
self._tags = []
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(value))
self.tags = self.name.split('_')
class AssetItemModel(QtCore.QAbstractListModel):
NameRole, TagRole = range(QtCore.Qt.UserRole, QtCore.Qt.UserRole+2)
def __init__(self, *args, **kwargs):
QtCore.QAbstractListModel.__init__(self, *args, **kwargs)
self._items = []
self._icons = {}
self._iconSize = QtCore.QSize(96, 96)
def setIconSize(self, size):
self._iconSize = size
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 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 == QtCore.Qt.DecorationRole:
ix_p = QtCore.QPersistentModelIndex(index)
value = self.get_icon(ix_p)
if value is None:
value = QtGui.QIcon(item.filepath)
self._icons[ix_p] = value
return value
elif role == QtCore.Qt.SizeHintRole:
return self._iconSize
elif role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignLeft
elif role == AssetItemModel.NameRole:
return item.name
elif role == AssetItemModel.TagRole:
return item.tags
def get_icon(self, ix):
if ix in self._icons.keys():
return self._icons[ix]
class AssetDocument(QtGui.QTextDocument):
def __init__(self, title, content, font):
QtGui.QTextDocument.__init__(self)
cursor = QtGui.QTextCursor(self)
fmt = QtGui.QTextCharFormat()
fmt.setFont(font)
cursor.setCharFormat(fmt)
self.setPlainText(title+"\n"+", ".join(content))
cursor.movePosition(QtGui.QTextCursor.Start);
cursor.movePosition(QtGui.QTextCursor.EndOfLine, QtGui.QTextCursor.KeepAnchor)
fmt.setFontWeight(QtGui.QFont.Bold)
cursor.mergeCharFormat(fmt)
class AssetItemDelegate(QtGui.QStyledItemDelegate):
def paint(self, painter, option, index):
options = QtGui.QStyleOptionViewItemV4(option)
self.initStyleOption(options,index)
style = QtGui.QApplication.style() if options.widget is None else options.widget.style()
name = index.data(AssetItemModel.NameRole)
tags = index.data(AssetItemModel.TagRole)
doc = AssetDocument(name, tags, options.font)
style.drawControl(QtGui.QStyle.CE_ItemViewItem, options, painter)
ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, options, options.widget)
painter.save()
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
doc.documentLayout().draw(painter, ctx)
painter.restore()
class AssetImporterWindow(QtGui.QMainWindow):
def __init__(self):
super(AssetImporterWindow, self).__init__()
self.resize(500, 400)
self.setWindowTitle('Import New Assets')
self.ui_asset_viewer = QtGui.QListView()
self.ui_asset_viewer.setItemDelegate(AssetItemDelegate())
self.ui_asset_viewer.setResizeMode(QtGui.QListView.Adjust)
self.ui_asset_viewer.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.ui_asset_viewer.setIconSize(QtCore.QSize(96, 96))
self.ui_asset_viewer.setSpacing(5)
self.ui_asset_viewer.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
# self.ui_asset_viewer.setModel(AssetItemModel())
sorted_model = QtGui.QSortFilterProxyModel()
sorted_model.setSourceModel(AssetItemModel())
self.ui_asset_viewer.setModel(sorted_model)
self.ui_add_tags = QtGui.QPushButton('Add Tags')
self.ui_add_tags.clicked.connect(self.add_tags)
lay = QtGui.QVBoxLayout()
lay.addWidget(self.ui_asset_viewer)
lay.addWidget(self.ui_add_tags)
widget = QtGui.QWidget()
widget.setLayout(lay)
self.setCentralWidget(widget)
self.populate()
def populate(self):
root = '/home/eyllanesc/Pictures/'
for img in os.listdir(root):
path = os.path.abspath(os.path.join(root, img))
item = AssetItem(path)
self.ui_asset_viewer.model().sourceModel().addItem(item)
def add_tags(self):
indexes = self.ui_asset_viewer.selectedIndexes()
for i in indexes:
if i.isValid():
item = self.ui_asset_viewer.model().sourceModel().getItem(i)
item.tags.extend(['Red','Green'])
self.ui_asset_viewer.model().sourceModel().dataChanged.emit(i, i)
def main():
app = QtGui.QApplication(sys.argv)
ex = AssetImporterWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
另一种解决方案:
使用自定义小部件:
class AssetWidget(QtGui.QWidget):
def __init__ (self, title='Title', subtitle='Subtitle', icon=None):
super(AssetWidget, self).__init__()
self.resize(300,300)
if icon:
px = icon.pixmap(64, 64)
else:
px = QtGui.QPixmap(64,64)
px.fill(QtGui.QColor(QtCore.Qt.red))
self.ui_icon = QtGui.QLabel(self)
self.ui_icon.setPixmap(px)
self.ui_title = QtGui.QLabel(title, self)
fnt = self.ui_title.font()
fnt.setBold(True)
self.ui_title.setFont(fnt)
self.ui_subtitle = QtGui.QLabel(subtitle, self)
vertical_lay = QtGui.QVBoxLayout()
vertical_lay.addWidget(self.ui_title)
vertical_lay.addWidget(self.ui_subtitle)
main_layout = QtGui.QHBoxLayout(self)
main_layout.addWidget(self.ui_icon)
main_layout.addLayout(vertical_lay, QtCore.Qt.AlignLeft)
class AssetItemDelegate(QtGui.QStyledItemDelegate):
def paint(self, painter, option, index):
r = option.rect
painter.save()
painter.translate(r.topLeft())
icon = index.data(QtCore.Qt.DecorationRole)
name = index.data(AssetItemModel.NameRole)
tags = index.data(AssetItemModel.TagRole)
widget = AssetWidget(name, ", ".join(tags), icon)
widget.resize(r.size())
widget.render(painter, QtCore.QPoint(), QtGui.QRegion(), QtGui.QWidget.DrawChildren)
painter.restore()