Gui: implement a workaround for MSYS2 to access QUiLoader via its Python interface
This commit is contained in:
@@ -3,16 +3,18 @@ EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c
|
||||
OUTPUT_VARIABLE python_libs OUTPUT_STRIP_TRAILING_WHITESPACE )
|
||||
SET(PYTHON_MAIN_DIR ${python_libs})
|
||||
|
||||
set(NAMESPACE_INIT "${CMAKE_BINARY_DIR}/Ext/freecad/__init__.py")
|
||||
set(NAMESPACE_DIR "${CMAKE_BINARY_DIR}/Ext/freecad")
|
||||
set(NAMESPACE_INIT "${NAMESPACE_DIR}/__init__.py")
|
||||
if (WIN32)
|
||||
get_filename_component(FREECAD_LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}"
|
||||
get_filename_component(FREECAD_LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}"
|
||||
REALPATH BASE_DIR "${CMAKE_INSTALL_PREFIX}")
|
||||
set( ${CMAKE_INSTALL_BINDIR})
|
||||
set( ${CMAKE_INSTALL_BINDIR})
|
||||
else()
|
||||
set(FREECAD_LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
|
||||
set(FREECAD_LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
|
||||
configure_file(__init__.py.template ${NAMESPACE_INIT})
|
||||
configure_file(UiTools.py ${NAMESPACE_DIR}/UiTools.py)
|
||||
|
||||
if (INSTALL_TO_SITEPACKAGES)
|
||||
SET(SITE_PACKAGE_DIR ${PYTHON_MAIN_DIR}/freecad)
|
||||
@@ -23,6 +25,7 @@ endif()
|
||||
INSTALL(
|
||||
FILES
|
||||
${NAMESPACE_INIT}
|
||||
UiTools.py
|
||||
DESTINATION
|
||||
${SITE_PACKAGE_DIR}
|
||||
)
|
||||
|
||||
21
src/Ext/freecad/UiTools.py
Normal file
21
src/Ext/freecad/UiTools.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# (c) 2021 Werner Mayer LGPL
|
||||
|
||||
from PySide2 import QtUiTools
|
||||
from PySide2 import QtCore
|
||||
import FreeCADGui as Gui
|
||||
|
||||
|
||||
class QUiLoader(QtUiTools.QUiLoader):
|
||||
"""
|
||||
This is an extension of Qt's QUiLoader to also create custom widgets
|
||||
"""
|
||||
def __init__(self, arg = None):
|
||||
super(QUiLoader, self).__init__(arg)
|
||||
self.ui = Gui.PySideUic
|
||||
|
||||
def createWidget(self, className, parent = None, name = ""):
|
||||
widget = self.ui.createCustomWidget(className, parent, name)
|
||||
if not widget:
|
||||
widget = super(QUiLoader, self).createWidget(className, parent, name)
|
||||
return widget
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <QAction>
|
||||
# include <QActionGroup>
|
||||
# include <QDir>
|
||||
# include <QLayout>
|
||||
# include <QFile>
|
||||
# include <QTextStream>
|
||||
#endif
|
||||
@@ -31,9 +35,62 @@
|
||||
#include "PythonWrapper.h"
|
||||
#include "WidgetFactory.h"
|
||||
#include <Base/Interpreter.h>
|
||||
#include <functional>
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
namespace {
|
||||
|
||||
QWidget* createFromWidgetFactory(const QString & className, QWidget * parent, const QString& name)
|
||||
{
|
||||
QWidget* widget = nullptr;
|
||||
if (WidgetFactory().CanProduce((const char*)className.toLatin1()))
|
||||
widget = WidgetFactory().createWidget((const char*)className.toLatin1(), parent);
|
||||
if (widget)
|
||||
widget->setObjectName(name);
|
||||
return widget;
|
||||
}
|
||||
|
||||
Py::Object wrapFromWidgetFactory(const Py::Tuple& args, const std::function<QWidget*(const QString&, QWidget *, const QString&)> & callableFunc)
|
||||
{
|
||||
Gui::PythonWrapper wrap;
|
||||
|
||||
// 1st argument
|
||||
Py::String str(args[0]);
|
||||
std::string className;
|
||||
className = str.as_std_string("utf-8");
|
||||
// 2nd argument
|
||||
QWidget* parent = 0;
|
||||
if (wrap.loadCoreModule() && args.size() > 1) {
|
||||
QObject* object = wrap.toQObject(args[1]);
|
||||
if (object)
|
||||
parent = qobject_cast<QWidget*>(object);
|
||||
}
|
||||
|
||||
// 3rd argument
|
||||
std::string objectName;
|
||||
if (args.size() > 2) {
|
||||
Py::String str(args[2]);
|
||||
objectName = str.as_std_string("utf-8");
|
||||
}
|
||||
|
||||
QWidget* widget = callableFunc(QString::fromLatin1(className.c_str()), parent,
|
||||
QString::fromLatin1(objectName.c_str()));
|
||||
if (!widget) {
|
||||
return Py::None();
|
||||
// std::string err = "No such widget class '";
|
||||
// err += className;
|
||||
// err += "'";
|
||||
// throw Py::RuntimeError(err);
|
||||
}
|
||||
wrap.loadGuiModule();
|
||||
wrap.loadWidgetsModule();
|
||||
|
||||
const char* typeName = wrap.getWrapperName(widget);
|
||||
return wrap.fromQWidget(widget, typeName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PySideUicModule::PySideUicModule()
|
||||
: Py::ExtensionModule<PySideUicModule>("PySideUic")
|
||||
@@ -43,6 +100,8 @@ PySideUicModule::PySideUicModule()
|
||||
"and then execute it in a special frame to retrieve the form_class.");
|
||||
add_varargs_method("loadUi",&PySideUicModule::loadUi,
|
||||
"Addition of \"loadUi\" to PySide.");
|
||||
add_varargs_method("createCustomWidget",&PySideUicModule::createCustomWidget,
|
||||
"Create custom widgets.");
|
||||
initialize("PySideUic helper module"); // register with Python
|
||||
}
|
||||
|
||||
@@ -160,6 +219,295 @@ Py::Object PySideUicModule::loadUi(const Py::Tuple& args)
|
||||
return Py::None();
|
||||
}
|
||||
|
||||
Py::Object PySideUicModule::createCustomWidget(const Py::Tuple& args)
|
||||
{
|
||||
return wrapFromWidgetFactory(args, &createFromWidgetFactory);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
#if !defined (HAVE_QT_UI_TOOLS)
|
||||
namespace Gui {
|
||||
QUiLoader::QUiLoader(QObject* parent)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
PythonWrapper wrap;
|
||||
wrap.loadUiToolsModule();
|
||||
//PyObject* module = PyImport_ImportModule("PySide2.QtUiTools");
|
||||
PyObject* module = PyImport_ImportModule("freecad.UiTools");
|
||||
if (module) {
|
||||
Py::Tuple args(1);
|
||||
args[0] = wrap.fromQObject(parent);
|
||||
Py::Module mod(module, true);
|
||||
uiloader = mod.callMemberFunction("QUiLoader", args);
|
||||
}
|
||||
}
|
||||
|
||||
QUiLoader::~QUiLoader()
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
uiloader = Py::None();
|
||||
}
|
||||
|
||||
QStringList QUiLoader::pluginPaths() const
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::List list(uiloader.callMemberFunction("pluginPaths"));
|
||||
QStringList paths;
|
||||
for (const auto& it : list) {
|
||||
paths << QString::fromStdString(Py::String(it).as_std_string());
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return QStringList();
|
||||
}
|
||||
}
|
||||
|
||||
void QUiLoader::clearPluginPaths()
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
uiloader.callMemberFunction("clearPluginPaths");
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void QUiLoader::addPluginPath(const QString& path)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args[0] = Py::String(path.toStdString());
|
||||
uiloader.callMemberFunction("addPluginPath", args);
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* QUiLoader::load(QIODevice* device, QWidget* parentWidget)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
PythonWrapper wrap;
|
||||
Py::Tuple args(2);
|
||||
args[0] = wrap.fromQObject(device);
|
||||
args[1] = wrap.fromQObject(parentWidget);
|
||||
Py::Object form(uiloader.callMemberFunction("load", args));
|
||||
return qobject_cast<QWidget*>(wrap.toQObject(form));
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList QUiLoader::availableWidgets() const
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::List list(uiloader.callMemberFunction("availableWidgets"));
|
||||
QStringList widgets;
|
||||
for (const auto& it : list) {
|
||||
widgets << QString::fromStdString(Py::String(it).as_std_string());
|
||||
}
|
||||
return widgets;
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return QStringList();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList QUiLoader::availableLayouts() const
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::List list(uiloader.callMemberFunction("availableLayouts"));
|
||||
QStringList layouts;
|
||||
for (const auto& it : list) {
|
||||
layouts << QString::fromStdString(Py::String(it).as_std_string());
|
||||
}
|
||||
return layouts;
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return QStringList();
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* QUiLoader::createWidget(const QString& className, QWidget* parent, const QString& name)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
PythonWrapper wrap;
|
||||
Py::Tuple args(3);
|
||||
args[0] = Py::String(className.toStdString());
|
||||
args[1] = wrap.fromQObject(parent);
|
||||
args[2] = Py::String(name.toStdString());
|
||||
Py::Object form(uiloader.callMemberFunction("createWidget", args));
|
||||
return qobject_cast<QWidget*>(wrap.toQObject(form));
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QLayout* QUiLoader::createLayout(const QString& className, QObject* parent, const QString& name)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
PythonWrapper wrap;
|
||||
Py::Tuple args(3);
|
||||
args[0] = Py::String(className.toStdString());
|
||||
args[1] = wrap.fromQObject(parent);
|
||||
args[2] = Py::String(name.toStdString());
|
||||
Py::Object form(uiloader.callMemberFunction("createLayout", args));
|
||||
return qobject_cast<QLayout*>(wrap.toQObject(form));
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QActionGroup* QUiLoader::createActionGroup(QObject* parent, const QString& name)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
PythonWrapper wrap;
|
||||
Py::Tuple args(2);
|
||||
args[0] = wrap.fromQObject(parent);
|
||||
args[1] = Py::String(name.toStdString());
|
||||
Py::Object action(uiloader.callMemberFunction("createActionGroup", args));
|
||||
return qobject_cast<QActionGroup*>(wrap.toQObject(action));
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QAction* QUiLoader::createAction(QObject* parent, const QString& name)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
PythonWrapper wrap;
|
||||
Py::Tuple args(2);
|
||||
args[0] = wrap.fromQObject(parent);
|
||||
args[1] = Py::String(name.toStdString());
|
||||
Py::Object action(uiloader.callMemberFunction("createAction", args));
|
||||
return qobject_cast<QAction*>(wrap.toQObject(action));
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void QUiLoader::setWorkingDirectory(const QDir& dir)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
PythonWrapper wrap;
|
||||
Py::Tuple args(1);
|
||||
args[0] = wrap.fromQDir(dir);
|
||||
uiloader.callMemberFunction("setWorkingDirectory", args);
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
||||
QDir QUiLoader::workingDirectory() const
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
PythonWrapper wrap;
|
||||
Py::Object dir((uiloader.callMemberFunction("workingDirectory")));
|
||||
QDir* d = wrap.toQDir(dir.ptr());
|
||||
if (d) return *d;
|
||||
return QDir::current();
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return QDir::current();
|
||||
}
|
||||
}
|
||||
|
||||
void QUiLoader::setLanguageChangeEnabled(bool enabled)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args[0] = Py::Boolean(enabled);
|
||||
uiloader.callMemberFunction("setLanguageChangeEnabled", args);
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool QUiLoader::isLanguageChangeEnabled() const
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Boolean ok((uiloader.callMemberFunction("isLanguageChangeEnabled")));
|
||||
return static_cast<bool>(ok);
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void QUiLoader::setTranslationEnabled(bool enabled)
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args[0] = Py::Boolean(enabled);
|
||||
uiloader.callMemberFunction("setTranslationEnabled", args);
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool QUiLoader::isTranslationEnabled() const
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Boolean ok((uiloader.callMemberFunction("isTranslationEnabled")));
|
||||
return static_cast<bool>(ok);
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QString QUiLoader::errorString() const
|
||||
{
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::String error((uiloader.callMemberFunction("errorString")));
|
||||
return QString::fromStdString(error.as_std_string());
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
e.clear();
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
UiLoader::UiLoader(QObject* parent)
|
||||
@@ -180,11 +528,8 @@ QWidget* UiLoader::createWidget(const QString & className, QWidget * parent,
|
||||
{
|
||||
if (this->cw.contains(className))
|
||||
return QUiLoader::createWidget(className, parent, name);
|
||||
QWidget* w = 0;
|
||||
if (WidgetFactory().CanProduce((const char*)className.toLatin1()))
|
||||
w = WidgetFactory().createWidget((const char*)className.toLatin1(), parent);
|
||||
if (w) w->setObjectName(name);
|
||||
return w;
|
||||
|
||||
return createFromWidgetFactory(className, parent, name);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
@@ -279,38 +624,10 @@ Py::Object UiLoaderPy::load(const Py::Tuple& args)
|
||||
|
||||
Py::Object UiLoaderPy::createWidget(const Py::Tuple& args)
|
||||
{
|
||||
Gui::PythonWrapper wrap;
|
||||
|
||||
// 1st argument
|
||||
Py::String str(args[0]);
|
||||
std::string className;
|
||||
className = str.as_std_string("utf-8");
|
||||
// 2nd argument
|
||||
QWidget* parent = 0;
|
||||
if (wrap.loadCoreModule() && args.size() > 1) {
|
||||
QObject* object = wrap.toQObject(args[1]);
|
||||
if (object)
|
||||
parent = qobject_cast<QWidget*>(object);
|
||||
}
|
||||
|
||||
// 3rd argument
|
||||
std::string objectName;
|
||||
if (args.size() > 2) {
|
||||
Py::String str(args[2]);
|
||||
objectName = str.as_std_string("utf-8");
|
||||
}
|
||||
|
||||
QWidget* widget = loader.createWidget(QString::fromLatin1(className.c_str()), parent,
|
||||
QString::fromLatin1(objectName.c_str()));
|
||||
if (!widget) {
|
||||
std::string err = "No such widget class '";
|
||||
err += className;
|
||||
err += "'";
|
||||
throw Py::RuntimeError(err);
|
||||
}
|
||||
wrap.loadGuiModule();
|
||||
wrap.loadWidgetsModule();
|
||||
|
||||
const char* typeName = wrap.getWrapperName(widget);
|
||||
return wrap.fromQWidget(widget, typeName);
|
||||
return wrapFromWidgetFactory(args, std::bind(&UiLoader::createWidget, &loader,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3));
|
||||
}
|
||||
|
||||
#include "moc_UiLoader.cpp"
|
||||
|
||||
@@ -24,9 +24,26 @@
|
||||
#ifndef GUI_UILOADER_H
|
||||
#define GUI_UILOADER_H
|
||||
|
||||
#if !defined (__MINGW32__)
|
||||
#define HAVE_QT_UI_TOOLS
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_QT_UI_TOOLS)
|
||||
#include <QUiLoader>
|
||||
#else
|
||||
#include <QObject>
|
||||
#endif
|
||||
#include <CXX/Extensions.hxx>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLayout;
|
||||
class QAction;
|
||||
class QActionGroup;
|
||||
class QDir;
|
||||
class QIODevice;
|
||||
class QWidget;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
namespace Gui {
|
||||
|
||||
@@ -40,8 +57,46 @@ public:
|
||||
private:
|
||||
Py::Object loadUiType(const Py::Tuple& args);
|
||||
Py::Object loadUi(const Py::Tuple& args);
|
||||
Py::Object createCustomWidget(const Py::Tuple&);
|
||||
};
|
||||
|
||||
#if !defined (HAVE_QT_UI_TOOLS)
|
||||
class QUiLoader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QUiLoader(QObject* parent = nullptr);
|
||||
~QUiLoader();
|
||||
|
||||
QStringList pluginPaths() const;
|
||||
void clearPluginPaths();
|
||||
void addPluginPath(const QString& path);
|
||||
|
||||
QWidget* load(QIODevice* device, QWidget* parentWidget = nullptr);
|
||||
QStringList availableWidgets() const;
|
||||
QStringList availableLayouts() const;
|
||||
|
||||
virtual QWidget* createWidget(const QString& className, QWidget* parent = nullptr, const QString& name = QString());
|
||||
virtual QLayout* createLayout(const QString& className, QObject* parent = nullptr, const QString& name = QString());
|
||||
virtual QActionGroup* createActionGroup(QObject* parent = nullptr, const QString& name = QString());
|
||||
virtual QAction* createAction(QObject* parent = nullptr, const QString& name = QString());
|
||||
|
||||
void setWorkingDirectory(const QDir& dir);
|
||||
QDir workingDirectory() const;
|
||||
|
||||
void setLanguageChangeEnabled(bool enabled);
|
||||
bool isLanguageChangeEnabled() const;
|
||||
|
||||
void setTranslationEnabled(bool enabled);
|
||||
bool isTranslationEnabled() const;
|
||||
|
||||
QString errorString() const;
|
||||
|
||||
private:
|
||||
Py::Object uiloader;
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The UiLoader class provides the abitlity to use the widget factory
|
||||
* framework of FreeCAD within the framework provided by Qt. This class
|
||||
@@ -60,6 +115,7 @@ public:
|
||||
*/
|
||||
QWidget* createWidget(const QString & className, QWidget * parent=0,
|
||||
const QString& name = QString());
|
||||
|
||||
private:
|
||||
QStringList cw;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user