我有以下两个课程:
class QPolygonModel(QtGui.QPolygon):
_idx = None
_selected = None
def __init__(self, idx, polygon: QtGui.QPolygon = None):
# Call default constructor
if polygon is None:
super().__init__()
# Call copy constructor
else:
super().__init__(polygon)
self._idx = idx
self._selected = False
@property
def idx(self):
return self._idx
@property
def is_selected(self):
return self._selected
@is_selected.setter
def is_selected(self, flag):
self._selected = flag
def get_points(self):
res = []
for i in range(0, self.size()):
res.append(self.point(i))
return res
这是一个继承自QPolygon的自定义多边形类。此类的对象存储在“场景”类的列表中:
class ImageAnnotatorState:
points = None
radius = None
image = None
polygons = None
_finished = None
multiselect = None
def __init__(self, image):
super().__init__()
self.points = QtGui.QPolygon()
self.radius = 8
self.image = image
self.polygons = self._init_polygons()
self.is_finished = False
self.multiselect = False
def _init_polygons(self):
result = []
for annotation in self.image.annotations:
polyline = QPolygonModel(annotation.get_id())
for point in annotation.points:
q_point = QPoint(point.x, point.y)
polyline.append(q_point)
result.append(polyline)
return result
@property
def is_finished(self):
return self._finished
@is_finished.setter
def is_finished(self, flag):
self._finished = flag
现在,出于创建撤消功能的目的,我需要创建该场景类的深层副本,以便可以存储在进行场景更改之前处于活动状态的状态。
因此,在QDialog表单中,我尝试执行以下操作:
class ImageAnnotator(QDialog):
_state = None
_previous_state = None
def __init__(self, image):
super().__init__()
self._state = ImageAnnotatorState(image)
self._previous_state = copy.deepcopy(self._state)
self.show()
此处的deepcopy调用失败,并带有以下异常:
SystemError: attempt to pickle unknown type 'QPolygonModel'
我在做什么错了?
编辑:
可复制的示例:
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QApplication
import copy
import sys
class Test(QtGui.QPolygon):
idx = None
def __init__(self, z = None):
if z is None:
super().__init__()
else:
super().__init__(z)
class State:
test = None
def __init__(self):
self.test = [Test(), Test()]
print(self.test)
class Main(QDialog):
state = None
prev = None
def __init__(self):
super().__init__()
self.state = State()
prev = copy.deepcopy(self.state)
print(prev)
app = QApplication(sys.argv)
Main()
答案 0 :(得分:2)
似乎这是一个类似于ekhumoro在this answer中指出的错误。一种解决方法是实施__deepcopy__
方法。
另一方面,如果要在QPolygon的情况下设置默认值,请不要使用None,而要使用空的QPolygon。
基于上述内容,我实现了以下内容:
import copy
import random
from PyQt5 import QtCore, QtGui
class QPolygonModel(QtGui.QPolygon):
def __init__(self, idx, polygon=QtGui.QPolygon()):
super().__init__(polygon)
self._idx = idx
self._selected = False
@property
def idx(self):
return self._idx
@property
def is_selected(self):
return self._selected
@is_selected.setter
def is_selected(self, flag):
self._selected = flag
def get_points(self):
res = []
for i in range(0, self.size()):
res.append(self.point(i))
return res
# https://stackoverflow.com/a/10622689
def __deepcopy__(self, memo):
o = QPolygonModel(self.idx)
o.__dict__.update(self.__dict__)
ba = QtCore.QByteArray()
stream_w = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
stream_w << self
stream_r = QtCore.QDataStream(ba, QtCore.QIODevice.ReadOnly)
stream_r >> o
return o
class State:
def __init__(self):
self.polylines = []
for _ in range(4):
poly = QtGui.QPolygon(
[QtCore.QPoint(*random.sample(range(10), 2)) for _ in range(4)]
)
polyline = QPolygonModel(random.randint(0, 10), poly)
self.polylines.append(polyline)
if __name__ == "__main__":
curr = State()
prev = copy.deepcopy(curr)
assert len(curr.polylines) == len(prev.polylines)
for polyline1, polyline2 in zip(curr.polylines, prev.polylines):
assert id(polyline1) != id(polyline2)
assert polyline1.size() == polyline2.size()
assert polyline1.is_selected == polyline2.is_selected
assert polyline1.idx == polyline2.idx
for i, j in zip(range(polyline1.size()), range(polyline2.size())):
assert polyline1.point(i) == polyline2.point(j)