다음 코드에서 알 수 있듯이 항목 ( 메서드 를 QStandardItem
사용하여 하위 클래스로 분류 됨)을 끌어서 놓으면 하위 클래스가 아닌 a가 표시됩니다. 더욱이-클래스 또는 setData의 일부로 저장된 데이터가 손실됩니다. 나는 이것이 데이터를 '직렬화'할 수 없기 때문이라고 생각합니다. 그러나 나는 데이터 또는 메타를 '저장'하는 방법을 알지 못합니다. 어떻게 보존 할 수 있습니까? 다음 코드는 잘 작동하지만 분기 노드를 이동하면 분기와 분기의 모든 노드가 's'가되며 데이터가 손실됩니다 (있는 경우).clone()
QStandardItem
QObject
QStandardItem
myItem
# -*- coding: utf-8 -*-
"""
Created on Mon Nov 4 09:10:16 2019
Test of Tree view with subclassed QStandardItem and Drag and Drop
enabled. When you move a parent the parent looses the subclass and thus
the meta - however, it also looses the data: This is likely because
the data cannot be serialized. How to fix?
@author: tcarnaha
"""
import sys
from PyQt5 import QtGui, QtWidgets, QtCore
class myData():
def __init__(self, title):
self._title = title
self._stuff = dict()
self._obj = QtCore.QObject()
@property
def obj(self):
return self._obj
@obj.setter
def obj(self, value):
self._obj = value
@property
def title(self):
return self._title
@title.setter
def title(self, value):
self._title = value
class myItem(QtGui.QStandardItem):
def __init__(self, parent=None):
super(myItem, self).__init__(parent)
self._meta = None
@property
def meta(self):
return self._meta
@meta.setter
def meta(self, value):
self._meta = value
def clone(self):
print "My cloning"
old_data = self.data()
print "Old data [{}]".format(old_data)
old_meta = self.meta
obj = myItem()
obj.setData(old_data)
print "New data [{}]".format(obj.data())
obj.meta = old_meta
print "Clone is a ", obj.__class__
return obj
class mainWidget(QtWidgets.QMainWindow):
def __init__(self):
super(mainWidget, self).__init__()
self.model = QtGui.QStandardItemModel()
self.model.setItemPrototype(myItem())
self.view = QtWidgets.QTreeView()
self.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.view.customContextMenuRequested.connect(self.list_click)
self.view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
self.view.setDefaultDropAction(QtCore.Qt.MoveAction)
self.view.setDragDropOverwriteMode(False)
self.view.setAcceptDrops(True)
self.view.setDropIndicatorShown(True)
self.view.setDragEnabled(True)
self.view.setModel(self.model)
dataA = myData('A thing')
parentA = myItem()
parentA.setText('A')
parentA.setDragEnabled(True)
parentA.setDropEnabled(True)
parentA.setData(dataA)
parentA.meta = QtCore.QObject()
childa = myItem()
childa.setText('a')
childb = myItem()
childb.setText('b')
childc = myItem()
childc.setText('c')
parentA.appendRows([childa, childb, childc])
dataB = myData('B thing')
parentB = myItem()
parentB.setText('B')
parentB.setDragEnabled(True)
parentB.setDropEnabled(True)
parentB.setData(dataB)
parentB.meta = QtCore.QObject()
childd = myItem()
childd.setText('d')
childe = myItem()
childe.setText('e')
childf = myItem()
childf.setText('f')
parentB.appendRows([childd, childe, childf])
self.model.appendRow(parentA)
self.model.appendRow(parentB)
classAct = QtWidgets.QAction('Class', self)
classAct.triggered.connect(self.classIs)
dataAct = QtWidgets.QAction('Data', self)
dataAct.triggered.connect(self.dataIs)
metaAct = QtWidgets.QAction('Meta', self)
metaAct.triggered.connect(self.metaIs)
self.menu = QtWidgets.QMenu("Item info")
self.menu.addAction(classAct)
self.menu.addAction(dataAct)
self.menu.addAction(metaAct)
self.setCentralWidget(self.view)
@QtCore.pyqtSlot(QtCore.QPoint)
def list_click(self, position):
self.menu.popup(self.view.viewport().mapToGlobal(position))
def classIs(self):
selected_indexes = self.view.selectedIndexes()
for index in selected_indexes:
item = self.model.itemFromIndex(index)
print "Item {} Class {} ".format(item.text(), item.__class__())
def dataIs(self):
selected_indexes = self.view.selectedIndexes()
for index in selected_indexes:
item = self.model.itemFromIndex(index)
try:
print "Item {} data {} Object {}".format(item.text(),
item.data().title,
item.data().obj)
except Exception as exc:
print "Data exception ", exc
def metaIs(self):
selected_indexes = self.view.selectedIndexes()
for index in selected_indexes:
item = self.model.itemFromIndex(index)
try:
print "Item {} meta {} ".format(item.text(), item.meta)
except Exception as exc:
print "Meta exception ", exc
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = mainWidget()
main.show()
app.exec_()
Qt 및 PyQt에 의해 객체가 직렬화되는 방법과 관련된 몇 가지 문제가 있습니다. 첫째,를 복제 할 때 QStandardItem
플래그와 데이터 만 복사되고 나머지는 모두 무시됩니다 (동적 파이썬 속성 포함). 둘째, QObject
. 이것은 QVariant
(Qt가 직렬화에 사용 하는) 캐스트 할 수없고 피클 (PyQt가 직렬화에 사용하는) 할 수 없기 때문입니다.
두 번째 문제를 해결하려면 모든 QObject
인스턴스에 대한 별도의 참조를 유지 한 다음 간접 키를 사용하여 나중에 다시 액세스해야합니다. 이를 달성하는 방법에는 여러 가지가있을 수 있지만 기본 아이디어를 보여주는 매우 간단한 접근 방식이 있습니다.
objects = {}
class MyObject(QtCore.QObject):
def __init__(self, parent=None):
super(MyObject, self).__init__(parent)
self.setProperty('key', max(objects.keys() or [0]) + 1)
objects[self.property('key')] = self
따라서 자동으로 각 인스턴스를 전역 캐시에 추가하고 나중에 쉽게 찾을 수 있도록 고유 한 조회 키를 제공합니다. 이 상태에서 myData
이제 클래스를 사용하여 피클 링이 올바르게 처리MyObject
되도록 클래스를 조정해야 합니다 .
class myData():
def __init__(self, title):
self._title = title
self._stuff = dict()
self._obj = MyObject()
def __setstate__(self, state):
self._obj = objects.get(state['obj'])
self._stuff = state['stuff']
self._title = state['title']
def __getstate__(self):
return {
'obj': self._obj and self._obj.property('key'),
'title': self._title,
'stuff': self._stuff,
}
첫 번째 문제를 해결하는 것은 훨씬 간단합니다. 동적 Python 속성이 사용자 지정 데이터 역할을 사용하여 항목의 데이터에 기본 값을 저장하는지 확인하기 만하면됩니다. 이 특별한 경우, 값은 MyObject
드래그 앤 드롭 작업 후에 검색 할 수 있도록 항목 인스턴스 의 키 여야합니다 .
class myItem(QtGui.QStandardItem):
MetaRole = QtCore.Qt.UserRole + 1000
@property
def meta(self):
return objects.get(self.data(myItem.MetaRole))
@meta.setter
def meta(self, value):
self.setData(value.property('key'), myItem.MetaRole)
def clone(self):
print "My cloning"
obj = myItem(self)
print "Clone is a ", obj.__class__
return obj
아래는 위의 모든 사항을 구현하는 원본 스크립트의 작동 버전입니다. 그러나 실제 코드에서 제대로 작동하려면 거의 확실하게이를 조정해야합니다. 이것은 위에서 설명한 두 가지 문제를 처리하는 방법을 보여주는 작동하는 개념 증명입니다.
# -*- coding: utf-8 -*-
import sys
from PyQt5 import QtGui, QtWidgets, QtCore
objects = {}
class MyObject(QtCore.QObject):
def __init__(self, parent=None):
super(MyObject, self).__init__(parent)
self.setProperty('key', max(objects.keys() or [0]) + 1)
objects[self.property('key')] = self
class myData():
def __init__(self, title):
self._title = title
self._stuff = dict()
self._obj = MyObject()
def __setstate__(self, state):
self._obj = objects.get(state['obj'])
self._stuff = state['stuff']
self._title = state['title']
def __getstate__(self):
return {
'obj': self._obj.property('key'),
'title': self._title,
'stuff': self._stuff,
}
@property
def obj(self):
return self._obj
@obj.setter
def obj(self, value):
self._obj = value
@property
def title(self):
return self._title
@title.setter
def title(self, value):
self._title = value
class myItem(QtGui.QStandardItem):
MetaRole = QtCore.Qt.UserRole + 1000
@property
def meta(self):
return objects.get(self.data(myItem.MetaRole))
@meta.setter
def meta(self, value):
self.setData(value.property('key'), myItem.MetaRole)
def clone(self):
print "My cloning"
obj = myItem(self)
print "Clone is a ", obj.__class__
return obj
class mainWidget(QtWidgets.QMainWindow):
def __init__(self):
super(mainWidget, self).__init__()
self.model = QtGui.QStandardItemModel()
self.model.setItemPrototype(myItem())
self.view = QtWidgets.QTreeView()
self.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.view.customContextMenuRequested.connect(self.list_click)
self.view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
self.view.setDefaultDropAction(QtCore.Qt.MoveAction)
self.view.setDragDropOverwriteMode(False)
self.view.setAcceptDrops(True)
self.view.setDropIndicatorShown(True)
self.view.setDragEnabled(True)
self.view.setModel(self.model)
dataA = myData('A thing')
parentA = myItem()
parentA.setText('A')
parentA.setDragEnabled(True)
parentA.setDropEnabled(True)
parentA.setData(dataA)
parentA.meta = MyObject()
childa = myItem()
childa.setText('a')
childb = myItem()
childb.setText('b')
childc = myItem()
childc.setText('c')
parentA.appendRows([childa, childb, childc])
dataB = myData('B thing')
parentB = myItem()
parentB.setText('B')
parentB.setDragEnabled(True)
parentB.setDropEnabled(True)
parentB.setData(dataB)
parentB.meta = MyObject()
childd = myItem()
childd.setText('d')
childe = myItem()
childe.setText('e')
childf = myItem()
childf.setText('f')
parentB.appendRows([childd, childe, childf])
self.model.appendRow(parentA)
self.model.appendRow(parentB)
classAct = QtWidgets.QAction('Class', self)
classAct.triggered.connect(self.classIs)
dataAct = QtWidgets.QAction('Data', self)
dataAct.triggered.connect(self.dataIs)
metaAct = QtWidgets.QAction('Meta', self)
metaAct.triggered.connect(self.metaIs)
self.menu = QtWidgets.QMenu("Item info")
self.menu.addAction(classAct)
self.menu.addAction(dataAct)
self.menu.addAction(metaAct)
self.setCentralWidget(self.view)
@QtCore.pyqtSlot(QtCore.QPoint)
def list_click(self, position):
self.menu.popup(self.view.viewport().mapToGlobal(position))
def classIs(self):
selected_indexes = self.view.selectedIndexes()
for index in selected_indexes:
item = self.model.itemFromIndex(index)
print "Item {} Class {} ".format(item.text(), item.__class__())
def dataIs(self):
selected_indexes = self.view.selectedIndexes()
for index in selected_indexes:
item = self.model.itemFromIndex(index)
try:
print "Item {} data {} Object {}".format(item.text(),
item.data().title,
item.data().obj)
except Exception as exc:
print "Data exception ", exc
def metaIs(self):
selected_indexes = self.view.selectedIndexes()
for index in selected_indexes:
item = self.model.itemFromIndex(index)
try:
print "Item {} meta {} ".format(item.text(), item.meta)
except Exception as exc:
print "Meta exception ", exc
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = mainWidget()
main.show()
app.exec_()
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다