diff options
Diffstat (limited to 'sources/pyside6/PySide6/QtDesigner')
5 files changed, 400 insertions, 0 deletions
diff --git a/sources/pyside6/PySide6/QtDesigner/CMakeLists.txt b/sources/pyside6/PySide6/QtDesigner/CMakeLists.txt new file mode 100644 index 000000000..e91532b87 --- /dev/null +++ b/sources/pyside6/PySide6/QtDesigner/CMakeLists.txt @@ -0,0 +1,70 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +project(QtDesigner) + +qt_wrap_cpp(QtDesigner_static_src "${pyside6_SOURCE_DIR}/qpydesignerextensions.h") +list(APPEND QtDesigner_static_src "${QtDesigner_SOURCE_DIR}/qpydesignercustomwidgetcollection.cpp") + +set(QtDesigner_SRC +${QtDesigner_GEN_DIR}/qabstractextensionfactory_wrapper.cpp +${QtDesigner_GEN_DIR}/qabstractextensionmanager_wrapper.cpp +${QtDesigner_GEN_DIR}/qabstractformbuilder_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesigneractioneditorinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignercontainerextension_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignercustomwidgetcollectioninterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignercustomwidgetinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerdnditeminterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerdynamicpropertysheetextension_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerformeditorinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerformwindowcursorinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerformwindowinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerformwindowmanagerinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerformwindowtoolinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignermembersheetextension_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerobjectinspectorinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerpropertyeditorinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerpropertysheetextension_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignertaskmenuextension_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerwidgetboxinterface_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerwidgetboxinterface_category_wrapper.cpp +${QtDesigner_GEN_DIR}/qdesignerwidgetboxinterface_widget_wrapper.cpp +${QtDesigner_GEN_DIR}/qextensionmanager_wrapper.cpp +${QtDesigner_GEN_DIR}/qextensionfactory_wrapper.cpp +${QtDesigner_GEN_DIR}/qformbuilder_wrapper.cpp +${QtDesigner_GEN_DIR}/qpydesignercustomwidgetcollection_wrapper.cpp +${QtDesigner_GEN_DIR}/qpydesignercontainerextension_wrapper.cpp +${QtDesigner_GEN_DIR}/qpydesignermembersheetextension_wrapper.cpp +${QtDesigner_GEN_DIR}/qpydesignerpropertysheetextension_wrapper.cpp +${QtDesigner_GEN_DIR}/qpydesignertaskmenuextension_wrapper.cpp + +# module is always needed +${QtDesigner_GEN_DIR}/qtdesigner_module_wrapper.cpp +) + +configure_file("${QtDesigner_SOURCE_DIR}/QtDesigner_global.pre.h.in" + "${QtDesigner_BINARY_DIR}/QtDesigner_global.pre.h" @ONLY) +configure_file("${QtDesigner_SOURCE_DIR}/QtDesigner_global.post.h.in" + "${QtDesigner_BINARY_DIR}/QtDesigner_global.post.h" @ONLY) + +set(QtDesigner_include_dirs ${QtDesigner_SOURCE_DIR} + ${QtDesigner_BINARY_DIR} + ${QtCore_GEN_DIR} + ${QtGui_GEN_DIR} + ${QtWidgets_GEN_DIR}) + +set(QtDesigner_libraries pyside6 + ${Qt${QT_MAJOR_VERSION}Designer_LIBRARIES}) + +set(QtDesigner_deps QtWidgets) + +create_pyside_module(NAME QtDesigner + INCLUDE_DIRS QtDesigner_include_dirs + LIBRARIES QtDesigner_libraries + DEPS QtDesigner_deps + TYPESYSTEM_PATH QtDesigner_SOURCE_DIR + SOURCES QtDesigner_SRC + STATIC_SOURCES QtDesigner_static_src + TYPESYSTEM_NAME ${QtDesigner_BINARY_DIR}/typesystem_designer.xml) + +install(FILES ${pyside6_SOURCE_DIR}/qpydesignerextensions.h DESTINATION include/PySide6/QtDesigner) diff --git a/sources/pyside6/PySide6/QtDesigner/QtDesigner_global.post.h.in b/sources/pyside6/PySide6/QtDesigner/QtDesigner_global.post.h.in new file mode 100644 index 000000000..e5537a6cf --- /dev/null +++ b/sources/pyside6/PySide6/QtDesigner/QtDesigner_global.post.h.in @@ -0,0 +1 @@ +#include <qpydesignerextensions.h> diff --git a/sources/pyside6/PySide6/QtDesigner/QtDesigner_global.pre.h.in b/sources/pyside6/PySide6/QtDesigner/QtDesigner_global.pre.h.in new file mode 100644 index 000000000..195acbaa7 --- /dev/null +++ b/sources/pyside6/PySide6/QtDesigner/QtDesigner_global.pre.h.in @@ -0,0 +1,3 @@ +// Not included by the module header +#include <QtDesigner/QExtensionFactory> +#include <QtDesigner/QExtensionManager> diff --git a/sources/pyside6/PySide6/QtDesigner/qpydesignercustomwidgetcollection.cpp b/sources/pyside6/PySide6/QtDesigner/qpydesignercustomwidgetcollection.cpp new file mode 100644 index 000000000..31f8ec152 --- /dev/null +++ b/sources/pyside6/PySide6/QtDesigner/qpydesignercustomwidgetcollection.cpp @@ -0,0 +1,223 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <qpydesignerextensions.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QVariant> + +#include <shiboken.h> +#include <bindingmanager.h> + +QT_BEGIN_NAMESPACE + +static QString pyStringToQString(PyObject *s) +{ + const char *utf8 = _PepUnicode_AsString(s); + return utf8 ? QString::fromUtf8(utf8) : QString(); +} + +// Return a string from keyword argument dict +static QString kwdString(PyObject *kwds, PyObject *key) +{ + QString result; + if (PyDict_Contains(kwds, key)) { + if (auto value = PyDict_GetItem(kwds, key)) + result = pyStringToQString(value); + } + return result; +} + +// Return a bool from keyword argument dict +static bool kwdBool(PyObject *kwds, PyObject *key) +{ + bool result = false; + if (PyDict_Contains(kwds, key)) { + if (auto value = PyDict_GetItem(kwds, key)) + result = PyObject_IsTrue(value); + } + return result; +} + +// PyDesignerCustomWidget: A custom widget registered by type +// (similar to what is done by QUiLoader.registerCustomWidget()). +class PyDesignerCustomWidget : public QDesignerCustomWidgetInterface +{ +public: + explicit PyDesignerCustomWidget(PyObject *pyTypeObject) : + m_pyTypeObject(pyTypeObject) {} + + QString name() const override; + QString group() const override { return m_group; } + QString toolTip() const override { return m_toolTip; } + QString whatsThis() const override { return toolTip(); } + QString includeFile() const override { return m_includeFile; } + QIcon icon() const override { return m_icon; } + bool isContainer() const override { return m_container; } + + QWidget *createWidget(QWidget *parent) override; + + bool isInitialized() const override { return m_core != nullptr; } + void initialize(QDesignerFormEditorInterface *core) override; + + QString domXml() const override { return m_domXml; } + + void setGroup(const QString &group) { m_group = group; } + void setToolTip(const QString &toolTip) { m_toolTip = toolTip; } + void setIncludeFile(const QString &includeFile) { m_includeFile = includeFile; } + void setIcon(const QIcon &icon) { m_icon = icon; } + void setDomXml(const QString &domXml) { m_domXml = domXml; } + void setContainer(bool container) { m_container = container; } + +private: + const char *utf8Name() const; + + QDesignerFormEditorInterface *m_core = nullptr; + QString m_group; + QString m_toolTip; + QString m_includeFile; + QIcon m_icon; + QString m_domXml; + PyObject *m_pyTypeObject = nullptr; + bool m_container = false; +}; + +const char *PyDesignerCustomWidget::utf8Name() const +{ + return reinterpret_cast<PyTypeObject *>(m_pyTypeObject)->tp_name; +} + +QString PyDesignerCustomWidget::name() const +{ + return QString::fromUtf8(utf8Name()); +} + +QWidget *PyDesignerCustomWidget::createWidget(QWidget *parent) +{ + // This is a copy of the similar function used for QUiLoader + // (see sources/pyside6/plugins/uitools/customwidget.cpp) + // Create a python instance and return cpp object + PyObject *pyParent = nullptr; + bool unknownParent = false; + if (parent) { + pyParent = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(parent)); + if (pyParent) { + Py_INCREF(pyParent); + } else { + static Shiboken::Conversions::SpecificConverter converter("QWidget*"); + pyParent = converter.toPython(&parent); + unknownParent = true; + } + } else { + Py_INCREF(Py_None); + pyParent = Py_None; + } + + Shiboken::AutoDecRef pyArgs(PyTuple_New(1)); + PyTuple_SET_ITEM(pyArgs, 0, pyParent); // tuple will keep pyParent reference + + // Call python constructor + auto result = reinterpret_cast<SbkObject *>(PyObject_CallObject(m_pyTypeObject, pyArgs)); + if (!result) { + qWarning("Unable to create a Python custom widget of type \"%s\".", utf8Name()); + PyErr_Print(); + return nullptr; + } + + if (unknownParent) // if parent does not exist in python, transfer the ownership to cpp + Shiboken::Object::releaseOwnership(result); + else + Shiboken::Object::setParent(pyParent, reinterpret_cast<PyObject *>(result)); + + return reinterpret_cast<QWidget *>(Shiboken::Object::cppPointer(result, Py_TYPE(result))); +} + +void PyDesignerCustomWidget::initialize(QDesignerFormEditorInterface *core) +{ + m_core = core; +} + +// QPyDesignerCustomWidgetCollection: A QDesignerCustomWidgetCollectionInterface +// implementation that is instantiated as a singleton and stored as a dynamic +// property of QCoreApplication. The PySide Designer plugin retrieves it from +// there. Provides static convenience functions for registering types +// or adding QDesignerCustomWidgetInterface instances. + +static QPyDesignerCustomWidgetCollection *collectionInstance = nullptr; + +static const char propertyName[] = "__qt_PySideCustomWidgetCollection"; + +static void cleanup() +{ + delete collectionInstance; + collectionInstance = nullptr; +} + +QPyDesignerCustomWidgetCollection::QPyDesignerCustomWidgetCollection() = default; + +QPyDesignerCustomWidgetCollection::~QPyDesignerCustomWidgetCollection() +{ + qDeleteAll(m_customWidgets); +} + +QList<QDesignerCustomWidgetInterface *> QPyDesignerCustomWidgetCollection::customWidgets() const +{ + return m_customWidgets; +} + +QPyDesignerCustomWidgetCollection *QPyDesignerCustomWidgetCollection::instance() +{ + if (collectionInstance == nullptr) { + collectionInstance = new QPyDesignerCustomWidgetCollection(); + if (auto coreApp = QCoreApplication::instance()) { + QDesignerCustomWidgetCollectionInterface *c = collectionInstance; + coreApp->setProperty(propertyName, QVariant::fromValue<void *>(c)); + qAddPostRoutine(cleanup); + } else { + qWarning("%s: Cannot find QCoreApplication instance.", Q_FUNC_INFO); + } + } + return collectionInstance; +} + +// Register a custom widget by type and optional keyword arguments providing +// the parameters of QDesignerCustomWidgetInterface. +bool QPyDesignerCustomWidgetCollection::_registerCustomWidgetHelper(PyObject *typeArg, PyObject *kwds) +{ + if (!PyType_Check(typeArg)) { + PyErr_SetString(PyExc_TypeError, "registerCustomWidget() requires a type argument."); + return false; + } + + auto pyCustomWidget = new PyDesignerCustomWidget(typeArg); + + static PyObject *xmlKey = Shiboken::String::createStaticString("xml"); + pyCustomWidget->setDomXml(kwdString(kwds, xmlKey)); + static PyObject *toolTipKey = Shiboken::String::createStaticString("tool_tip"); + pyCustomWidget->setToolTip(kwdString(kwds, toolTipKey)); + static PyObject *groupKey = Shiboken::String::createStaticString("group"); + pyCustomWidget->setGroup(kwdString(kwds, groupKey)); + static PyObject *moduleKey = Shiboken::String::createStaticString("module"); + pyCustomWidget->setIncludeFile(kwdString(kwds, moduleKey)); + static PyObject *containerKey = Shiboken::String::createStaticString("container"); + pyCustomWidget->setContainer(kwdBool(kwds, containerKey)); + static PyObject *iconKey = Shiboken::String::createStaticString("icon"); + const QString iconPath = kwdString(kwds, iconKey); + if (!iconPath.isEmpty()) { + QIcon icon(iconPath); + if (icon.availableSizes().isEmpty()) + qWarning("%s: Cannot load icon from '%s'.", __FUNCTION__, qPrintable(iconPath)); + else + pyCustomWidget->setIcon(icon); + } + + addCustomWidget(pyCustomWidget); + return true; +} + +void QPyDesignerCustomWidgetCollection::addCustomWidget(QDesignerCustomWidgetInterface *c) +{ + instance()->m_customWidgets.append(c); +} + +QT_END_NAMESPACE diff --git a/sources/pyside6/PySide6/QtDesigner/typesystem_designer.xml b/sources/pyside6/PySide6/QtDesigner/typesystem_designer.xml new file mode 100644 index 000000000..7d37cbddf --- /dev/null +++ b/sources/pyside6/PySide6/QtDesigner/typesystem_designer.xml @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +--> +<typesystem package="PySide6.QtDesigner" + namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE"> + <load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/> + + <rejection class="qdesigner_internal"/> + + <object-type name="QAbstractExtensionFactory"/> + <object-type name="QAbstractExtensionManager"/> + <object-type name="QAbstractFormBuilder"/> + <object-type name="QDesignerActionEditorInterface"/> + <object-type name="QDesignerContainerExtension"/> + <object-type name="QDesignerCustomWidgetCollectionInterface"/> + <object-type name="QDesignerCustomWidgetInterface"> + <modify-function signature="createWidget(QWidget*)"> + <modify-argument index="return"> + <define-ownership class="native" owner="c++"/> + </modify-argument> + </modify-function> + </object-type> + <!-- QDesignerDnDItemInterface has abstract functions dealing with the + unexposed DomUI classes, we cannot generate a wrapper. --> + <object-type name="QDesignerDnDItemInterface" disable-wrapper="yes"> + <enum-type name="DropType"/> + </object-type> + <object-type name="QDesignerDynamicPropertySheetExtension"/> + <object-type name="QDesignerFormEditorInterface"/> + <object-type name="QDesignerFormWindowCursorInterface"> + <enum-type name="MoveMode"/> + <enum-type name="MoveOperation"/> + </object-type> + <!-- QDesignerFormWindowInterface has abstract functions dealing with private + class QtResourceSet, so, we cannot generate a wrapper. --> + <object-type name="QDesignerFormWindowInterface" disable-wrapper="yes"> + <enum-type name="FeatureFlag" flags="Feature"/> + <enum-type name="ResourceFileSaveMode"/> + </object-type> + <object-type name="QDesignerFormWindowManagerInterface"> + <enum-type name="Action"/> + <enum-type name="ActionGroup"/> + </object-type> + <object-type name="QDesignerFormWindowToolInterface"/> + <object-type name="QDesignerMemberSheetExtension"/> + <object-type name="QDesignerObjectInspectorInterface"/> + <object-type name="QDesignerPropertyEditorInterface"/> + <object-type name="QDesignerPropertySheetExtension"/> + <object-type name="QDesignerTaskMenuExtension"/> + <object-type name="QDesignerWidgetBoxInterface"> + <value-type name="Category"> + <enum-type name="Type"/> + </value-type> + <value-type name="Widget"> + <enum-type name="Type"/> + </value-type> + </object-type> + <object-type name="QExtensionManager"/> + <object-type name="QExtensionFactory"/> + <object-type name="QFormBuilder"/> + <object-type name="QPyDesignerCustomWidgetCollection" disable-wrapper="yes"> + <extra-includes> + <include file-name="qpydesignerextensions.h" location="global"/> + </extra-includes> + <modify-function signature="addCustomWidget(QDesignerCustomWidgetInterface*)"> + <modify-argument index="1"> + <define-ownership owner="c++"/> + </modify-argument> + </modify-function> + <!-- Force VARARGS/keyword arguments by giving a default parameters --> + <add-function signature='registerCustomWidget(PyObject*@customWidgetType@,const QString& @xml@ = {}, const QString& @tool_tip@ = {}, const QString& @group@ = {}, const QString& @module@ = {}, bool @container@ = false, const QString& @icon@ = {})' + return-type='void' static='true'> + <inject-code class="target" position="beginning" file="../glue/qtdesigner.cpp" snippet="qtdesigner-registercustomwidget"/> + </add-function> + </object-type> + <object-type name="QPyDesignerContainerExtension"> + <extra-includes> + <include file-name="qpydesignerextensions.h" location="global"/> + </extra-includes> + </object-type> + <object-type name="QPyDesignerMemberSheetExtension"> + <extra-includes> + <include file-name="qpydesignerextensions.h" location="global"/> + </extra-includes> + </object-type> + <object-type name="QPyDesignerPropertySheetExtension"> + <extra-includes> + <include file-name="qpydesignerextensions.h" location="global"/> + </extra-includes> + </object-type> + <object-type name="QPyDesignerTaskMenuExtension"> + <extra-includes> + <include file-name="qpydesignerextensions.h" location="global"/> + </extra-includes> + </object-type> + + <!-- Suppress all QString */int * out parameters --> + <suppress-warning text="^There's no user provided way.*handle the primitive type.*$"/> + <suppress-warning text="^.*skipping abstract function.*QtResourceSet.*$"/> + <suppress-warning text="^.*skipping abstract function.*DomUI.*$"/> +</typesystem> |