在 python 3.6+ 和 PyQt5 中腌制 QPolygon
Pickle a QPolygon in python 3.6+ and PyQt5
我尝试 pickle QPolygon 并在之后加载它,但出现错误。我已经在 Python2 上使用 PyQt4 完成了此操作,但现在想在 Python3 上使用 PyQt5 使用它。
我不想read/load用Python生成数据2! pickle 文件只是用来临时存储 Qt 元素,比如从 Python3 到 Python3.
的 QPolygons
我已经为 pickle.dump() 测试了 1-4 的不同协议选项,并尝试使用 "fix_imports=True" 选项,这对 Python3.[=14 应该没有影响=]
这是我的简化代码
from PyQt5.QtGui import QPolygon
from PyQt5.QtCore import QPoint
import pickle
file_name = "test_pickle.chip"
with open(file_name, 'wb') as f:
poly = QPolygon((QPoint(1, 1), QPoint(2, 2)))
pickle.dump(poly, f, protocol=2) # , fix_imports=True)
# loading the data again
with open(file_name, 'rb') as f:
elem = pickle.load(f, encoding='bytes') # , fix_imports=True)
我收到以下错误消息,但无法对其执行任何操作:
elem = pickle.load(f, encoding='bytes') # , fix_imports=True)
TypeError: index 0 has type 'int' but 'QPoint' is expected
除了pickle,还有其他选择吗?
提前致谢!
您可以使用QDataStream
序列化/反序列化Qt对象:
from PyQt5 import QtCore
from PyQt5.QtGui import QPolygon
from PyQt5.QtCore import QPoint, QFile, QIODevice, QDataStream, QVariant
file_name = "test.dat"
output_file = QFile(file_name)
output_file.open(QIODevice.WriteOnly)
stream_out = QDataStream(output_file)
output_poly = QPolygon((QPoint(1, 6), QPoint(2, 6)))
output_str = QVariant('foo') # Use QVariant for QString
stream_out << output_poly << output_str
output_file.close()
input_file = QFile(file_name)
input_file.open(QIODevice.ReadOnly)
stream_in = QDataStream(input_file)
input_poly = QPolygon()
input_str = QVariant()
stream_in >> input_poly >> input_str
input_file.close()
print(str(output_str.value()))
print(str(input_str.value()))
对文档的一些搜索告诉您如何通过 __reduce__
method 为自定义 类 实现自定义酸洗。基本上你 return 一个元组,包括在取消酸洗时要创建的新对象的构造函数以及将传递给构造函数的参数元组。然后,您可以选择传递一个 state
对象(参见 __getstate__
和 __setstate__
)和一些迭代器来添加位置数据和键值数据。
通过子类化 QPolygon
我们可以这样添加可拾取性:(这可能会被清理/重组,但这是我得到的第一个实现)
from PyQt5.QtGui import QPolygon
from PyQt5.QtCore import QPoint
import pickle
class Picklable_QPolygon(QPolygon):
def __reduce__(self):
# constructor, (initargs), state object passed to __setstate__
return type(self), (), self.__getstate__()
#I'm not sure where this gets called other than manually in __reduce__
# but it seemed like the semantically correct way to do it...
def __getstate__(self):
state = [0]
for point in self:
state.append(point.x())
state.append(point.y())
return state
#putPoints seems to omit the `nPoints` arg compared to c++ version.
# this may be a version dependent thing for PyQt. Definitely test that
# what you're getting out is the same as what you put in..
def __setstate__(self, state):
self.putPoints(*state)
poly = Picklable_QPolygon((QPoint(1, 1), QPoint(2, 2)))
s = pickle.dumps(poly)
elem = pickle.loads(s)
受前面答案的启发,我创建了一个函数,它采用某种支持 QDataSteam 的 Qt 和 returns 一个 class 继承自 class 并且是可腌制的,在下面的例子我展示了 QPoygon 和 QPainterPath:
import pickle
from PyQt5 import QtCore, QtGui
def picklable_reduce(self):
return type(self), (), self.__getstate__()
def picklable_getstate(self):
ba = QtCore.QByteArray()
stream = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
stream << self
return ba
def picklable_setstate(self, ba):
stream = QtCore.QDataStream(ba, QtCore.QIODevice.ReadOnly)
stream >> self
def create_qt_picklable(t):
return type("Picklable_{}".format(t.__name__), (t,),
{
'__reduce__': picklable_reduce,
'__getstate__': picklable_getstate,
'__setstate__': picklable_setstate
}
)
if __name__ == '__main__':
# QPolygon picklable
Picklable_QPolygon = create_qt_picklable(QtGui.QPolygon)
old_poly = Picklable_QPolygon((QtCore.QPoint(1, 1), QtCore.QPoint(2, 2)))
s = pickle.dumps(old_poly)
new_poly = pickle.loads(s)
assert(old_poly == new_poly)
# QPainterPath picklable
Picklable_QPainterPath = create_qt_picklable(QtGui.QPainterPath)
old_painterpath = Picklable_QPainterPath()
old_painterpath.addRect(20, 20, 60, 60)
old_painterpath.moveTo(0, 0)
old_painterpath.cubicTo(99, 0, 50, 50, 99, 99)
old_painterpath.cubicTo(0, 99, 50, 50, 0, 0);
s = pickle.dumps(old_painterpath)
new_painterpath= pickle.loads(s)
assert(old_painterpath == new_painterpath)
使用OP代码:
if __name__ == '__main__':
Picklable_QPolygon = create_qt_picklable(QtGui.QPolygon)
file_name = "test_pickle.chip"
poly = Picklable_QPolygon((QtCore.QPoint(1, 1), QtCore.QPoint(2, 2)))
with open(file_name, 'wb') as f:
pickle.dump(poly, f, protocol=2) # , fix_imports=True)
elem = None
with open(file_name, 'rb') as f:
elem = pickle.load(f, encoding='bytes') # , fix_imports=True)
assert(poly == elem)
这一定是 pyqt5 中的错误,因为 the documentation states that pickling QPolygon
is supported. Please therefore post a bug report to the pyqt mailing list 使用您的示例。
同时,目前最简单的替代方法是使用 QSettings:
from PyQt5.QtGui import QPolygon
from PyQt5.QtCore import QSettings, QPoint
file_name = "test_pickle.chip"
poly = QPolygon((QPoint(1, 1), QPoint(2, 2)))
# write object
s = QSettings(file_name, QSettings.IniFormat)
s.setValue('poly', poly)
del s, poly
# read object
s = QSettings(file_name, QSettings.IniFormat)
poly = s.value('poly')
print(poly)
print(poly.point(0), poly.point(1))
输出:
<PyQt5.QtGui.QPolygon object at 0x7f871f1f8828>
PyQt5.QtCore.QPoint(1, 1) PyQt5.QtCore.QPoint(2, 2)
这可以与任何 PyQt 支持的酸洗类型一起使用,加上 PyQt 可以 convert to/from a QVariant. PyQt also supports a type
argument to QSettings.value()
that can be used to explicitly state the required type. And since QSettings
is designed for storing application config data, any number of objects can be stored in the same file (somewhat like python's shelve module) 的任何其他类型。
我尝试 pickle QPolygon 并在之后加载它,但出现错误。我已经在 Python2 上使用 PyQt4 完成了此操作,但现在想在 Python3 上使用 PyQt5 使用它。
我不想read/load用Python生成数据2! pickle 文件只是用来临时存储 Qt 元素,比如从 Python3 到 Python3.
的 QPolygons我已经为 pickle.dump() 测试了 1-4 的不同协议选项,并尝试使用 "fix_imports=True" 选项,这对 Python3.[=14 应该没有影响=]
这是我的简化代码
from PyQt5.QtGui import QPolygon
from PyQt5.QtCore import QPoint
import pickle
file_name = "test_pickle.chip"
with open(file_name, 'wb') as f:
poly = QPolygon((QPoint(1, 1), QPoint(2, 2)))
pickle.dump(poly, f, protocol=2) # , fix_imports=True)
# loading the data again
with open(file_name, 'rb') as f:
elem = pickle.load(f, encoding='bytes') # , fix_imports=True)
我收到以下错误消息,但无法对其执行任何操作:
elem = pickle.load(f, encoding='bytes') # , fix_imports=True) TypeError: index 0 has type 'int' but 'QPoint' is expected
除了pickle,还有其他选择吗? 提前致谢!
您可以使用QDataStream
序列化/反序列化Qt对象:
from PyQt5 import QtCore
from PyQt5.QtGui import QPolygon
from PyQt5.QtCore import QPoint, QFile, QIODevice, QDataStream, QVariant
file_name = "test.dat"
output_file = QFile(file_name)
output_file.open(QIODevice.WriteOnly)
stream_out = QDataStream(output_file)
output_poly = QPolygon((QPoint(1, 6), QPoint(2, 6)))
output_str = QVariant('foo') # Use QVariant for QString
stream_out << output_poly << output_str
output_file.close()
input_file = QFile(file_name)
input_file.open(QIODevice.ReadOnly)
stream_in = QDataStream(input_file)
input_poly = QPolygon()
input_str = QVariant()
stream_in >> input_poly >> input_str
input_file.close()
print(str(output_str.value()))
print(str(input_str.value()))
对文档的一些搜索告诉您如何通过 __reduce__
method 为自定义 类 实现自定义酸洗。基本上你 return 一个元组,包括在取消酸洗时要创建的新对象的构造函数以及将传递给构造函数的参数元组。然后,您可以选择传递一个 state
对象(参见 __getstate__
和 __setstate__
)和一些迭代器来添加位置数据和键值数据。
通过子类化 QPolygon
我们可以这样添加可拾取性:(这可能会被清理/重组,但这是我得到的第一个实现)
from PyQt5.QtGui import QPolygon
from PyQt5.QtCore import QPoint
import pickle
class Picklable_QPolygon(QPolygon):
def __reduce__(self):
# constructor, (initargs), state object passed to __setstate__
return type(self), (), self.__getstate__()
#I'm not sure where this gets called other than manually in __reduce__
# but it seemed like the semantically correct way to do it...
def __getstate__(self):
state = [0]
for point in self:
state.append(point.x())
state.append(point.y())
return state
#putPoints seems to omit the `nPoints` arg compared to c++ version.
# this may be a version dependent thing for PyQt. Definitely test that
# what you're getting out is the same as what you put in..
def __setstate__(self, state):
self.putPoints(*state)
poly = Picklable_QPolygon((QPoint(1, 1), QPoint(2, 2)))
s = pickle.dumps(poly)
elem = pickle.loads(s)
受前面答案的启发,我创建了一个函数,它采用某种支持 QDataSteam 的 Qt 和 returns 一个 class 继承自 class 并且是可腌制的,在下面的例子我展示了 QPoygon 和 QPainterPath:
import pickle
from PyQt5 import QtCore, QtGui
def picklable_reduce(self):
return type(self), (), self.__getstate__()
def picklable_getstate(self):
ba = QtCore.QByteArray()
stream = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
stream << self
return ba
def picklable_setstate(self, ba):
stream = QtCore.QDataStream(ba, QtCore.QIODevice.ReadOnly)
stream >> self
def create_qt_picklable(t):
return type("Picklable_{}".format(t.__name__), (t,),
{
'__reduce__': picklable_reduce,
'__getstate__': picklable_getstate,
'__setstate__': picklable_setstate
}
)
if __name__ == '__main__':
# QPolygon picklable
Picklable_QPolygon = create_qt_picklable(QtGui.QPolygon)
old_poly = Picklable_QPolygon((QtCore.QPoint(1, 1), QtCore.QPoint(2, 2)))
s = pickle.dumps(old_poly)
new_poly = pickle.loads(s)
assert(old_poly == new_poly)
# QPainterPath picklable
Picklable_QPainterPath = create_qt_picklable(QtGui.QPainterPath)
old_painterpath = Picklable_QPainterPath()
old_painterpath.addRect(20, 20, 60, 60)
old_painterpath.moveTo(0, 0)
old_painterpath.cubicTo(99, 0, 50, 50, 99, 99)
old_painterpath.cubicTo(0, 99, 50, 50, 0, 0);
s = pickle.dumps(old_painterpath)
new_painterpath= pickle.loads(s)
assert(old_painterpath == new_painterpath)
使用OP代码:
if __name__ == '__main__':
Picklable_QPolygon = create_qt_picklable(QtGui.QPolygon)
file_name = "test_pickle.chip"
poly = Picklable_QPolygon((QtCore.QPoint(1, 1), QtCore.QPoint(2, 2)))
with open(file_name, 'wb') as f:
pickle.dump(poly, f, protocol=2) # , fix_imports=True)
elem = None
with open(file_name, 'rb') as f:
elem = pickle.load(f, encoding='bytes') # , fix_imports=True)
assert(poly == elem)
这一定是 pyqt5 中的错误,因为 the documentation states that pickling QPolygon
is supported. Please therefore post a bug report to the pyqt mailing list 使用您的示例。
同时,目前最简单的替代方法是使用 QSettings:
from PyQt5.QtGui import QPolygon
from PyQt5.QtCore import QSettings, QPoint
file_name = "test_pickle.chip"
poly = QPolygon((QPoint(1, 1), QPoint(2, 2)))
# write object
s = QSettings(file_name, QSettings.IniFormat)
s.setValue('poly', poly)
del s, poly
# read object
s = QSettings(file_name, QSettings.IniFormat)
poly = s.value('poly')
print(poly)
print(poly.point(0), poly.point(1))
输出:
<PyQt5.QtGui.QPolygon object at 0x7f871f1f8828>
PyQt5.QtCore.QPoint(1, 1) PyQt5.QtCore.QPoint(2, 2)
这可以与任何 PyQt 支持的酸洗类型一起使用,加上 PyQt 可以 convert to/from a QVariant. PyQt also supports a type
argument to QSettings.value()
that can be used to explicitly state the required type. And since QSettings
is designed for storing application config data, any number of objects can be stored in the same file (somewhat like python's shelve module) 的任何其他类型。