使用QDataWidgetMapper时如何正确提交数据?
How to properly submit data when using QDataWidgetMapper?
我正在使用 QAbstractTableModel 的子类自定义模型,我的数据是 数据类 .
的列表
我设置了一个简单的 GUI,其中包含一个 QListView 和两个 QLineEdits,如下所示:
import sys
import dataclasses
from typing import List, Any
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
@dataclasses.dataclass()
class StorageItem:
field1: str = 'Item °1'
field2: int = 42
class StorageModel(QAbstractTableModel):
def __init__(self, parent=None):
super().__init__(parent)
self._data: List[StorageItem] = [StorageItem()]
def data(self, index: QModelIndex, role: int = ...) -> Any:
if not index.isValid():
return
item = self._data[index.row()]
col = index.column()
if role in {Qt.DisplayRole, Qt.EditRole}:
if col == 0:
return item.field1
elif col == 1:
return item.field2
else:
return None
def setData(self, index: QModelIndex, value, role: int = ...) -> bool:
print('dataChanged')
if not index.isValid() or role != Qt.EditRole:
return False
item = self._data[index.row()]
col = index.column()
if col == 0:
item.field1 = value
elif col == 1:
item.field2 = value
self.dataChanged.emit(index, index)
return True
def flags(self, index: QModelIndex) -> Qt.ItemFlags:
return Qt.ItemFlags(
Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
)
def rowCount(self, parent=None) -> int:
return len(self._data)
def columnCount(self, parent=None) -> int:
return len(dataclasses.fields(StorageItem))
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
cent_widget = QWidget()
self.setCentralWidget(cent_widget)
# Vertical Layout
v_layout = QVBoxLayout()
v_layout.setContentsMargins(10, 10, 10, 10)
self.model = StorageModel()
# Listview
self.listview = QListView()
self.listview.setModel(self.model)
v_layout.addWidget(self.listview)
# Horizontal Layout
h_layout = QHBoxLayout()
h_layout.setContentsMargins(*[0]*4)
self.field1 = QLineEdit()
h_layout.addWidget(self.field1)
self.field2 = QLineEdit()
h_layout.addWidget(self.field2)
v_layout.addLayout(h_layout)
cent_widget.setLayout(v_layout)
# Set Mapping
mapper = QDataWidgetMapper()
mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)
mapper.setModel(self.model)
mapper.addMapping(self.field1, 0)
mapper.addMapping(self.field2, 1)
mapper.toFirst()
# self.field1.textChanged.connect(lambda: mapper.submit())
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
if __name__ == '__main__':
main()
我正在努力实现,每当我更改第一个 QLineEdit 的内容时,列表视图也会更新。
通过阅读 QDataWidgetMapper 的 documentation 我知道模型 应该 在当前小部件失去焦点时更新,但事实并非如此.无论我在编辑字段中输入什么,都不会调用模型的 setData 方法。
即使我在列表视图中编辑项目,行编辑的内容也不会改变。
我发现,当我将文本字段的 textChanged 信号连接到映射器的 submit 方法时,一切正常,但是dataChanged-方法调用了3次,不明白为什么。
更奇怪的是,现在只要我在列表视图中编辑项目,文本字段的内容就会更新,尽管连接到 textChanged 信号(至少我这么认为)只是一个 -方式连接。
我做错了什么?我显然遗漏了一些东西,因为如果这是正确的方法,QDataWidgetMapper 的 SubmitPolicy 将完全没用。
您的映射器会在 __init__
returns 后立即被删除,因为它没有永久引用。这是 PyQt 中 Qt 对象的常见错误,通常是由于即使没有 python 引用,添加到父级或布局的小部件也会持久化,但事实是将小部件添加到布局实际上会创建持久引用(父小部件采用 “所有权”,在 Qt 术语中),从而防止垃圾收集。
只需将其设为实例成员或添加父参数即可:
self.mapper = QDataWidgetMapper()
# alternatively (which is "safer" from the Qt point of view):
mapper = QDataWidgetMapper(self)
# but since you will probably need further access in any case:
self.mapper = QDataWidgetMapper(self)
我正在使用 QAbstractTableModel 的子类自定义模型,我的数据是 数据类 .
的列表我设置了一个简单的 GUI,其中包含一个 QListView 和两个 QLineEdits,如下所示:
import sys
import dataclasses
from typing import List, Any
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
@dataclasses.dataclass()
class StorageItem:
field1: str = 'Item °1'
field2: int = 42
class StorageModel(QAbstractTableModel):
def __init__(self, parent=None):
super().__init__(parent)
self._data: List[StorageItem] = [StorageItem()]
def data(self, index: QModelIndex, role: int = ...) -> Any:
if not index.isValid():
return
item = self._data[index.row()]
col = index.column()
if role in {Qt.DisplayRole, Qt.EditRole}:
if col == 0:
return item.field1
elif col == 1:
return item.field2
else:
return None
def setData(self, index: QModelIndex, value, role: int = ...) -> bool:
print('dataChanged')
if not index.isValid() or role != Qt.EditRole:
return False
item = self._data[index.row()]
col = index.column()
if col == 0:
item.field1 = value
elif col == 1:
item.field2 = value
self.dataChanged.emit(index, index)
return True
def flags(self, index: QModelIndex) -> Qt.ItemFlags:
return Qt.ItemFlags(
Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
)
def rowCount(self, parent=None) -> int:
return len(self._data)
def columnCount(self, parent=None) -> int:
return len(dataclasses.fields(StorageItem))
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
cent_widget = QWidget()
self.setCentralWidget(cent_widget)
# Vertical Layout
v_layout = QVBoxLayout()
v_layout.setContentsMargins(10, 10, 10, 10)
self.model = StorageModel()
# Listview
self.listview = QListView()
self.listview.setModel(self.model)
v_layout.addWidget(self.listview)
# Horizontal Layout
h_layout = QHBoxLayout()
h_layout.setContentsMargins(*[0]*4)
self.field1 = QLineEdit()
h_layout.addWidget(self.field1)
self.field2 = QLineEdit()
h_layout.addWidget(self.field2)
v_layout.addLayout(h_layout)
cent_widget.setLayout(v_layout)
# Set Mapping
mapper = QDataWidgetMapper()
mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)
mapper.setModel(self.model)
mapper.addMapping(self.field1, 0)
mapper.addMapping(self.field2, 1)
mapper.toFirst()
# self.field1.textChanged.connect(lambda: mapper.submit())
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
if __name__ == '__main__':
main()
我正在努力实现,每当我更改第一个 QLineEdit 的内容时,列表视图也会更新。
通过阅读 QDataWidgetMapper 的 documentation 我知道模型 应该 在当前小部件失去焦点时更新,但事实并非如此.无论我在编辑字段中输入什么,都不会调用模型的 setData 方法。
即使我在列表视图中编辑项目,行编辑的内容也不会改变。
我发现,当我将文本字段的 textChanged 信号连接到映射器的 submit 方法时,一切正常,但是dataChanged-方法调用了3次,不明白为什么。
更奇怪的是,现在只要我在列表视图中编辑项目,文本字段的内容就会更新,尽管连接到 textChanged 信号(至少我这么认为)只是一个 -方式连接。
我做错了什么?我显然遗漏了一些东西,因为如果这是正确的方法,QDataWidgetMapper 的 SubmitPolicy 将完全没用。
您的映射器会在 __init__
returns 后立即被删除,因为它没有永久引用。这是 PyQt 中 Qt 对象的常见错误,通常是由于即使没有 python 引用,添加到父级或布局的小部件也会持久化,但事实是将小部件添加到布局实际上会创建持久引用(父小部件采用 “所有权”,在 Qt 术语中),从而防止垃圾收集。
只需将其设为实例成员或添加父参数即可:
self.mapper = QDataWidgetMapper()
# alternatively (which is "safer" from the Qt point of view):
mapper = QDataWidgetMapper(self)
# but since you will probably need further access in any case:
self.mapper = QDataWidgetMapper(self)