1 // This implements the conversion of a QVariant to a Python object and is part
2 // of the public API.
3 //
4 // Copyright (c) 2021 Riverbank Computing Limited <info@riverbankcomputing.com>
5 //
6 // This file is part of PyQt5.
7 //
8 // This file may be used under the terms of the GNU General Public License
9 // version 3.0 as published by the Free Software Foundation and appearing in
10 // the file LICENSE included in the packaging of this file. Please review the
11 // following information to ensure the GNU General Public License version 3.0
12 // requirements will be met: http://www.gnu.org/copyleft/gpl.html.
13 //
14 // If you do not wish to use this file under the terms of the GPL version 3.0
15 // then you may purchase a commercial license. For more information contact
16 // info@riverbankcomputing.com.
17 //
18 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
19 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20
21
22 #include <Python.h>
23
24 #include <QVariant>
25
26 #include "qpycore_chimera.h"
27
28 #include "sipAPIQtCore.h"
29
30
31 // Forward declarations.
32 static PyObject *convert_hash(const Chimera *ct, const QVariantHash &value);
33 static PyObject *convert_list(const Chimera *ct, const QVariantList &value);
34 static PyObject *convert_map(const Chimera *ct, const QVariantMap &value);
35 static PyObject *convert(const Chimera *ct, const QVariant &value);
36 static int add_variant_to_dict(const Chimera *ct, PyObject *dict,
37 const QString &key, const QVariant &value);
38
39
40 // Convert a QVariant to a Python object according to an optional type.
pyqt5_from_qvariant_by_type(QVariant & value,PyObject * type)41 PyObject *pyqt5_from_qvariant_by_type(QVariant &value, PyObject *type)
42 {
43 PyObject *value_obj;
44
45 if (type)
46 {
47 const Chimera *ct = Chimera::parse(type);
48
49 if (!ct)
50 return 0;
51
52 QVariant::Type wanted = static_cast<QVariant::Type>(ct->metatype());
53
54 // Get QVariant to do a conversion if there is one to do.
55 if (value.isValid() && ct->metatype() < static_cast<int>(QVariant::UserType))
56 {
57 // If we have a QStringList but are not wanting one then convert it
58 // to a QVariantList.
59 if (wanted != QVariant::StringList && value.type() == QVariant::StringList)
60 value.convert(QVariant::List);
61
62 // If we have a container but are not wanting one then assume we
63 // want a container with elements of the wanted type.
64 if (wanted != QVariant::List && value.type() == QVariant::List)
65 {
66 // If we have a QVariantList but we wanted a QStringList then
67 // assume each variant is a string.
68 if (wanted == QVariant::StringList)
69 value_obj = convert(ct, value);
70 else
71 value_obj = convert_list(ct, value.toList());
72 }
73 else if (wanted != QVariant::Map && value.type() == QVariant::Map)
74 {
75 value_obj = convert_map(ct, value.toMap());
76 }
77 else if (wanted != QVariant::Hash && value.type() == QVariant::Hash)
78 {
79 value_obj = convert_hash(ct, value.toHash());
80 }
81 else
82 {
83 value_obj = convert(ct, value);
84 }
85 }
86 else if (!value.isValid())
87 {
88 // Convert an invalid value to the default value of the requested
89 // type.
90 if (ct->py_type())
91 value_obj = PyObject_CallObject(ct->py_type(), NULL);
92 else
93 value_obj = ct->toPyObject(QVariant(wanted));
94 }
95 else
96 {
97 // This is likely to fail and the exception will say why.
98 value_obj = ct->toPyObject(value);
99 }
100
101 delete ct;
102 }
103 else
104 {
105 QVariant *heap = new QVariant(value);
106 value_obj = sipConvertFromNewType(heap, sipType_QVariant, 0);
107
108 if (!value_obj)
109 delete heap;
110 }
111
112 return value_obj;
113 }
114
115
116 // Convert a QVariantList to a list of Python objects.
convert_list(const Chimera * ct,const QVariantList & value)117 static PyObject *convert_list(const Chimera *ct, const QVariantList &value)
118 {
119 PyObject *list = PyList_New(value.size());
120
121 if (!list)
122 return 0;
123
124 for (int i = 0; i < value.size(); ++i)
125 {
126 PyObject *el = convert(ct, value.at(i));
127
128 if (!el)
129 {
130 Py_DECREF(list);
131 return 0;
132 }
133
134 PyList_SetItem(list, i, el);
135 }
136
137 return list;
138 }
139
140
141 // Convert a QVariantMap to a dict of Python objects.
convert_map(const Chimera * ct,const QVariantMap & value)142 static PyObject *convert_map(const Chimera *ct, const QVariantMap &value)
143 {
144 PyObject *dict = PyDict_New();
145
146 if (!dict)
147 return 0;
148
149 for (QVariantMap::const_iterator it = value.constBegin(); it != value.constEnd(); ++it)
150 {
151 if (add_variant_to_dict(ct, dict, it.key(), it.value()) < 0)
152 {
153 Py_DECREF(dict);
154 return 0;
155 }
156 }
157
158 return dict;
159 }
160
161
162 // Convert a QVariantHash to a dict of Python objects.
convert_hash(const Chimera * ct,const QVariantHash & value)163 static PyObject *convert_hash(const Chimera *ct, const QVariantHash &value)
164 {
165 PyObject *dict = PyDict_New();
166
167 if (!dict)
168 return 0;
169
170 for (QVariantHash::const_iterator it = value.constBegin(); it != value.constEnd(); ++it)
171 {
172 if (add_variant_to_dict(ct, dict, it.key(), it.value()) < 0)
173 {
174 Py_DECREF(dict);
175 return 0;
176 }
177 }
178
179 return dict;
180 }
181
182
183 // Convert a QVariant to a Python object.
convert(const Chimera * ct,const QVariant & value)184 static PyObject *convert(const Chimera *ct, const QVariant &value)
185 {
186 QVariant converted = value;
187
188 if (!converted.convert(static_cast<QVariant::Type>(ct->metatype())))
189 converted = value;
190
191 return ct->toPyObject(converted);
192 }
193
194
195 // Add a QVariant to a Python dict with a QString key.
add_variant_to_dict(const Chimera * ct,PyObject * dict,const QString & key,const QVariant & value)196 static int add_variant_to_dict(const Chimera *ct, PyObject *dict,
197 const QString &key, const QVariant &value)
198 {
199 QString *key_heap = new QString(key);
200 PyObject *key_obj = sipConvertFromNewType(key_heap, sipType_QString, 0);
201
202 if (!key_obj)
203 {
204 delete key_heap;
205 return -1;
206 }
207
208 PyObject *value_obj = convert(ct, value);
209
210 if (!value_obj)
211 {
212 Py_DECREF(key_obj);
213 return -1;
214 }
215
216 int rc = PyDict_SetItem(dict, key_obj, value_obj);
217
218 Py_DECREF(key_obj);
219 Py_DECREF(value_obj);
220
221 return rc;
222 }
223