diff --git a/src/Gui/PropertyPage.cpp b/src/Gui/PropertyPage.cpp index b5e92d6ae0..16c09c7711 100644 --- a/src/Gui/PropertyPage.cpp +++ b/src/Gui/PropertyPage.cpp @@ -119,12 +119,11 @@ void PreferencePage::changeEvent(QEvent *e) PreferenceUiForm::PreferenceUiForm(const QString& fn, QWidget* parent) : PreferencePage(parent), form(nullptr) { - UiLoader loader; - loader.setLanguageChangeEnabled(true); - loader.setWorkingDirectory(QFileInfo(fn).absolutePath()); + auto loader = UiLoader::newInstance(); + loader->setWorkingDirectory(QFileInfo(fn).absolutePath()); QFile file(fn); if (file.open(QFile::ReadOnly)) - form = loader.load(&file, this); + form = loader->load(&file, this); file.close(); if (form) { this->setWindowTitle(form->windowTitle()); diff --git a/src/Gui/TaskView/TaskDialogPython.cpp b/src/Gui/TaskView/TaskDialogPython.cpp index 7d05b12411..795b7cf43a 100644 --- a/src/Gui/TaskView/TaskDialogPython.cpp +++ b/src/Gui/TaskView/TaskDialogPython.cpp @@ -544,8 +544,7 @@ TaskDialogPython::~TaskDialogPython() bool TaskDialogPython::tryLoadUiFile() { if (dlg.hasAttr(std::string("ui"))) { - UiLoader loader; - loader.setLanguageChangeEnabled(true); + auto loader = UiLoader::newInstance(); QString fn, icon; Py::String ui(dlg.getAttr(std::string("ui"))); std::string path = static_cast(ui); @@ -554,7 +553,7 @@ bool TaskDialogPython::tryLoadUiFile() QFile file(fn); QWidget* form = nullptr; if (file.open(QFile::ReadOnly)) - form = loader.load(&file, nullptr); + form = loader->load(&file, nullptr); file.close(); if (form) { appendForm(form, QPixmap(icon)); diff --git a/src/Gui/UiLoader.cpp b/src/Gui/UiLoader.cpp index 002f9affbe..20db81ce55 100644 --- a/src/Gui/UiLoader.cpp +++ b/src/Gui/UiLoader.cpp @@ -24,6 +24,7 @@ #ifndef _PreComp_ # include # include +# include # include # include # include @@ -488,10 +489,20 @@ QString QUiLoader::errorString() const UiLoader::UiLoader(QObject* parent) : QUiLoader(parent) { - // do not use the plugins for additional widgets as we don't need them and - // the application may crash under Linux (tested on Ubuntu 7.04 & 7.10). - clearPluginPaths(); this->cw = availableWidgets(); + setLanguageChangeEnabled(true); +} + +std::unique_ptr UiLoader::newInstance(QObject *parent) +{ + QCoreApplication *app=QCoreApplication::instance(); + QStringList libPaths= app->libraryPaths(); + + app->setLibraryPaths(QStringList{}); //< backup library paths, so QUiLoader won't load plugins by default + std::unique_ptr rv{new UiLoader{parent}}; + app->setLibraryPaths(libPaths); + + return rv; } UiLoader::~UiLoader() @@ -544,8 +555,8 @@ void UiLoaderPy::init_type() } UiLoaderPy::UiLoaderPy() + : loader{UiLoader::newInstance()} { - loader.setLanguageChangeEnabled(true); } UiLoaderPy::~UiLoaderPy() @@ -592,7 +603,7 @@ Py::Object UiLoaderPy::load(const Py::Tuple& args) } if (device) { - QWidget* widget = loader.load(device, parent); + QWidget* widget = loader->load(device, parent); if (widget) { wrap.loadGuiModule(); wrap.loadWidgetsModule(); @@ -613,7 +624,7 @@ Py::Object UiLoaderPy::load(const Py::Tuple& args) Py::Object UiLoaderPy::createWidget(const Py::Tuple& args) { - return wrapFromWidgetFactory(args, std::bind(&UiLoader::createWidget, &loader, + return wrapFromWidgetFactory(args, std::bind(&UiLoader::createWidget, loader.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); @@ -625,7 +636,7 @@ Py::Object UiLoaderPy::addPluginPath(const Py::Tuple& args) if (wrap.loadCoreModule()) { std::string fn; if (wrap.toCString(args[0], fn)) { - loader.addPluginPath(QString::fromStdString(fn)); + loader->addPluginPath(QString::fromStdString(fn)); } } return Py::None(); @@ -633,13 +644,13 @@ Py::Object UiLoaderPy::addPluginPath(const Py::Tuple& args) Py::Object UiLoaderPy::clearPluginPaths(const Py::Tuple& /*args*/) { - loader.clearPluginPaths(); + loader->clearPluginPaths(); return Py::None(); } Py::Object UiLoaderPy::pluginPaths(const Py::Tuple& /*args*/) { - auto list = loader.pluginPaths(); + auto list = loader->pluginPaths(); Py::List py; for (const auto& it : list) { py.append(Py::String(it.toStdString())); @@ -649,7 +660,7 @@ Py::Object UiLoaderPy::pluginPaths(const Py::Tuple& /*args*/) Py::Object UiLoaderPy::availableWidgets(const Py::Tuple& /*args*/) { - auto list = loader.availableWidgets(); + auto list = loader->availableWidgets(); Py::List py; for (const auto& it : list) { py.append(Py::String(it.toStdString())); @@ -665,17 +676,17 @@ Py::Object UiLoaderPy::availableWidgets(const Py::Tuple& /*args*/) Py::Object UiLoaderPy::errorString(const Py::Tuple& /*args*/) { - return Py::String(loader.errorString().toStdString()); + return Py::String(loader->errorString().toStdString()); } Py::Object UiLoaderPy::isLanguageChangeEnabled(const Py::Tuple& /*args*/) { - return Py::Boolean(loader.isLanguageChangeEnabled()); + return Py::Boolean(loader->isLanguageChangeEnabled()); } Py::Object UiLoaderPy::setLanguageChangeEnabled(const Py::Tuple& args) { - loader.setLanguageChangeEnabled(Py::Boolean(args[0])); + loader->setLanguageChangeEnabled(Py::Boolean(args[0])); return Py::None(); } @@ -685,7 +696,7 @@ Py::Object UiLoaderPy::setWorkingDirectory(const Py::Tuple& args) if (wrap.loadCoreModule()) { std::string fn; if (wrap.toCString(args[0], fn)) { - loader.setWorkingDirectory(QString::fromStdString(fn)); + loader->setWorkingDirectory(QString::fromStdString(fn)); } } return Py::None(); @@ -693,7 +704,7 @@ Py::Object UiLoaderPy::setWorkingDirectory(const Py::Tuple& args) Py::Object UiLoaderPy::workingDirectory(const Py::Tuple& /*args*/) { - QDir dir = loader.workingDirectory(); + QDir dir = loader->workingDirectory(); QString path = dir.absolutePath(); return Py::String(path.toStdString()); } diff --git a/src/Gui/UiLoader.h b/src/Gui/UiLoader.h index 112138bd57..94a7ab16dc 100644 --- a/src/Gui/UiLoader.h +++ b/src/Gui/UiLoader.h @@ -106,8 +106,29 @@ private: */ class UiLoader : public QUiLoader { -public: +protected: + /** + * A protected construct for UiLoader. + * To create an instance of UiLoader @see UiLoader::newInstance() + */ explicit UiLoader(QObject* parent=nullptr); + +public: + /** + * Creates a new instance of a UiLoader. + * + * Due to its flaw the QUiLoader upon creation loads every available Qt + * designer plugin it can find in QApplication::libraryPaths(). Some of + * those plugins may perform some unexpected actions upon load which may + * interfere with FreeCAD's functionality. Only way to avoid such behaviour + * is to reset QApplication::libraryPaths, create a QUiLoader and then + * restore the libs paths. Hence need for this function to wrap + * construction. + * + * @see https://github.com/FreeCAD/FreeCAD/issues/8708 + */ + static std::unique_ptr newInstance(QObject *parent=0); + ~UiLoader() override; /** @@ -149,7 +170,7 @@ private: static PyObject *PyMake(struct _typeobject *, PyObject *, PyObject *); private: - UiLoader loader; + std::unique_ptr loader; }; } // namespace Gui diff --git a/src/Gui/WidgetFactory.cpp b/src/Gui/WidgetFactory.cpp index 298ab3b02f..6c2503b904 100644 --- a/src/Gui/WidgetFactory.cpp +++ b/src/Gui/WidgetFactory.cpp @@ -461,11 +461,10 @@ void PyResource::load(const char* name) QWidget* w=nullptr; try { - UiLoader loader; - loader.setLanguageChangeEnabled(true); + auto loader = UiLoader::newInstance(); QFile file(fn); if (file.open(QFile::ReadOnly)) - w = loader.load(&file, QApplication::activeWindow()); + w = loader->load(&file, QApplication::activeWindow()); file.close(); } catch (...) {