创建deepcopy时尝试pickle未知类型

2024-09-29 01:35:36 发布

您现在位置:Python中文网/ 问答频道 /正文

我有以下两门课:

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

现在为了创建undo函数,我需要创建这个场景类的deepcopy,这样我就可以存储在场景更改之前处于活动状态的状态。你知道吗

因此,在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()

Tags: imageselfnoneinitisdefclasspoint
1条回答
网友
1楼 · 发布于 2024-09-29 01:35:36

似乎这是一个类似于ekhumorothis answer中指出的bug。解决方法是实现__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)

相关问题 更多 >