首页 > 解决方案 > PyQt5 从模型中获取数据以在 QListView 中显示自定义小部件

问题描述

我正在使用 PyQt5 并尝试使用一些自定义数据结构 ( Recipe) 的列表创建一个 GUI,并且我将自定义小部件定义为QWidget描述它应该如何显示的子类。我正在尝试使用 MVC,所以我有一个QListView子类QAbstractListModel。我希望列表包含我的自定义小部件而不仅仅是文本,因此我还为其定义了一个返回该小部件的项目委托。也许我对这三个组件(模型、项目委托、视图)如何协同工作的理解存在缺陷,因为我不确定如何访问data()模型方法返回的任何内容并将其转换为我想要的特定小部件显示在该列表单元格中。

示例代码:

class Recipe:
    pass # custom data structure

class RecipeWidget(QWidget, RecipeWidgetUI):
    def __init__(self, recipe, *args, **kwargs):
        super(RecipeWidget, self).__init__(*args, **kwargs)
        self.recipe = recipe
        self.setupUi(self)
        # some code here to populate the UI elements with data from self.recipe

class RecipeListModel(QAbstractListModel):
    def __init__(self, recipes, *args, **kwargs):
        super(RecipeListModel, self).__init__(*args, **kwargs)
        self.recipes = recipes
    def rowCount(self, index):
        return len(self.recipes)
    def data(self, index, role):
        if role == Qt.DisplayRole:
            return str(self.recipes[index.row()]) # this is the part I'm confused about

class RecipeItemDelegate(QItemDelegate):
    def __init__(self, parent):
        QItemDelegate.__init__(self, parent)

    def createEditor(self, parent, option, index):
        return RecipeWidget(recipe, parent=parent) # where do I get recipe from??

class MainWindow(...):
    def __init__(self, ...):
        ...
        self.model = RecipeListModel(recipes)
        self.listView.setModel(self.model)
        self.listView.setItemDelegate(RecipeItemDelegate(self.listView))

我知道data(...)模型中的函数应该返回一个字符串;我不明白的是:

标签: pythonuser-interfacemodel-view-controllerpyqt5

解决方案


  1. model.data() 需要为所需角色提供数据。在您的情况下,您可能决定使用 Qt.DisplayRole 来表示数据以供显示。你可以return self.recipes[index.row()]。但是如果要支持拖放,QT需要能够序列化数据。现在让我们在 self.recipes 中提供 python 索引,它可以被序列化。return index.row() # which happens to be python index in the self.recipes
  2. RecipeItemDelegate 需要能够使用来自 model.data() 的数据引用 Recipe 对象。所以,让我们初始化 RecipeItemDelegate(parent, parent.model().recipes)
  3. RecipeItemDelegate.paint(self,painter, option, index) 应被实施以绘制“显示”。在paint() 内部,index.data(Qt.DisplayRole) 获取配方中的数据行(恰好是index.row())。self.reference_to_recipes[index.data(Qt.DisplayRole)] 获取 Recipe 对象。然后你拿起“painter”,从你得到的数据中绘制信息,在“option”中指定区域和样式。另请参阅 QT 文档中的 QItemDelegate::paint。

如果您希望代理支持编辑,则:

  1. createEditor() 应该创建用于更改模型数据的小部件
  2. setEditorData() 应该使用要操作的数据填充小部件。喜欢editor.set_recipe(self.reference_to_recipes[index.data(Qt.DisplayRole)])
  3. updateEditorGeometry() 确保编辑器相对于项目视图正确显示。
  4. setModelData() 将更新的数据返回给模型。当然, model.setData model.setData(index, editor.get_modified_recipe(), Qt.EditRole)() 需要能够在 Qt.EditRole 上处理 Recipe 对象

推荐阅读