diff options
| -rw-r--r-- | sources/pyside6/libpyside/pysidesignal.cpp | 2 | ||||
| -rw-r--r-- | sources/pyside6/tests/QtQml/signal_types.py | 124 | ||||
| -rw-r--r-- | sources/pyside6/tests/QtQml/signal_types.qml | 26 |
3 files changed, 152 insertions, 0 deletions
diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp index 8a32bf005..f87b86a61 100644 --- a/sources/pyside6/libpyside/pysidesignal.cpp +++ b/sources/pyside6/libpyside/pysidesignal.cpp @@ -867,6 +867,8 @@ QByteArray getTypeName(PyObject *obType) return QByteArrayLiteral("bool"); if (type == &PyList_Type) return QByteArrayLiteral("QVariantList"); + if (type == &PyDict_Type) + return QByteArrayLiteral("QVariantMap"); if (Py_TYPE(type) == SbkEnumType_TypeF()) return Shiboken::Enum::getCppName(type); return QByteArrayLiteral("PyObject"); diff --git a/sources/pyside6/tests/QtQml/signal_types.py b/sources/pyside6/tests/QtQml/signal_types.py new file mode 100644 index 000000000..240c0fd6e --- /dev/null +++ b/sources/pyside6/tests/QtQml/signal_types.py @@ -0,0 +1,124 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import json +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from init_paths import init_test_paths +init_test_paths(False) + +from helper.helper import quickview_errorstring +from helper.timedqguiapplication import TimedQGuiApplication + +from PySide6.QtQuick import QQuickView +from PySide6.QtCore import QObject, Signal, Slot, QUrl +from PySide6.QtQml import QmlElement + +"""PYSIDE-2098: Roundtrip test for signals using QVariantList/QVariantMap. + +@QmlElement Obj has signals of list/dict type which are connected to an +instance of Connections in QML. The QML instance sends them back to Obj's +slots and additionally sends them back as stringified JSON. This verifies that +a conversion is done instead of falling back to the default PyObject +passthrough converter, resulting in a QVariant<PyObject> and reference leaks +on the PyObject. +""" + +QML_IMPORT_NAME = "test.Obj" +QML_IMPORT_MAJOR_VERSION = 1 + + +@QmlElement +class Obj(QObject): + listSignal = Signal(list) + dictSignal = Signal(dict) + + def __init__(self, parent=None): + super().__init__(parent) + self._last_data = None + self._last_json_data = None + + def clear(self): + self._last_data = None + self._last_json_data = None + + def last_data(self): + """Last data received.""" + return self._last_data + + def last_json_data(self): + """Last data converted from JSON.""" + return self._last_json_data + + def emit_list(self, test_list): + self.listSignal.emit(test_list) + + def emit_dict(self, test_dict): + self.dictSignal.emit(test_dict) + + @Slot(list) + def list_slot(self, l): + self._last_data = l + print("list_slot", l) + + @Slot(dict) + def dict_slot(self, d): + self._last_data = d + print("dict_slot", d) + + @Slot(str) + def json_slot(self, s): + self._last_json_data = json.loads(s) + print(f'json_slot "{s}"->', self._last_json_data) + + +class TestConnectionWithQml(TimedQGuiApplication): + + def setUp(self): + super().setUp() + self._view = QQuickView() + self._obj = Obj() + + self._view.setInitialProperties({"o": self._obj}) + file = Path(__file__).resolve().parent / "signal_types.qml" + self.assertTrue(file.is_file()) + self._view.setSource(QUrl.fromLocalFile(file)) + root = self._view.rootObject() + self.assertTrue(root, quickview_errorstring(self._view)) + + def tearDown(self): + super().tearDown() + del self._view + self._view = None + + def testVariantList(self): + self._obj.clear() + test_list = [1, 2] + before_refcount = sys.getrefcount(test_list) + self._obj.emit_list(test_list) + received = self._obj.last_data() + self.assertTrue(isinstance(received, list)) + self.assertEqual(test_list, received) + self.assertEqual(test_list, self._obj.last_json_data()) + refcount = sys.getrefcount(test_list) + self.assertEqual(before_refcount, refcount) + + def testVariantDict(self): + self._obj.clear() + test_dict = {"1": 1, "2": 2} + before_refcount = sys.getrefcount(test_dict) + self._obj.emit_dict(test_dict) + received = self._obj.last_data() + self.assertTrue(isinstance(received, dict)) + self.assertEqual(test_dict, received) + self.assertEqual(test_dict, self._obj.last_json_data()) + refcount = sys.getrefcount(test_dict) + self.assertEqual(before_refcount, refcount) + + +if __name__ == "__main__": + unittest.main() diff --git a/sources/pyside6/tests/QtQml/signal_types.qml b/sources/pyside6/tests/QtQml/signal_types.qml new file mode 100644 index 000000000..6b03b3abd --- /dev/null +++ b/sources/pyside6/tests/QtQml/signal_types.qml @@ -0,0 +1,26 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import test.Obj + +Rectangle { + visible: true + required property Obj o + + Connections { + target: o + function onListSignal(list) { + var json_data = JSON.stringify(list) + console.log("Connections.onListSignal: " + typeof(list) + " " + json_data) + o.list_slot(list) + o.json_slot(json_data) + } + function onDictSignal(dict) { + var json_data = JSON.stringify(dict) + console.log("Connections.onDictSignal: " + typeof(dict) + " " + json_data) + o.dict_slot(dict) + o.json_slot(json_data) + } + } +} |
