1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
# (C) Copyright 2005-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
from pickle import dumps, load, loads, PickleError
import warnings
import io
from pyface.qt import QtCore
# -------------------------------------------------------------------------------
# 'PyMimeData' class:
# -------------------------------------------------------------------------------
def str2bytes(s):
return bytes(s, "ascii")
class PyMimeData(QtCore.QMimeData):
""" The PyMimeData wraps a Python instance as MIME data.
"""
# The MIME type for instances.
MIME_TYPE = "application/x-ets-qt4-instance"
NOPICKLE_MIME_TYPE = "application/x-ets-qt4-instance-no-pickle"
def __init__(self, data=None, pickle=True):
""" Initialise the instance.
"""
QtCore.QMimeData.__init__(self)
# Keep a local reference to be returned if possible.
self._local_instance = data
if pickle:
if data is not None:
# We may not be able to pickle the data.
try:
pdata = dumps(data)
# This format (as opposed to using a single sequence) allows
# the type to be extracted without unpickling the data.
self.setData(self.MIME_TYPE, dumps(data.__class__) + pdata)
except (PickleError, TypeError, AttributeError):
# if pickle fails, still try to create a draggable
warnings.warn(
(
"Could not pickle dragged object %s, "
+ "using %s mimetype instead"
)
% (repr(data), self.NOPICKLE_MIME_TYPE),
RuntimeWarning,
)
self.setData(
self.NOPICKLE_MIME_TYPE, str2bytes(str(id(data)))
)
else:
self.setData(self.NOPICKLE_MIME_TYPE, str2bytes(str(id(data))))
@classmethod
def coerce(cls, md):
""" Wrap a QMimeData or a python object to a PyMimeData.
"""
# See if the data is already of the right type. If it is then we know
# we are in the same process.
if isinstance(md, cls):
return md
if isinstance(md, PyMimeData):
# if it is a PyMimeData, migrate all its data, subclasses should
# override this method if it doesn't do thgs correctly for them
data = md.instance()
nmd = cls()
nmd._local_instance = data
for format in md.formats():
nmd.setData(format, md.data(format))
elif isinstance(md, QtCore.QMimeData):
# if it is a QMimeData, migrate all its data
nmd = cls()
for format in md.formats():
nmd.setData(format, md.data(format))
else:
# by default, try to pickle the coerced object
pickle = True
# See if the data is a list, if so check for any items which are
# themselves of the right type. If so, extract the instance and
# track whether we should pickle.
# XXX lists should suffice for now, but may want other containers
if isinstance(md, list):
pickle = not any(
item.hasFormat(cls.NOPICKLE_MIME_TYPE)
for item in md
if isinstance(item, QtCore.QMimeData)
)
md = [
item.instance() if isinstance(item, PyMimeData) else item
for item in md
]
# Arbitrary python object, wrap it into PyMimeData
nmd = cls(md, pickle)
return nmd
def instance(self):
""" Return the instance.
"""
if self._local_instance is not None:
return self._local_instance
if not self.hasFormat(self.MIME_TYPE):
# We have no pickled python data defined.
return None
stream = io.BytesIO(self.data(self.MIME_TYPE).data())
try:
# Skip the type.
load(stream)
# Recreate the instance.
return load(stream)
except PickleError:
pass
return None
def instanceType(self):
""" Return the type of the instance.
"""
if self._local_instance is not None:
return self._local_instance.__class__
try:
if self.hasFormat(self.MIME_TYPE):
return loads(self.data(self.MIME_TYPE).data())
except PickleError:
pass
return None
def localPaths(self):
""" The list of local paths from url list, if any.
"""
ret = []
for url in self.urls():
if url.scheme() == "file":
ret.append(url.toLocalFile())
return ret
|