From de5f162a70dda9d74cfce2337a9f90ddcb4ca75f Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 3 May 2022 12:53:33 +0200 Subject: shiboken: Introduce a function to get the type name of a polymorphic class Helps for hierarchies that do not have virtual destructors where the default typeid(t).name() does not work (QStyleOption). Pick-to: 6.3 Fixes: PYSIDE-1909 Change-Id: I9ce3769a8c3550d011023597eede0ae6f8c9ad68 Reviewed-by: Christian Tismer --- .../QtWidgets/typesystem_widgets_common.xml | 5 +- sources/pyside6/PySide6/glue/qtwidgets.cpp | 57 ++++++++++++++++++ sources/pyside6/tests/QtWidgets/CMakeLists.txt | 1 + .../pyside6/tests/QtWidgets/qstyleoption_test.py | 68 ++++++++++++++++++++++ sources/shiboken6/ApiExtractor/typesystem.cpp | 13 +++++ sources/shiboken6/ApiExtractor/typesystem.h | 3 + .../shiboken6/ApiExtractor/typesystemparser.cpp | 2 + .../shiboken6/doc/typesystem_specifying_types.rst | 15 +++++ .../shiboken6/generator/shiboken/cppgenerator.cpp | 10 +++- 9 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 sources/pyside6/tests/QtWidgets/qstyleoption_test.py (limited to 'sources') diff --git a/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml b/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml index fa973e52d..7a74d1820 100644 --- a/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml +++ b/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml @@ -88,7 +88,10 @@ - + + diff --git a/sources/pyside6/PySide6/glue/qtwidgets.cpp b/sources/pyside6/PySide6/glue/qtwidgets.cpp index 7511e89cc..b355f41cb 100644 --- a/sources/pyside6/PySide6/glue/qtwidgets.cpp +++ b/sources/pyside6/PySide6/glue/qtwidgets.cpp @@ -719,6 +719,63 @@ QAction *cppResult = %CPPSELF.exec(%1, %2, %3, %4); %PYARG_0 = %CONVERTTOPYTHON[QAction*](cppResult); // @snippet qmenu-exec-3 +// @snippet qstyleoption-typename +const char *styleOptionType(const QStyleOption *o) +{ + switch (o->type) { + case QStyleOption::SO_Default: + break; + case QStyleOption::SO_FocusRect: + return "StyleOptionFocusRect"; + case QStyleOption::SO_Button: + return "StyleOptionButton"; + case QStyleOption::SO_Tab: + return "StyleOptionTab"; + case QStyleOption::SO_MenuItem: + return "StyleOptionMenuItem"; + case QStyleOption::SO_Frame: + return "StyleOptionFrame"; + case QStyleOption::SO_ProgressBar: + return "StyleOptionProgressBar"; + case QStyleOption::SO_ToolBox: + return "StyleOptionToolBox"; + case QStyleOption::SO_Header: + return "StyleOptionHeader"; + case QStyleOption::SO_DockWidget: + return "StyleOptionDockWidget"; + case QStyleOption::SO_ViewItem: + return "StyleOptionViewItem"; + case QStyleOption::SO_TabWidgetFrame: + return "StyleOptionTabWidgetFrame"; + case QStyleOption::SO_TabBarBase: + return "StyleOptionTabBarBase"; + case QStyleOption::SO_RubberBand: + return "StyleOptionRubberBand"; + case QStyleOption::SO_ToolBar: + return "StyleOptionToolBar"; + case QStyleOption::SO_GraphicsItem: + return "StyleOptionGraphicsItem"; + case QStyleOption::SO_Slider: + return "StyleOptionSlider"; + case QStyleOption::SO_SpinBox: + return "StyleOptionSpinBox"; + case QStyleOption::SO_ToolButton: + return "StyleOptionToolButton"; + case QStyleOption::SO_ComboBox: + return "StyleOptionComboBox"; + case QStyleOption::SO_TitleBar: + return "StyleOptionTitleBar"; + case QStyleOption::SO_GroupBox: + return "StyleOptionGroupBox"; + case QStyleOption::SO_SizeGrip: + return "StyleOptionSizeGrip"; + default: + break; + } + return "QStyleOption"; +} +// @snippet qstyleoption-typename + /********************************************************************* * CONVERSIONS ********************************************************************/ diff --git a/sources/pyside6/tests/QtWidgets/CMakeLists.txt b/sources/pyside6/tests/QtWidgets/CMakeLists.txt index 8978df9a4..5989c0625 100644 --- a/sources/pyside6/tests/QtWidgets/CMakeLists.txt +++ b/sources/pyside6/tests/QtWidgets/CMakeLists.txt @@ -106,6 +106,7 @@ PYSIDE_TEST(qpicture_test.py) PYSIDE_TEST(qpushbutton_test.py) PYSIDE_TEST(qsplitter_test.py) PYSIDE_TEST(qstyle_test.py) +PYSIDE_TEST(qstyleoption_test.py) PYSIDE_TEST(qtableview_test.py) PYSIDE_TEST(qtabwidget_test.py) PYSIDE_TEST(qtabwidgetclear_test.py) diff --git a/sources/pyside6/tests/QtWidgets/qstyleoption_test.py b/sources/pyside6/tests/QtWidgets/qstyleoption_test.py new file mode 100644 index 000000000..2cbdfcfbd --- /dev/null +++ b/sources/pyside6/tests/QtWidgets/qstyleoption_test.py @@ -0,0 +1,68 @@ +############################################################################# +## +## Copyright (C) 2022 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys +import os +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.usesqapplication import UsesQApplication + +from PySide6.QtWidgets import (QApplication, QCommonStyle, QPushButton) + + +text = '' + + +class Style(QCommonStyle): + + def drawControl(self, element, option, painter, widget=None): + # This should be a QStyleOptionButton with a "text" field + global text + text = option.text + + +class StyleOptionTest(UsesQApplication): + '''PYSIDE-1909: Test cast to derived style option classes.''' + + def testStyle(self): + global text + button = QPushButton("Hello World") + button.setStyle(Style()) + button.show() + while not text: + QApplication.processEvents() + self.assertEqual(text, button.text()) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/ApiExtractor/typesystem.cpp b/sources/shiboken6/ApiExtractor/typesystem.cpp index ca9bcc67d..101be0c79 100644 --- a/sources/shiboken6/ApiExtractor/typesystem.cpp +++ b/sources/shiboken6/ApiExtractor/typesystem.cpp @@ -1242,6 +1242,7 @@ public: uint m_deleteInMainThread : 1; QString m_polymorphicIdValue; + QString m_polymorphicNameFunction; QString m_targetType; ComplexTypeEntry::TypeFlags m_typeFlags; ComplexTypeEntry::CopyableFlag m_copyableFlag = ComplexTypeEntry::Unknown; @@ -1421,6 +1422,18 @@ QString ComplexTypeEntry::polymorphicIdValue() const return d->m_polymorphicIdValue; } +QString ComplexTypeEntry::polymorphicNameFunction() const +{ + S_D(const ComplexTypeEntry); + return d->m_polymorphicNameFunction; +} + +void ComplexTypeEntry::setPolymorphicNameFunction(const QString &n) +{ + S_D(ComplexTypeEntry); + d->m_polymorphicNameFunction = n; +} + QString ComplexTypeEntry::targetType() const { S_D(const ComplexTypeEntry); diff --git a/sources/shiboken6/ApiExtractor/typesystem.h b/sources/shiboken6/ApiExtractor/typesystem.h index 5c4689839..dd8b1524a 100644 --- a/sources/shiboken6/ApiExtractor/typesystem.h +++ b/sources/shiboken6/ApiExtractor/typesystem.h @@ -597,6 +597,9 @@ public: void setPolymorphicIdValue(const QString &value); QString polymorphicIdValue() const; + QString polymorphicNameFunction() const; + void setPolymorphicNameFunction(const QString &n); + QString targetType() const; void setTargetType(const QString &code); diff --git a/sources/shiboken6/ApiExtractor/typesystemparser.cpp b/sources/shiboken6/ApiExtractor/typesystemparser.cpp index a7ce0b3d5..2de95fcdc 100644 --- a/sources/shiboken6/ApiExtractor/typesystemparser.cpp +++ b/sources/shiboken6/ApiExtractor/typesystemparser.cpp @@ -1728,6 +1728,8 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader ctype->setTargetLangName(attributes->takeAt(i).value().toString()); } else if (name == u"polymorphic-base") { ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); + } else if (name == u"polymorphic-name-function") { + ctype->setPolymorphicNameFunction(attributes->takeAt(i).value().toString()); } else if (name == u"polymorphic-id-expression") { ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); } else if (name == copyableAttribute()) { diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst index 5459046e0..caacc5cb3 100644 --- a/sources/shiboken6/doc/typesystem_specifying_types.rst +++ b/sources/shiboken6/doc/typesystem_specifying_types.rst @@ -401,6 +401,8 @@ object-type hash-function="..." isNull ="yes | no" operator-bool="yes | no" + polymorphic-id-expression="..." + polymorphic-name-function="..." private="yes | no" qt-register-metatype = "yes | no | base" stream="yes | no" @@ -457,6 +459,19 @@ object-type to override the command line setting for generating bool casts (see :ref:`bool-cast`). + The *optional* **polymorphic-id-expression** attribute specifies an + expression checking whether a base class pointer is of the matching + type. For example, in a ``virtual eventHandler(BaseEvent *e)`` + function, this is used to construct a Python wrapper matching + the derived class (for example, a ``MouseEvent`` or similar). + + The *optional* **polymorphic-name-function** specifies the name of a + function returning the type name of a derived class on the base class + type entry. Normally, ``typeid(ptr).name()`` is used for this. + However, this fails if the type hierarchy does not have virtual functions. + In this case, a function is required which typically decides depending + on some type enumeration. + interface-type ^^^^^^^^^^^^^^ diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index b6dc533c8..a3a1dce63 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -1633,8 +1633,14 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas c << "}\n" << "bool changedTypeName = false;\n" << "auto tCppIn = reinterpret_cast(cppIn); -const char *typeName = typeid(*tCppIn).name(); -auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName); +const char *typeName = )"; + + const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction(); + if (nameFunc.isEmpty()) + c << "typeid(*tCppIn).name();\n"; + else + c << nameFunc << "(tCppIn);\n"; + c << R"(auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName); if (sbkType && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) { typeName = typeNameOf(tCppIn); changedTypeName = true; -- cgit v1.2.3