我试着弄清楚代理在pyqt中是如何工作的,并写了下面的代码来尝试它们。但是我不明白为什么paint方法看起来不能正确地排列项目中的NameAge小部件。有人能给我建议吗?
import sys
from PyQt5.QtCore import (
Qt, QAbstractListModel, QModelIndex)
from PyQt5.QtWidgets import (
QApplication, QWidget, QListView, QAbstractItemDelegate,
QHBoxLayout, QLineEdit, QSpinBox, QVBoxLayout)
class NameAge(QWidget):
def __init__(self, name='', age=0, parent=None):
super().__init__(parent)
self.name_edit = QLineEdit(name)
self.age_spinbox = QSpinBox()
self.age_spinbox.setValue(age)
layout = QHBoxLayout()
layout.addWidget(self.name_edit)
layout.addWidget(self.age_spinbox)
self.setLayout(layout)
class NameAgeModel(QAbstractListModel):
def __init__(self, data, parent=None):
super().__init__(parent)
self.data = data
def rowCount(self, parent=QModelIndex()):
return len(self.data)
def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return self.data[index.row()]
elif role == Qt.EditRole:
return self.data[index.row()]
def setData(self, index, value, role):
if role == Qt.EditRole:
self.data[index.row()] = value
self.dataChanged.emit(index, index)
return True
return False
def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled
class NameAgeDelegate(QAbstractItemDelegate):
def setEditorData(self, editor, index):
name, age = index.data()
editor.name_edit.setText(name)
editor.age_spinbox.setValue(age)
def setModelData(self, editor, model, index):
name = editor.name_edit.text()
age = editor.age_spinbox.value()
model.setData(index, (name, age), Qt.EditRole)
def createEditor(self, parent, option, index):
name, age = index.data(Qt.DisplayRole)
name_age = NameAge(name, age, self.parent())
name_age.setGeometry(option.rect)
return name_age
def paint(self, painter, option, index):
name, age = index.data(Qt.DisplayRole)
name_age = NameAge(name, age, self.parent())
name_age.setGeometry(option.rect)
name_age.render(painter, option.rect.topLeft())
def sizeHint(self, option, index):
name, age = index.data(Qt.DisplayRole)
name_age = NameAge(name, age, self.parent())
return name_age.sizeHint()
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class MainWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
self.setLayout(layout)
self.list_view = QListView()
self.list_view.setItemDelegate(NameAgeDelegate(self.list_view))
self.list_view.setModel(NameAgeModel(
[('Mark', 38), ('John', 30), ('Jane', 25)]))
layout.addWidget(self.list_view)
if __name__ == '__main__':
app = QApplication(sys.argv)
main_widget = MainWidget()
main_widget.show()
sys.exit(app.exec_())
任何建议将不胜感激抱歉,因为找不到很多人试图这样做的例子。
谢谢,马克
1条答案
按热度按时间knpiaxh11#
具体的问题是,您将
targetOffset
与几何体沿着使用,而您应该只设置大小并平移绘图工具:不幸的是,这只是错误问题的正确解决方案,因为您面临的问题要大得多:你 * 不断地 * 创建
NameAge
的新示例,这是非常错误的,原因有二:1.对
paint()
和sizeHint()
的调用极其频繁,这意味着您将创建数百(或数千)次新的小部件,即使只是用鼠标悬停项目或调整视图大小;1.创建具有父控件意味着只要父控件存在,它们就将存在;有了你的代码,你可以很容易地得到 * 数千 * 个未使用的示例;
代码中的另一个问题是使用了错误的父级
self.parent()
创建编辑器。这是错误的,原因有两个:createEditor()
已经提供了父视图,即视图的viewport(不是视图!);None
作为父对象;None
,则编辑器将显示为新的顶层窗口;现在,避免以上所有取决于您的需要。
我怀疑您只是想让编辑器即使在用户不编辑它的时候也显示出来,所以正确的解决方案是使用一个 * 持久编辑器 *,这可以通过调用
openPersistentEditor()
来实现。对于QListView这样的简单视图,这是非常简单的(QTableView或QTreeView可能需要对每列使用不同的委托):只需确保每次向模型添加新行时视图都打开一个持久编辑器,这可以通过将
rowsInserted
信号连接到调用openPersistentEditor()
的函数来轻松完成。然后,为了获得正确的行为(包括大小提示),您必须基于索引保留编辑器的引用。注意,基本索引(QModelIndex示例)是“volatile”的,正如文档所指出的:
注意:应立即使用模型索引,然后将其丢弃。调用更改模型结构或删除项的模型函数后,不应依赖索引来保持有效。如果需要随时间保留模型索引,请使用QPersistentModelIndex。
因此,为了在索引和编辑器之间有正确的引用,必须在字典中使用QPersistentModelIndex,并最终删除键/值对,以防编辑器被破坏。
还要注意,编辑器可能会更改条目的默认大小提示(特别是在新索引 * 之后 * 创建的条目),为此,必须在创建编辑器时发出
sizeHintChanged
。另一个需要注意的重要方面是QObject支持 *user属性 *,这是任何对象的默认属性,在Qt中使用该属性可以轻松设置对象最重要的“方面”。
例如,QLineEdit将其
text
属性设置为用户1,而QSpinBox使用value
.Qt委托自动设置和获取这些“用户属性”,并在阅读或写入模型时应用它们。如果你为你的编辑器正确地实现了一个qt属性,这会简化一些事情。唯一需要注意的是,属性通常是 basic 对象(所以,不同类型的元组,就像你的例子一样,是不受支持的)。解决方案是只实现需要的,在这个例子中,就是
setModelData()
。