aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/pythonextensions
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/pythonextensions')
-rw-r--r--plugins/pythonextensions/PythonExtensions.json.in18
-rw-r--r--plugins/pythonextensions/bindingheaders/core.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_actioncontainer.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_actionmanager.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_command.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_constants.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_context.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_documentmanager.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_documentmodel.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_editormanager.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_fileutils.h33
-rw-r--r--plugins/pythonextensions/bindingheaders/core_icontext.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_icore.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_id.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_idocument.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_ieditor.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/core_messagemanager.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/extensionsystem.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/extensionsystem_iplugin.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/pythonextensions.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/pythonextensions_internal.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/pythonextensions_internal_pythonextensionsplugin.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/utils.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/utils_filename.h0
-rw-r--r--plugins/pythonextensions/bindingheaders/utils_macroexpander.h0
-rw-r--r--plugins/pythonextensions/glue/macroexpander_glue.cpp66
-rw-r--r--plugins/pythonextensions/pyside2.pri42
-rw-r--r--plugins/pythonextensions/pythonextensions.pro126
-rw-r--r--plugins/pythonextensions/pythonextensions_dependencies.pri12
-rw-r--r--plugins/pythonextensions/pythonextensions_global.h34
-rw-r--r--plugins/pythonextensions/pythonextensionsplugin.cpp295
-rw-r--r--plugins/pythonextensions/pythonextensionsplugin.h74
-rw-r--r--plugins/pythonextensions/pyutil.cpp271
-rw-r--r--plugins/pythonextensions/pyutil.h72
-rw-r--r--plugins/pythonextensions/qtcreator.pri15
-rw-r--r--plugins/pythonextensions/typesystem_qtcreator.xml237
-rw-r--r--plugins/pythonextensions/wrappedclasses.h50
37 files changed, 1345 insertions, 0 deletions
diff --git a/plugins/pythonextensions/PythonExtensions.json.in b/plugins/pythonextensions/PythonExtensions.json.in
new file mode 100644
index 0000000..e6bf147
--- /dev/null
+++ b/plugins/pythonextensions/PythonExtensions.json.in
@@ -0,0 +1,18 @@
+{
+ \"Name\" : \"PythonExtensions\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin 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 plugin. 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.\"
+ ],
+ \"Description\" : \"This plugin manages and runs Python extensions.\",
+ \"Url\" : \"https://www.qt.io\",
+ $$dependencyList
+}
diff --git a/plugins/pythonextensions/bindingheaders/core.h b/plugins/pythonextensions/bindingheaders/core.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core.h
diff --git a/plugins/pythonextensions/bindingheaders/core_actioncontainer.h b/plugins/pythonextensions/bindingheaders/core_actioncontainer.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_actioncontainer.h
diff --git a/plugins/pythonextensions/bindingheaders/core_actionmanager.h b/plugins/pythonextensions/bindingheaders/core_actionmanager.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_actionmanager.h
diff --git a/plugins/pythonextensions/bindingheaders/core_command.h b/plugins/pythonextensions/bindingheaders/core_command.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_command.h
diff --git a/plugins/pythonextensions/bindingheaders/core_constants.h b/plugins/pythonextensions/bindingheaders/core_constants.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_constants.h
diff --git a/plugins/pythonextensions/bindingheaders/core_context.h b/plugins/pythonextensions/bindingheaders/core_context.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_context.h
diff --git a/plugins/pythonextensions/bindingheaders/core_documentmanager.h b/plugins/pythonextensions/bindingheaders/core_documentmanager.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_documentmanager.h
diff --git a/plugins/pythonextensions/bindingheaders/core_documentmodel.h b/plugins/pythonextensions/bindingheaders/core_documentmodel.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_documentmodel.h
diff --git a/plugins/pythonextensions/bindingheaders/core_editormanager.h b/plugins/pythonextensions/bindingheaders/core_editormanager.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_editormanager.h
diff --git a/plugins/pythonextensions/bindingheaders/core_fileutils.h b/plugins/pythonextensions/bindingheaders/core_fileutils.h
new file mode 100644
index 0000000..b726b41
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_fileutils.h
@@ -0,0 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Python Extensions Plugin for QtCreator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifdef SBK_QTCREATORPYTHON_PYTHON_H
+#ifndef BINDINGHEADERS_FILE_UTILS_H
+#define BINDINGHEADERS_FILE_UTILS_H
+
+#include <coreplugin/fileutils.h>
+
+#endif
+#endif
diff --git a/plugins/pythonextensions/bindingheaders/core_icontext.h b/plugins/pythonextensions/bindingheaders/core_icontext.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_icontext.h
diff --git a/plugins/pythonextensions/bindingheaders/core_icore.h b/plugins/pythonextensions/bindingheaders/core_icore.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_icore.h
diff --git a/plugins/pythonextensions/bindingheaders/core_id.h b/plugins/pythonextensions/bindingheaders/core_id.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_id.h
diff --git a/plugins/pythonextensions/bindingheaders/core_idocument.h b/plugins/pythonextensions/bindingheaders/core_idocument.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_idocument.h
diff --git a/plugins/pythonextensions/bindingheaders/core_ieditor.h b/plugins/pythonextensions/bindingheaders/core_ieditor.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_ieditor.h
diff --git a/plugins/pythonextensions/bindingheaders/core_messagemanager.h b/plugins/pythonextensions/bindingheaders/core_messagemanager.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/core_messagemanager.h
diff --git a/plugins/pythonextensions/bindingheaders/extensionsystem.h b/plugins/pythonextensions/bindingheaders/extensionsystem.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/extensionsystem.h
diff --git a/plugins/pythonextensions/bindingheaders/extensionsystem_iplugin.h b/plugins/pythonextensions/bindingheaders/extensionsystem_iplugin.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/extensionsystem_iplugin.h
diff --git a/plugins/pythonextensions/bindingheaders/pythonextensions.h b/plugins/pythonextensions/bindingheaders/pythonextensions.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/pythonextensions.h
diff --git a/plugins/pythonextensions/bindingheaders/pythonextensions_internal.h b/plugins/pythonextensions/bindingheaders/pythonextensions_internal.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/pythonextensions_internal.h
diff --git a/plugins/pythonextensions/bindingheaders/pythonextensions_internal_pythonextensionsplugin.h b/plugins/pythonextensions/bindingheaders/pythonextensions_internal_pythonextensionsplugin.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/pythonextensions_internal_pythonextensionsplugin.h
diff --git a/plugins/pythonextensions/bindingheaders/utils.h b/plugins/pythonextensions/bindingheaders/utils.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/utils.h
diff --git a/plugins/pythonextensions/bindingheaders/utils_filename.h b/plugins/pythonextensions/bindingheaders/utils_filename.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/utils_filename.h
diff --git a/plugins/pythonextensions/bindingheaders/utils_macroexpander.h b/plugins/pythonextensions/bindingheaders/utils_macroexpander.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/pythonextensions/bindingheaders/utils_macroexpander.h
diff --git a/plugins/pythonextensions/glue/macroexpander_glue.cpp b/plugins/pythonextensions/glue/macroexpander_glue.cpp
new file mode 100644
index 0000000..606bf81
--- /dev/null
+++ b/plugins/pythonextensions/glue/macroexpander_glue.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Python Extensions Plugin for QtCreator.
+**
+** 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.
+**
+****************************************************************************/
+
+void registerPythonVariable(const QByteArray &variable, const QString &description, PyObject *func)
+{
+ if (!PyCallable_Check(func)) {
+ PyErr_BadArgument();
+ return;
+ }
+ Py_XINCREF(func);
+
+ globalMacroExpander()->registerVariable(variable, description,
+ [=]() -> QString {
+ PyObject *ret = PyObject_Repr(PyObject_CallFunction(func, NULL));
+ #if PY_MAJOR_VERSION >= 3
+ QString value = QString::fromUtf8(PyUnicode_AsUTF8(ret));
+ #else
+ QString value = QString::fromUtf8(PyString_AsString(ret));
+ #endif
+ return value;
+ }
+ );
+}
+
+void registerPythonPrefixVariable(const QByteArray &variable, const QString &description, PyObject *func)
+{
+ if (!PyCallable_Check(func)) {
+ PyErr_BadArgument();
+ return;
+ }
+ Py_XINCREF(func);
+
+ globalMacroExpander()->registerPrefix(variable, description,
+ [=](QString passed_string) -> QString {
+ PyObject *ret = PyObject_Repr(PyObject_CallFunction(func, "s", passed_string.toStdString().c_str()));
+ #if PY_MAJOR_VERSION >= 3
+ QString value = QString::fromUtf8(PyUnicode_AsUTF8(ret));
+ #else
+ QString value = QString::fromUtf8(PyString_AsString(ret));
+ #endif
+ return value;
+ }
+ );
+}
diff --git a/plugins/pythonextensions/pyside2.pri b/plugins/pythonextensions/pyside2.pri
new file mode 100644
index 0000000..3e6cc23
--- /dev/null
+++ b/plugins/pythonextensions/pyside2.pri
@@ -0,0 +1,42 @@
+# Discover PySide2 configuration
+
+PYSIDE_CONFIG = ../../tools/pyside2_config.py
+
+PYSIDE2 = $$system($$PYTHON $$PYSIDE_CONFIG --pyside2)
+isEmpty(PYSIDE2): error(Unable to locate the PySide2 package location)
+
+PYTHON_INCLUDE = $$system($$PYTHON $$PYSIDE_CONFIG --python-include)
+isEmpty(PYTHON_INCLUDE): error(Unable to locate the Python include headers directory)
+
+PYTHON_LFLAGS = $$system($$PYTHON $$PYSIDE_CONFIG --python-link)
+isEmpty(PYTHON_LFLAGS): error(Unable to locate the Python library for linking)
+
+PYSIDE2_INCLUDE = $$system($$PYTHON $$PYSIDE_CONFIG --pyside2-include)
+isEmpty(PYSIDE2_INCLUDE): error(Unable to locate the PySide2 include headers directory)
+
+PYSIDE2_LFLAGS = $$system($$PYTHON $$PYSIDE_CONFIG --pyside2-link)
+isEmpty(PYSIDE2_LFLAGS): error(Unable to locate the PySide2 libraries for linking)
+
+PYSIDE2_SHARED_LIBRARIES = $$system($$PYTHON $$PYSIDE_CONFIG --pyside2-shared-libraries)
+isEmpty(PYSIDE2_SHARED_LIBRARIES): error(Unable to locate the used PySide2 shared libraries)
+
+INCLUDEPATH += "$$PYTHON_INCLUDE" $$PYSIDE2_INCLUDE
+LIBS += $$PYTHON_LFLAGS $$PYSIDE2_LFLAGS
+!build_pass:message(INCLUDEPATH is $$INCLUDEPATH)
+!build_pass:message(LIBS are $$LIBS)
+
+!build_pass:message(Using $$PYSIDE2)
+
+!win32 {
+ QMAKE_RPATHDIR += $$PYSIDE2
+}
+
+# Needed to fix Python dynamic linking problems
+# see pyutil.cpp
+LIBS += -ldl
+
+# Suppress non-relevant warnings from
+# Shiboken generated code
+QMAKE_CXXFLAGS += \
+ -Wno-missing-field-initializers \
+ -Wno-unused-parameter
diff --git a/plugins/pythonextensions/pythonextensions.pro b/plugins/pythonextensions/pythonextensions.pro
new file mode 100644
index 0000000..d3c5119
--- /dev/null
+++ b/plugins/pythonextensions/pythonextensions.pro
@@ -0,0 +1,126 @@
+# NOTE: This is not yet properly tested on general systems
+# and will (if at all) probably only work on a linux environment
+
+PYTHON = python
+DEFINES += PYTHONEXTENSIONS_LIBRARY
+
+# PythonExtensions files
+
+SOURCES += \
+ pythonextensionsplugin.cpp \
+ pyutil.cpp
+
+HEADERS += \
+ pythonextensionsplugin.h \
+ pythonextensions_global.h \
+ pyutil.h
+
+
+# Qt Creator linking
+
+# Shared QtCreator sources and build destination
+# (these are shared by this and the optional bindings)
+include(qtcreator.pri)
+
+# Declare dependencies
+include(pythonextensions_dependencies.pri)
+
+include($$IDE_SOURCE_TREE/src/qtcreatorplugin.pri)
+
+# Install extension manager extension during first build
+copyextensions.commands = $(COPY_DIR) $$PWD/../../python $$DESTDIR/
+first.depends = $(first) copyextensions
+export(first.depends)
+export(copyextensions.commands)
+QMAKE_EXTRA_TARGETS += first copyextensions
+
+# Shiboken stuff
+
+# This setup is currently only tested on Linux
+
+WRAPPED_HEADER = wrappedclasses.h
+WRAPPER_DIR = $$OUT_PWD/PythonExtension/QtCreator
+TYPESYSTEM_FILE = typesystem_qtcreator.xml
+
+include(pyside2.pri)
+
+## Include Qt and QtCreator paths
+QT_INCLUDEPATHS = -I"$$[QT_INSTALL_HEADERS]" -I"$$[QT_INSTALL_HEADERS]/QtCore" \
+ -I"$$[QT_INSTALL_HEADERS]/QtGui" -I"$$[QT_INSTALL_HEADERS]/QtWidgets" \
+ -I"$$IDE_SOURCE_TREE/src/plugins" \
+ -I"$$IDE_SOURCE_TREE/src/plugins/coreplugin" \
+ -I"$$IDE_SOURCE_TREE/src/libs"
+
+SHIBOKEN_OPTIONS = --generator-set=shiboken --enable-parent-ctor-heuristic \
+ --enable-pyside-extensions --enable-return-value-heuristic --use-isnull-as-nb_nonzero \
+ $$QT_INCLUDEPATHS -I$$PWD -T$$PWD -T$$PYSIDE2/typesystems --output-directory=$$OUT_PWD
+
+## Prepare the shiboken tool
+QT_TOOL.shiboken.binary = $$system_path($$PYSIDE2/shiboken2)
+qtPrepareTool(SHIBOKEN, shiboken)
+
+## Shiboken run that adds the module wrapper to GENERATED_SOURCES
+shiboken.output = $$WRAPPER_DIR/qtcreator_module_wrapper.cpp
+shiboken.commands = $$SHIBOKEN $$SHIBOKEN_OPTIONS $$PWD/wrappedclasses.h ${QMAKE_FILE_IN}
+shiboken.input = TYPESYSTEM_FILE
+shiboken.dependency_type = TYPE_C
+shiboken.variable_out = GENERATED_SOURCES
+
+## These headers are needed so the generated wrappers are added to the
+## build. Right now they are empty files, however there might be a more elegant
+## option.
+WRAPPED_CLASSES = \
+ bindingheaders/pythonextensions.h \
+ bindingheaders/pythonextensions_internal.h \
+ bindingheaders/pythonextensions_internal_pythonextensionsplugin.h \
+ bindingheaders/core.h \
+ bindingheaders/core_actioncontainer.h \
+ bindingheaders/core_actionmanager.h \
+ bindingheaders/core_command.h \
+ bindingheaders/core_constants.h \
+ bindingheaders/core_icontext.h \
+ bindingheaders/core_icore.h \
+ bindingheaders/core_id.h \
+ bindingheaders/core_context.h \
+ bindingheaders/core_editormanager.h \
+ bindingheaders/core_ieditor.h \
+ bindingheaders/core_idocument.h \
+ bindingheaders/core_documentmanager.h \
+ bindingheaders/core_documentmodel.h \
+ bindingheaders/core_fileutils.h \
+ bindingheaders/core_messagemanager.h \
+ bindingheaders/utils.h \
+ bindingheaders/utils_macroexpander.h \
+ bindingheaders/utils_filename.h \
+ bindingheaders/extensionsystem.h \
+ bindingheaders/extensionsystem_iplugin.h
+# Sentinel line
+
+module_wrapper_dummy_command.output = $$WRAPPER_DIR/${QMAKE_FILE_BASE}_wrapper.cpp
+module_wrapper_dummy_command.commands = echo ${QMAKE_FILE_IN}
+module_wrapper_dummy_command.depends = $$WRAPPER_DIR/qtcreator_module_wrapper.cpp
+module_wrapper_dummy_command.input = WRAPPED_CLASSES
+module_wrapper_dummy_command.dependency_type = TYPE_C
+module_wrapper_dummy_command.variable_out = GENERATED_SOURCES
+
+## Get the path component to the active config build folder
+defineReplace(getOutDir) {
+ out_dir = $$OUT_PWD
+ CONFIG(release, debug|release): out_dir = $$out_dir/release
+ else:out_dir = $$out_dir/debug
+ return($$out_dir)
+}
+
+QMAKE_EXTRA_COMPILERS += shiboken module_wrapper_dummy_command
+
+# TODO: Fix some more of these hardcoded include paths
+INCLUDEPATH += $$WRAPPER_DIR \
+ "$$IDE_SOURCE_TREE/src/plugins/coreplugin" \
+ "$$IDE_SOURCE_TREE/src/plugins/coreplugin/actionmanager" \
+ "$$IDE_SOURCE_TREE/src/plugins/coreplugin/editormanager" \
+ "$$IDE_SOURCE_TREE/src/libs/extensionsystem" \
+ "$$IDE_SOURCE_TREE/src/libs/utils"
+
+for(i, PYSIDE2_INCLUDE) {
+ INCLUDEPATH += $$i/QtWidgets $$i/QtGui $$i/QtCore
+}
diff --git a/plugins/pythonextensions/pythonextensions_dependencies.pri b/plugins/pythonextensions/pythonextensions_dependencies.pri
new file mode 100644
index 0000000..4a0b56e
--- /dev/null
+++ b/plugins/pythonextensions/pythonextensions_dependencies.pri
@@ -0,0 +1,12 @@
+# Declare dependencies
+
+QTC_PLUGIN_NAME = PythonExtensions
+QTC_LIB_DEPENDS += \
+ extensionsystem \
+ utils
+
+QTC_PLUGIN_DEPENDS += \
+ coreplugin
+
+QTC_PLUGIN_RECOMMENDS += \
+ # optional plugin dependencies. nothing here at this time
diff --git a/plugins/pythonextensions/pythonextensions_global.h b/plugins/pythonextensions/pythonextensions_global.h
new file mode 100644
index 0000000..6f0dab7
--- /dev/null
+++ b/plugins/pythonextensions/pythonextensions_global.h
@@ -0,0 +1,34 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Python Extensions Plugin for QtCreator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QtGlobal>
+
+#if defined(PYTHONEXTENSIONS_LIBRARY)
+# define PYTHONEXTENSIONSSHARED_EXPORT Q_DECL_EXPORT
+#else
+# define PYTHONEXTENSIONSSHARED_EXPORT Q_DECL_IMPORT
+#endif
diff --git a/plugins/pythonextensions/pythonextensionsplugin.cpp b/plugins/pythonextensions/pythonextensionsplugin.cpp
new file mode 100644
index 0000000..078a78c
--- /dev/null
+++ b/plugins/pythonextensions/pythonextensionsplugin.cpp
@@ -0,0 +1,295 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Python Extensions Plugin for QtCreator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "pythonextensionsplugin.h"
+
+#include "pyutil.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/command.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/messagemanager.h>
+
+#include <extensionsystem/pluginmanager.h>
+#include <extensionsystem/pluginspec.h>
+
+#include <QDir>
+#include <QIODevice>
+#include <QFile>
+#include <QDir>
+#include <QTextStream>
+#include <QString>
+#include <QStringList>
+#include <QLibrary>
+
+
+namespace PythonExtensions {
+namespace Constants {
+
+const char EXTENSIONS_DIR[] = "/python";
+const char PY_PACKAGES_DIR[] = "/site-packages";
+
+const char PY_BINDING_LIB[] = "/libPythonBinding";
+
+const char MESSAGE_MANAGER_PREFIX[] = "Python Extensions: ";
+
+} // namespace Constants
+namespace Internal {
+
+PythonExtensionsPlugin::PythonExtensionsPlugin()
+{
+ // Empty
+}
+
+PythonExtensionsPlugin::~PythonExtensionsPlugin()
+{
+ // Unregister objects from the plugin manager's object pool
+ // Delete members
+}
+
+bool PythonExtensionsPlugin::initialize(const QStringList &arguments, QString *errorString)
+{
+ // Register objects in the plugin manager's object pool
+ // Load settings
+ // Add actions to menus
+ // Connect to other plugins' signals
+ // In the initialize function, a plugin can be sure that the plugins it
+ // depends on have initialized their members.
+
+ Q_UNUSED(arguments)
+ Q_UNUSED(errorString)
+
+ initializePythonBindings();
+
+ // Python extensions are loaded after C++ plugins for now (plan: later flag can be set)
+
+ return true;
+}
+
+void PythonExtensionsPlugin::extensionsInitialized()
+{
+ // Retrieve objects from the plugin manager's object pool
+ // In the extensionsInitialized function, a plugin can be sure that all
+ // plugins that depend on it are completely initialized.
+}
+
+bool PythonExtensionsPlugin::delayedInitialize()
+{
+ // Initialize optional bindings
+ initializeOptionalBindings();
+ // Pip install any requirements known for the script
+ installRequirements();
+ // Run the setup for each extension that requires it
+ setupPythonExtensions();
+ // Python plugins are initialized here, to avoid blocking on startup
+ initializePythonExtensions();
+ return true;
+}
+
+ExtensionSystem::IPlugin::ShutdownFlag PythonExtensionsPlugin::aboutToShutdown()
+{
+ // Save settings
+ // Disconnect from signals that are not needed during shutdown
+ // Hide UI (if you add UI that is not in the main window directly)
+ return SynchronousShutdown;
+}
+
+QDir PythonExtensionsPlugin::extensionDir()
+{
+ // Search python directory in plugin paths
+ QDir extension_dir;
+ for (const QString &path : ExtensionSystem::PluginManager::pluginPaths()) {
+ extension_dir = QDir(path + Constants::EXTENSIONS_DIR);
+ if (extension_dir.exists())
+ break;
+ }
+ // Can be checked for validity with .exists()
+ return extension_dir;
+}
+
+QStringList PythonExtensionsPlugin::extensionList(const bool loadedOnly)
+{
+ if (loadedOnly)
+ return m_loadedExtensions;
+
+ QDir extension_dir = extensionDir();
+ if (!extension_dir.exists())
+ return QStringList();
+
+ QStringList extension_list = extension_dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
+ extension_list.removeOne("site-packages");
+ return extension_list;
+}
+
+void PythonExtensionsPlugin::flagAsLoaded(const QString &extension)
+{
+ m_loadedExtensions << extension;
+}
+
+QString PythonExtensionsPlugin::pythonPackagePath()
+{
+ if (extensionDir().exists()) {
+ return extensionDir().absolutePath() + Constants::PY_PACKAGES_DIR;
+ } else {
+ return QString();
+ }
+}
+
+void PythonExtensionsPlugin::initializePythonBindings()
+{
+ // Add our custom module directory
+ if (extensionDir().exists())
+ PyUtil::addToSysPath(pythonPackagePath().toStdString());
+ // Initialize the Python context and register global QtCreator variable
+ if (!PyUtil::bindShibokenModuleObject("PythonExtension", "QtCreator")) {
+ qWarning() << "Python bindings could not be initialized";
+ Core::MessageManager::write(Constants::MESSAGE_MANAGER_PREFIX + tr("Python bindings could not be initialized"));
+ return;
+ }
+ // Bind the plugin instance
+ PyUtil::bindObject("PythonExtension", "PluginInstance", PyUtil::PythonExtensionsPluginType, this);
+}
+
+void PythonExtensionsPlugin::initializeOptionalBindings()
+{
+ // Try to load optional bindings for all loaded plugins
+ // If a plugin has optional bindings, they occur in the form of
+ // a shared object which has the name libPythonBinding{PluginName}
+ // and exposes a symbol called `bind' which is a void function taking
+ // no arguments. This function is responsible for binding the
+ // object using the exposed PyUtil api and for reporting any errors
+ // etc. to the stderr / stdout.
+ // Examples of projects for such libraries exist within this repository.
+ for (int i = 0; i < ExtensionSystem::PluginManager::loadQueue().size(); i++) {
+ // Check each plugin directory for the library (first found is used)
+ QString name = ExtensionSystem::PluginManager::loadQueue()[i]->name();
+ for (const QString &path : ExtensionSystem::PluginManager::pluginPaths()) {
+ QLibrary bindingLib(path + Constants::PY_BINDING_LIB + name);
+ QFunctionPointer bind = bindingLib.resolve("bind");
+ if (bind) {
+ qDebug() << "Initializing optional bindings for plugin" << name;
+ bind();
+ break;
+ }
+ }
+ }
+}
+
+void PythonExtensionsPlugin::installRequirements()
+{
+ // Pip install any requirements.txt file found
+ QDir extension_dir = extensionDir();
+ if (!extension_dir.exists())
+ return;
+
+ QStringList extension_list = extensionList();
+ for (const QString &extension : extension_list) {
+ QString extension_requirements(extension_dir.absolutePath() + "/" + extension + "/requirements.txt");
+ if (QFileInfo::exists(extension_requirements) && !QFileInfo::exists(extension_requirements + ".installed")) {
+ if (!PyUtil::pipInstallRequirements(
+ extension_requirements.toStdString(),
+ pythonPackagePath().toStdString()
+ )) {
+ qWarning() << "Failed to install requirements for extension" << extension;
+ Core::MessageManager::write(Constants::MESSAGE_MANAGER_PREFIX + tr("Failed to install requirements for extension ") + extension);
+ }
+ }
+ }
+}
+
+void PythonExtensionsPlugin::setupPythonExtensions()
+{
+ // Run the setup.py file for all extensions that provide it.
+ // later, there might be a way to determine if the setup needs
+ // to run.
+ QDir extension_dir = extensionDir();
+ if (!extension_dir.exists())
+ return;
+
+ QStringList extension_list = extensionList();
+ for (const QString &extension : extension_list) {
+ QFile extension_setup(extension_dir.absolutePath() + "/" + extension + "/setup.py");
+ if (extension_setup.open(QIODevice::ReadOnly)) {
+ QTextStream in(&extension_setup);
+ QString setup_code = in.readAll();
+ if (!PyUtil::runScriptWithPath(
+ setup_code.toStdString(),
+ QString(extension_dir.absolutePath() + "/" + extension).toStdString()
+ )) {
+ qWarning() << "Failed to setup extension" << extension;
+ Core::MessageManager::write(Constants::MESSAGE_MANAGER_PREFIX + tr("Failed to setup extension ") + extension);
+ }
+ }
+ }
+}
+
+void PythonExtensionsPlugin::initializePythonExtensions()
+{
+ // Search python directory in plugin paths
+ QDir extension_dir = extensionDir();
+ if (!extension_dir.exists()) {
+ qWarning() << "Python extension directory not found";
+ Core::MessageManager::write(Constants::MESSAGE_MANAGER_PREFIX + tr("Python extension directory not found"));
+ return;
+ }
+
+ qDebug() << "Found Python extension directory at location" << extension_dir.absolutePath();
+
+ QStringList extension_list = extensionList();
+
+ qDebug() << "Number of Python extensions found:" << extension_list.size();
+
+ // Run the extension initialization code
+ for (const QString &extension : extension_list) {
+ qDebug() << "Trying to initialize extension" << extension;
+
+ QFile extension_main(extension_dir.absolutePath() + "/" + extension + "/main.py");
+ if (extension_main.open(QIODevice::ReadOnly)) {
+ QTextStream in(&extension_main);
+ QString extension_code = in.readAll();
+ if (!PyUtil::runScriptWithPath(
+ extension_code.toStdString(),
+ QString(extension_dir.absolutePath() + "/" + extension).toStdString()
+ )) {
+ qWarning() << "Failed to initialize extension" << extension;
+ Core::MessageManager::write(Constants::MESSAGE_MANAGER_PREFIX + tr("Failed to initialize extension ") + extension);
+ } else {
+ m_loadedExtensions << extension;
+ }
+ } else {
+ qWarning() << "Failed to load main.py for extension" << extension;
+ Core::MessageManager::write(Constants::MESSAGE_MANAGER_PREFIX + tr("Failed to load main.py for extension ") + extension);
+ }
+ }
+
+ qDebug() << "Number of Python extensions loaded:" << m_loadedExtensions.size();
+}
+
+} // namespace Internal
+} // namespace PythonExtensions
diff --git a/plugins/pythonextensions/pythonextensionsplugin.h b/plugins/pythonextensions/pythonextensionsplugin.h
new file mode 100644
index 0000000..a920911
--- /dev/null
+++ b/plugins/pythonextensions/pythonextensionsplugin.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Python Extensions Plugin for QtCreator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "pythonextensions_global.h"
+
+#include <extensionsystem/iplugin.h>
+
+#include <QDir>
+#include <QStringList>
+
+namespace PythonExtensions {
+namespace Internal {
+
+class PythonExtensionsPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "PythonExtensions.json")
+
+public:
+ PythonExtensionsPlugin();
+ ~PythonExtensionsPlugin();
+
+ bool initialize(const QStringList &arguments, QString *errorString) final;
+ void extensionsInitialized() final;
+ bool delayedInitialize() final;
+ ShutdownFlag aboutToShutdown() final;
+
+ QDir extensionDir();
+ QStringList extensionList(const bool loadedOnly = false);
+ void flagAsLoaded(const QString &extension);
+ QString pythonPackagePath();
+private:
+ QStringList m_loadedExtensions;
+ void initializePythonBindings();
+ void initializeOptionalBindings();
+ void installRequirements();
+ void setupPythonExtensions();
+ void initializePythonExtensions();
+};
+
+// Util functions
+// TODO: Are any needed? Until now there were no problems with object ownership
+
+} // namespace Internal
+} // namespace PythonExtensions
+
+
+// Fix for binding build, caused by multiple QtCreator headers
+// having the same name, for some build configurations.
+#include "bindingheaders/core_fileutils.h"
diff --git a/plugins/pythonextensions/pyutil.cpp b/plugins/pythonextensions/pyutil.cpp
new file mode 100644
index 0000000..56f7c03
--- /dev/null
+++ b/plugins/pythonextensions/pyutil.cpp
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Python Extensions Plugin for QtCreator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "pyutil.h"
+
+#include <dlfcn.h> // dlopen
+
+#include <QtCore/QByteArray>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QStringList>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QDir>
+
+// These are used in python and cause compile-time errors
+// if still defined.
+#undef signals
+#undef slots
+
+#include <sbkpython.h>
+#include <sbkconverter.h>
+#include <sbkmodule.h>
+
+// Python has no concept of private variables and there
+// is no way to declare a namespace or scope that will be
+// inaccessible from the user script.
+// To avoid naming collisions with the setup and tear down
+// scripts that attempt to separate different extensions on
+// the level of user code (or at least make them appear separated),
+// this macro mangles the names of variables used.
+// Use as:
+// "... some Python code ..." privatName("my_var_name") "... more code ..."
+#define privateName(name) "qt_creator_" name "_symbol_mchawrioklpilnjajqkfl"
+
+// Setup and utility functions for QtCreator bindings
+// from typesystem.xml
+
+#if PY_MAJOR_VERSION >= 3
+extern "C" PyObject *PyInit_QtCreator();
+#else
+extern "C" void initQtCreator();
+#endif
+
+// These variables store all Python types exported by QtCreators bindings,
+// as well as the types exported for QtWidgets.
+extern PyTypeObject **SbkPythonExtension_QtCreatorTypes;
+
+// This variable stores the Python module generated by Shiboken
+extern PyObject *SbkPythonExtension_QtCreatorModuleObject;
+
+namespace PyUtil {
+
+static State state = PythonUninitialized;
+
+static void cleanup()
+{
+ if (state > PythonUninitialized) {
+ Py_Finalize();
+ state = PythonUninitialized;
+ }
+}
+
+State init()
+{
+ if (state > PythonUninitialized)
+ return state;
+
+ // If there is an active python virtual environment, use that environment's packages location.
+ QByteArray virtualEnvPath = qgetenv("VIRTUAL_ENV");
+ if (!virtualEnvPath.isEmpty())
+ qputenv("PYTHONHOME", virtualEnvPath);
+
+ // Python's shared libraries don't work properly if included from other
+ // shared libraries. See https://mail.python.org/pipermail/new-bugs-announce/2008-November/003322.html
+ #if PY_MAJOR_VERSION >= 3
+ std::string version = "libpython"+std::to_string(PY_MAJOR_VERSION)+"."+std::to_string(PY_MINOR_VERSION)+"m.so";
+ #else
+ std::string version = "libpython"+std::to_string(PY_MAJOR_VERSION)+"."+std::to_string(PY_MINOR_VERSION)+".so";
+ #endif
+ dlopen(version.c_str(), RTLD_LAZY | RTLD_GLOBAL);
+
+ Py_Initialize();
+ qAddPostRoutine(cleanup);
+ state = PythonInitialized;
+
+ #if PY_MAJOR_VERSION >= 3
+ const bool pythonInitialized = PyInit_QtCreator() != nullptr;
+ #else
+ const bool pythonInitialized = true;
+ initQtCreator();
+ #endif
+ const bool pyErrorOccurred = PyErr_Occurred() != nullptr;
+ if (pythonInitialized && !pyErrorOccurred) {
+ state = QtCreatorModuleLoaded;
+ } else {
+ if (pyErrorOccurred)
+ PyErr_Print();
+ qWarning("Failed to initialize the QtCreator module.");
+ }
+
+ // The Python interpreter eats SIGINT, which is not what we want.
+ // This stops it from happening.
+ // See https://mail.python.org/pipermail/cplusplus-sig/2012-December/016858.html
+ if (PyRun_SimpleString(std::string(
+ "import signal as " privateName("signal") "\n"
+ "" privateName("signal") ".signal(" privateName("signal") ".SIGINT, " privateName("signal") ".SIG_DFL)"
+ ).c_str()) == -1) {
+ if (PyErr_Occurred())
+ PyErr_Print();
+ qWarning("Failed to prevent SIGINT capture.");
+ }
+
+ return state;
+}
+
+bool createModule(const std::string &moduleName)
+{
+ if (init() != QtCreatorModuleLoaded)
+ return false;
+
+ PyObject *module = PyImport_AddModule(moduleName.c_str());
+ if (!module) {
+ if (PyErr_Occurred())
+ PyErr_Print();
+ qWarning() << __FUNCTION__ << "Failed to create module";
+ return false;
+ }
+
+ return true;
+}
+
+bool bindObject(const QString &moduleName, const QString &name, int index, void *o)
+{
+ if (init() != QtCreatorModuleLoaded)
+ return false;
+
+ // Generate the type
+ PyTypeObject *typeObject = SbkPythonExtension_QtCreatorTypes[index];
+
+ PyObject *po = Shiboken::Conversions::pointerToPython(reinterpret_cast<SbkObjectType *>(typeObject), o);
+ if (!po) {
+ qWarning() << __FUNCTION__ << "Failed to create wrapper for" << o;
+ return false;
+ }
+ Py_INCREF(po);
+
+ return bindPyObject(moduleName, name, po);
+}
+
+bool bindShibokenModuleObject(const QString &moduleName, const QString &name)
+{
+ return bindPyObject(moduleName, name, SbkPythonExtension_QtCreatorModuleObject);
+}
+
+bool bindPyObject(const QString &moduleName, const QString &name, void *obj)
+{
+ if (init() != QtCreatorModuleLoaded)
+ return false;
+
+ PyObject *module = PyImport_AddModule(moduleName.toLocal8Bit().constData());
+ if (!module) {
+ if (PyErr_Occurred())
+ PyErr_Print();
+ qWarning() << __FUNCTION__ << "Failed to locate module" << moduleName;
+ return false;
+ }
+
+ if (PyModule_AddObject(module, name.toLocal8Bit().constData(), (PyObject *)obj) < 0) {
+ if (PyErr_Occurred())
+ PyErr_Print();
+ qWarning() << __FUNCTION__ << "Failed to add object" << name << "to" << moduleName;
+ return false;
+ }
+
+ return true;
+}
+
+bool bindSubPyObject(const QString &moduleName, const QString &name, void *obj)
+{
+ PyObject *moduleDict = PyModule_GetDict((PyObject *)obj);
+ if (!moduleDict) {
+ if (PyErr_Occurred())
+ PyErr_Print();
+ qWarning("Could not obtain module dict");
+ return false;
+ }
+ PyObject *moduleItem = PyDict_GetItemString(moduleDict, name.toLocal8Bit().constData());
+ if (!moduleDict) {
+ if (PyErr_Occurred())
+ PyErr_Print();
+ qWarning() << "Could not obtain module item" << name;
+ return false;
+ }
+ return bindPyObject(moduleName, name, (void *)moduleItem);
+}
+
+bool runScript(const std::string &script)
+{
+ if (init() == PythonUninitialized)
+ return false;
+
+ if (PyRun_SimpleString(script.c_str()) == -1) {
+ if (PyErr_Occurred())
+ PyErr_Print();
+ return false;
+ }
+
+ return true;
+}
+
+bool runScriptWithPath(const std::string &script, const std::string &path)
+{
+ // I couldn't find a direct c api, but this should cause no variable name
+ // collisions. It also cleans the imported modules after the script finishes
+ const std::string s =
+"import sys as " privateName("sys") "\n"
+"" privateName("path") " = list(" privateName("sys") ".path)\n"
+"" privateName("sys") ".path.insert(0, \"" + path + "\")\n"
+"" privateName("loaded_modules") " = list(" privateName("sys") ".modules.keys())\n"
+"" + script + "\n"
+"" privateName("sys") ".path = " privateName("path") "\n"
+"for m in list(" privateName("sys") ".modules):\n"
+" if m not in " privateName("loaded_modules") ":\n"
+" del(" privateName("sys") ".modules[m])\n";
+ return runScript(s);
+}
+
+bool addToSysPath(const std::string &path)
+{
+ // Add a path to Pythons sys.path
+ // Used for installing dependencies into custom
+ // directory
+ const std::string s =
+"import sys as " privateName("sys") "\n"
+"" privateName("sys") ".path.append(\"" + path + "\")";
+ return runScript(s);
+}
+
+bool pipInstallRequirements(const std::string &requirements, const std::string &target)
+{
+ // Run a requirements.txt file with pip
+ const std::string s =
+"import subprocess, sys\n"
+"subprocess.check_call(\"{} -m pip install -t " + target + " -r " + requirements + "\".format(sys.executable).split())\n"
+"open(\"" + requirements + "\".replace(\"requirements.txt\",\"requirements.txt.installed\"),\"a\").close()\n";
+ return runScriptWithPath(s, "");
+}
+
+} // namespace PyUtil
diff --git a/plugins/pythonextensions/pyutil.h b/plugins/pythonextensions/pyutil.h
new file mode 100644
index 0000000..908427d
--- /dev/null
+++ b/plugins/pythonextensions/pyutil.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Python Extensions Plugin for QtCreator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef PYUTIL_H
+#define PYUTIL_H
+
+#include "pythonextensions_global.h"
+#include <string>
+
+class QObject;
+class QString;
+
+namespace PyUtil {
+
+// Note: If modifying the bindings, check these types still align
+enum QtCreatorTypes
+{
+ PythonExtensionsPluginType = 33, // SBK_PYTHONEXTENSIONS_INTERNAL_PYTHONEXTENSIONSPLUGIN_IDX
+};
+
+enum State
+{
+ PythonUninitialized,
+ PythonInitialized,
+ QtCreatorModuleLoaded,
+};
+
+State init();
+
+PYTHONEXTENSIONSSHARED_EXPORT bool createModule(const std::string &moduleName);
+
+bool bindObject(const QString &moduleName, const QString &name, int index, void *o);
+
+bool bindShibokenModuleObject(const QString &moduleName, const QString &name);
+
+PYTHONEXTENSIONSSHARED_EXPORT bool bindPyObject(const QString &moduleName, const QString &name, void *obj);
+
+PYTHONEXTENSIONSSHARED_EXPORT bool bindSubPyObject(const QString &moduleName, const QString &name, void *obj);
+
+PYTHONEXTENSIONSSHARED_EXPORT bool runScript(const std::string &script);
+
+PYTHONEXTENSIONSSHARED_EXPORT bool runScriptWithPath(const std::string &script, const std::string &path);
+
+PYTHONEXTENSIONSSHARED_EXPORT bool addToSysPath(const std::string &path);
+
+PYTHONEXTENSIONSSHARED_EXPORT bool pipInstallRequirements(const std::string &requirements, const std::string &target);
+
+} // namespace PyUtil
+
+#endif
diff --git a/plugins/pythonextensions/qtcreator.pri b/plugins/pythonextensions/qtcreator.pri
new file mode 100644
index 0000000..cb58e20
--- /dev/null
+++ b/plugins/pythonextensions/qtcreator.pri
@@ -0,0 +1,15 @@
+# QtCreator specific build settings
+# (shared by plugin and optional bindings)
+
+## Either set the IDE_SOURCE_TREE when running qmake,
+## or set the QTC_SOURCE environment variable, to override the default setting
+isEmpty(IDE_SOURCE_TREE): IDE_SOURCE_TREE = $$(QTC_SOURCE)
+
+## Either set the IDE_BUILD_TREE when running qmake,
+## or set the QTC_BUILD environment variable, to override the default setting
+isEmpty(IDE_BUILD_TREE): IDE_BUILD_TREE = $$(QTC_BUILD)
+
+# KEEP this line and DON'T edit it!
+# (if you NEED to change it, have a look at tools/build.py)
+# USE_USER_DESTDIR = yes
+# END KEEP
diff --git a/plugins/pythonextensions/typesystem_qtcreator.xml b/plugins/pythonextensions/typesystem_qtcreator.xml
new file mode 100644
index 0000000..9a1376d
--- /dev/null
+++ b/plugins/pythonextensions/typesystem_qtcreator.xml
@@ -0,0 +1,237 @@
+<?xml version="1.0"?>
+<!--
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Python Extensions Plugin for QtCreator.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+-->
+
+<!-- Typesystem for Qt Creator Python host plugin -->
+<typesystem package="PythonExtension.QtCreator">
+
+ <!-- Load PySide QtWidgets typesystem (is this correct? yup) -->
+ <load-typesystem name="typesystem_widgets.xml" generate="no"/>
+
+ <namespace-type name="PythonExtensions">
+ <namespace-type name="Internal">
+ <object-type name="PythonExtensionsPlugin"/>
+ </namespace-type>
+ </namespace-type>
+
+ <namespace-type name="Core">
+ <object-type name="ActionContainer">
+ <enum-type name="OnAllDisabledBehavior"/>
+ <modify-function signature="addMenu(Core::ActionContainer *, Core::ActionContainer *, Core::Id)"/>
+ <modify-function signature="addMenu(Core::ActionContainer *, Core::Id)"/>
+ <modify-function signature="addSeparator(const Core::Context &amp;, Core::Id, QAction **)" remove="all"/>
+ <modify-function signature="addSeparator(Core::Id)" remove="all"/>
+ </object-type>
+ <object-type name="ActionManager"/>
+ <!-- <object-type name="BaseFileFilter"/> -->
+ <!-- <object-type name="BaseFileWizard"/> -->
+ <!-- <object-type name="BaseFileWizardFactory"/> -->
+ <!-- <object-type name="BaseTextDocument"/> -->
+ <!-- <object-type name="BaseTextFind"/> -->
+ <!-- Causes Seg-Fault (still?) -->
+ <object-type name="Command">
+ <enum-type name="CommandAttribute"/>
+ </object-type>
+ <!-- <object-type name="CommandButton"/> -->
+ <!-- <object-type name="CommandLocator"/> -->
+ <!-- <object-type name="CommandMappings"/> -->
+ <value-type name="Context"/>
+ <!-- <object-type name="DesignMode"/> -->
+ <!-- <object-type name="DiffService"/> -->
+ <object-type name="DocumentManager">
+ <enum-type name="ResolveMode"/>
+ </object-type>
+ <object-type name="DocumentModel"/>
+ <object-type name="EditorManager">
+ <!-- <enum-type name="OpenEditorFlags"/> -->
+ </object-type>
+ <!-- <object-type name="EditorManagerPlaceHolder"/> -->
+ <!-- <object-type name="EditorToolBar"/> -->
+ <!-- <object-type name="ExternalToolManager"/> -->
+ <object-type name="FileUtils"/>
+ <!-- <object-type name="Find"/> -->
+ <!-- <object-type name="FindToolBarPlaceHolder"/> -->
+ <!-- <object-type name="FutureProgress"/> -->
+ <!-- <object-type name="GenerateFile"/> -->
+ <!-- <object-type name="HelpManager"/> -->
+ <!-- <object-type name="Highlight"/> -->
+ <!-- <object-type name="HighlightScrollBarController"/> -->
+ <object-type name="IContext"/>
+ <object-type name="ICore">
+ <enum-type name="ContextPriority"/>
+ <enum-type name="OpenFilesFlags"/>
+ </object-type>
+ <object-type name="IDocument">
+ <enum-type name="OpenResult"/>
+ <enum-type name="ReloadSetting"/>
+ <enum-type name="ChangeTrigger"/>
+ <enum-type name="ChangeType"/>
+ <enum-type name="ReloadBehavior"/>
+ <enum-type name="ReloadFlag"/>
+ </object-type>
+ <!-- <object-type name="IDocumentFactory"/> -->
+ <object-type name="IEditor"/>
+ <!-- <object-type name="IEditorFactory"/> -->
+ <!-- <object-type name="IExternalEditor"/> -->
+ <!-- <object-type name="IFeatureProvider"/> -->
+ <!-- <object-type name="IFindFilter"/> -->
+ <!-- <object-type name="IFindSupport"/> -->
+ <!-- <object-type name="ILocatorFilter"/> -->
+ <!-- <object-type name="IMode"/> -->
+ <!-- <object-type name="INavigationWidgetFactory"/> -->
+ <!-- <object-type name="IOptionsPage"/> -->
+ <!-- <object-type name="IOptionsPageProvider"/> -->
+ <!-- <object-type name="IOutputPane"/> -->
+ <!-- <object-type name="IVersionControl"/> -->
+ <!-- <object-type name="IWelcomePage"/> -->
+ <!-- <object-type name="IWizardFactory"/> -->
+ <value-type name="Id">
+ <modify-function signature="operator&gt;&gt;(QDataStream&amp;, Core::Id&amp;)" remove="all"/>
+ <modify-function signature="operator&lt;&lt;(QDataStream&amp;, Core::Id)" remove="all"/>
+ </value-type>
+ <!-- <object-type name="InfoBar"/> -->
+ <!-- <object-type name="InfoBarDisplay"/> -->
+ <!-- <object-type name="InfoBarEntry"/> -->
+ <!-- <object-type name="ItemViewFind"/> -->
+ <!-- <object-type name="JsExpander"/> -->
+ <!-- <object-type name="LocatorFilterEntry"/> -->
+ <!-- <object-type name="LocatorManager"/> -->
+ <!-- <object-type name="ModeManager"/> -->
+ <object-type name="MessageManager">
+ <enum-type name="PrintToOutputPaneFlag"/>
+ </object-type>
+ <!-- <object-type name="NavigationView"/> -->
+ <!-- <object-type name="NavigationWidget"/> -->
+ <!-- <object-type name="NavigationWidgetPlaceHolder"/> -->
+ <!-- <object-type name="NonResizingSplitter"/> -->
+ <!-- <object-type name="OpenDocumentsTreeView"/> -->
+ <!-- <object-type name="OutputPanePlaceHolder"/> -->
+ <!-- <object-type name="OutputWindow"/> -->
+ <!-- <object-type name="PatchTool"/> -->
+ <!-- <object-type name="ProgressManager"/> -->
+ <!-- <object-type name="ProgressTimer"/> -->
+ <!-- <object-type name="PromptOverwriteDialog"/> -->
+ <!-- <object-type name="ReadOnlyFilesDialog"/> -->
+ <!-- <object-type name="RightPanePlaceHolder"/> -->
+ <!-- <object-type name="RightPaneWidget"/> -->
+ <!-- <object-type name="SearchResult"/> -->
+ <!-- <object-type name="SearchResultItem"/> -->
+ <!-- <object-type name="SearchResultWindow"/> -->
+ <!-- <object-type name="SettingsDatabase"/> -->
+ <!-- <object-type name="ShellCommand"/> -->
+ <!-- <object-type name="SideBar"/> -->
+ <!-- <object-type name="SideBarItem"/> -->
+ <!-- <object-type name="StatusBarManager"/> -->
+ <!-- <object-type name="VariableChoser"/> -->
+ <!-- <object-type name="VcsManager"/> -->
+ <!-- <object-type name="WelcomePageButton"/> -->
+ <!-- <object-type name="WelcomePageFrame"/> -->
+ <!-- <object-type name="WizardDialogParameters"/> -->
+
+ <enum-type name="FindFlag"/>
+ <enum-type name="MakeWritableResult"/>
+ <enum-type name="Side"/>
+
+ <!-- <function signature="highlightAll(const QString &amp;, FindFlags)"/> -->
+ <!-- <function signature="qHash(Id)"/> -->
+
+ <namespace-type name="Constants"/>
+ </namespace-type>
+
+ <namespace-type name="Utils">
+ <object-type name="MacroExpander">
+ <inject-code class="native" position="beginning" file="glue/macroexpander_glue.cpp"/>
+
+ <!-- This requires to specifically disable threads, so that the python state is not empty when executing the expansion -->
+ <!-- However, since Shiboken does not support disabling threads directly, we need to rewrite the call to C++ -->
+ <modify-function signature="expand(const QString) const" allow-thread="no">
+ <inject-code class="target" position="beginning">
+ // Same as generated, no PyEval_SaveThread
+ %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(%1);
+ %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
+ </inject-code>
+ </modify-function>
+ <modify-function signature="expand(const QByteArray) const" allow-thread="no">
+ <inject-code class="target" position="beginning">
+ // Same as generated, no PyEval_SaveThread
+ %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(%1);
+ %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
+ </inject-code>
+ </modify-function>
+ <modify-function signature="expandProcessArgs(const QString) const" allow-thread="no">
+ <inject-code class="target" position="beginning">
+ // Same as generated, no PyEval_SaveThread
+ %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(%1);
+ %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
+ </inject-code>
+ </modify-function>
+ <modify-function signature="value(const QByteArray, bool *found) const" allow-thread="no">
+ <inject-code class="target" position="beginning">
+ // Same as generated, no PyEval_SaveThread
+ %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(%1);
+ %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
+ </inject-code>
+ </modify-function>
+
+ <!-- Not Pythonic, just call .value(b"Macro").decode("utf-8") -->
+ <modify-function signature="resolveMacro(const QString, QString) const" remove="all"/>
+
+ <add-function signature="registerVariable(const QByteArray&amp;,const QString&amp;,PyObject*)">
+ <inject-code>
+ registerPythonVariable(%1, %2, %3);
+ </inject-code>
+ </add-function>
+ <add-function signature="registerPrefix(const QByteArray&amp;,const QString&amp;,PyObject*)">
+ <inject-code>
+ registerPythonPrefixVariable(%1, %2, %3);
+ </inject-code>
+ </add-function>
+ </object-type>
+ <value-type name="FileName"/>
+ <modify-function signature="operator&lt;&lt;(QTextStream&amp;,Utils::FileName)" remove="all"/>
+ <enum-type name="OsType"/>
+ </namespace-type>
+
+ <namespace-type name="ExtensionSystem">
+ <object-type name="IPlugin"/>
+ </namespace-type>
+
+</typesystem>
diff --git a/plugins/pythonextensions/wrappedclasses.h b/plugins/pythonextensions/wrappedclasses.h
new file mode 100644
index 0000000..ebc9910
--- /dev/null
+++ b/plugins/pythonextensions/wrappedclasses.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Python Extensions Plugin for QtCreator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef WRAPPEDCLASSES_H
+#define WRAPPEDCLASSES_H
+
+#include "pythonextensionsplugin.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/editormanager/ieditor.h>
+#include <coreplugin/idocument.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/command.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/id.h>
+#include <coreplugin/documentmanager.h>
+#include <coreplugin/fileutils.h>
+#include <coreplugin/messagemanager.h>
+
+#include <utils/macroexpander.h>
+#include <utils/fileutils.h>
+
+#include <extensionsystem/iplugin.h>
+
+#endif // header end