From afcad256065b9ce4f4da9ef1a7362fcebbce251b Mon Sep 17 00:00:00 2001 From: Max Smolens Date: Fri, 4 Sep 2015 15:36:00 -0400 Subject: [PATCH 01/14] Update README.md to describe new commits --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f9fbde69..94b7b367 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Prerequisites Build instructions ------------------ -By default, the `patched-3` version will be checked out. +By default, the `patched-5` version will be checked out. ``` git clone git://github.com/commontk/PythonQt.git @@ -64,7 +64,9 @@ florianlink (8): ``` ### patched-5 -* Based on patched-4 + [r395](http://sourceforge.net/p/pythonqt/code/395/) +* Based on patched-4 + [r403](http://sourceforge.net/p/pythonqt/code/403/) excluding commit [r397](http://sourceforge.net/p/pythonqt/code/397/) +* List of bug fixes: + * Fix for memory leaks and cleanup crash * List of features: * CMake: * Fix install rules From e79d911bcc397271c8604d3733efa75898bba09a Mon Sep 17 00:00:00 2001 From: Max Smolens Date: Fri, 11 Sep 2015 09:17:59 -0400 Subject: [PATCH 02/14] Prevent crashes during and after cleanup This commit prevents crashes by handling scenarios such as: (a) object destruction after the Python interpreter has been finalized (b) object destruction after cleanup, i.e. the singleton no longer exists Any usage of a Qt enum demonstrates (a). One example that demonstrates (b) is a QTimer object which is created with a QApplication parent. PythonQt::cleanup() is called before the QApplication is completely destroyed, so the code that handles wrapping the QTimer object must handle the case when the PythonQt singleton no longer exists. Co-authored-by: Jean-Christophe Fillion-Robin --- src/PythonQt.h | 2 +- src/PythonQtObjectPtr.cpp | 2 +- src/PythonQtSignalReceiver.cpp | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/PythonQt.h b/src/PythonQt.h index 01ef8206..27130b54 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -526,7 +526,7 @@ class PYTHONQT_EXPORT PythonQt : public QObject { //@{ //! get access to internal data (should not be used on the public API, but is used by some C functions) - static PythonQtPrivate* priv() { return _self->_p; } + static PythonQtPrivate* priv() { return _self ? _self->_p : NULL; } //! clear all NotFound entries on all class infos, to ensure that //! newly loaded wrappers can add methods even when the object was wrapped by PythonQt before the wrapper was loaded diff --git a/src/PythonQtObjectPtr.cpp b/src/PythonQtObjectPtr.cpp index 95fb4e0f..2d79a5bb 100644 --- a/src/PythonQtObjectPtr.cpp +++ b/src/PythonQtObjectPtr.cpp @@ -49,7 +49,7 @@ PythonQtObjectPtr::PythonQtObjectPtr(PyObject* o) PythonQtObjectPtr::~PythonQtObjectPtr() { - if (_object) Py_DECREF(_object); + if (_object && Py_IsInitialized()) Py_DECREF(_object); } void PythonQtObjectPtr::setNewRef(PyObject* o) diff --git a/src/PythonQtSignalReceiver.cpp b/src/PythonQtSignalReceiver.cpp index d81ab176..e3ac27f2 100644 --- a/src/PythonQtSignalReceiver.cpp +++ b/src/PythonQtSignalReceiver.cpp @@ -172,7 +172,9 @@ PythonQtSignalReceiver::PythonQtSignalReceiver(QObject* obj):PythonQtSignalRecei PythonQtSignalReceiver::~PythonQtSignalReceiver() { - PythonQt::priv()->removeSignalEmitter(_obj); + if (PythonQt::priv()) { + PythonQt::priv()->removeSignalEmitter(_obj); + } } From c2d17cee8fc77d1bda483a138005161e3065a9f1 Mon Sep 17 00:00:00 2001 From: Max Smolens Date: Fri, 11 Sep 2015 10:06:45 -0400 Subject: [PATCH 03/14] Fix tests link error on Windows This commit fixes a link error building PythonQtCppTests on Windows: 1>PythonQtCppTests.obj : error LNK2001: unresolved external symbol "int __cdecl tests_PythonQtTestMain(int,char * * const)" (?tests_PythonQtTestMain@@YAHHQEAPEAD@Z) 1>C:\temp\PythonQt-build\Debug\PythonQtCppTests.exe : fatal error LNK1120: 1 unresolved externals --- tests/PythonQtTestMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PythonQtTestMain.cpp b/tests/PythonQtTestMain.cpp index 44f5bea6..303ac0d0 100644 --- a/tests/PythonQtTestMain.cpp +++ b/tests/PythonQtTestMain.cpp @@ -44,7 +44,7 @@ #include -int main( int argc, char **argv ) +int main(int argc, char *argv[]) { QApplication qapp(argc, argv); From c302f84279ad6ffb092af8d200d27345060be1ac Mon Sep 17 00:00:00 2001 From: Max Smolens Date: Fri, 11 Sep 2015 16:23:24 -0400 Subject: [PATCH 04/14] Fix refcount problems seen when re-initializing Python after finalizing A sequence of calls like the following would crash in Python when reinitializing the interpreter the second time: PythonQt::init(0); ... Py_Finalize(); PythonQt::init(0); ... Py_Finalize(); --- src/PythonQt.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 9cbcfba3..04e2fa81 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -657,6 +657,7 @@ PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtCla PyObject* className = PyString_FromString(pythonClassName.constData()); PyObject* baseClasses = PyTuple_New(1); + Py_INCREF(&PythonQtInstanceWrapper_Type); PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PythonQtInstanceWrapper_Type); PyObject* typeDict = PyDict_New(); @@ -1611,6 +1612,7 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ #endif _p->_pythonQtModuleName = name; + Py_INCREF(&PythonQtBoolResult_Type); PyModule_AddObject(_p->pythonQtModule().object(), "BoolResult", (PyObject*)&PythonQtBoolResult_Type); PythonQtObjectPtr sys; sys.setNewRef(PyImport_ImportModule("sys")); @@ -1634,7 +1636,9 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ Py_ssize_t old_size = PyTuple_Size(old_module_names); PyObject *module_names = PyTuple_New(old_size + 1); for (Py_ssize_t i = 0; i < old_size; i++) { - PyTuple_SetItem(module_names, i, PyTuple_GetItem(old_module_names, i)); + PyObject *item = PyTuple_GetItem(old_module_names, i); + Py_INCREF(item); + PyTuple_SetItem(module_names, i, item); } PyTuple_SetItem(module_names, old_size, PyString_FromString(name.constData())); PyModule_AddObject(sys.object(), "builtin_module_names", module_names); From c6884e54aa8d2e72eacdaee74820fc12bc7b9dc2 Mon Sep 17 00:00:00 2001 From: Max Smolens Date: Fri, 11 Sep 2015 16:31:48 -0400 Subject: [PATCH 05/14] Explicitly initialize global storage containers This commit changes initialization of the global storage containers to be explicit instead of happening during static initialization. This makes the containers properly initialized if PythonQt is initialized and cleaned up more than once. The motivation for this change is to support testing cleanup and finalization. --- src/PythonQt.cpp | 4 ++++ src/PythonQtMisc.h | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 04e2fa81..d4443528 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -1294,6 +1294,10 @@ PythonQtPrivate::PythonQtPrivate() _hadError = false; _systemExitExceptionHandlerEnabled = false; _debugAPI = new PythonQtDebugAPI(this); + + PythonQtConv::global_valueStorage.init(); + PythonQtConv::global_ptrStorage.init(); + PythonQtConv::global_variantStorage.init(); } void PythonQtPrivate::setupSharedLibrarySuffixes() diff --git a/src/PythonQtMisc.h b/src/PythonQtMisc.h index 2c75c456..65b58878 100644 --- a/src/PythonQtMisc.h +++ b/src/PythonQtMisc.h @@ -76,12 +76,25 @@ template class PythonQtValueStorage PythonQtValueStorage() { _chunkIdx = 0; _chunkOffset = 0; + _currentChunk = NULL; + }; + + //! initialize memory + void init() { + assert(_currentChunk == NULL); + assert(_chunks.isEmpty()); + _chunkIdx = 0; + _chunkOffset = 0; _currentChunk = new T[chunkEntries]; _chunks.append(_currentChunk); - }; + } //! clear all memory void clear() { + _chunkIdx = 0; + _chunkOffset = 0; + _currentChunk = NULL; + T* chunk; Q_FOREACH(chunk, _chunks) { delete[]chunk; From a41e7954f758260380932e39765c1a084e526646 Mon Sep 17 00:00:00 2001 From: Max Smolens Date: Fri, 11 Sep 2015 16:36:51 -0400 Subject: [PATCH 06/14] Add cleanup/finalization tests Add tests that check for clean cleanup and finalization in different scenarios. Co-authored-by: Jean-Christophe Fillion-Robin Co-authored-by: Pat Marion --- CMakeLists.txt | 12 ++++++ tests/PythonQtTestCleanup.cpp | 69 +++++++++++++++++++++++++++++++++++ tests/PythonQtTestCleanup.h | 45 +++++++++++++++++++++++ tests/PythonQtTestMain.cpp | 8 ++++ 4 files changed, 134 insertions(+) create mode 100644 tests/PythonQtTestCleanup.cpp create mode 100644 tests/PythonQtTestCleanup.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 521129d4..2349303a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -279,6 +279,18 @@ if(BUILD_TESTING) tests/PythonQtTests.h ) + if(PythonQt_Wrap_Qtcore) + include_directories(generated_cpp${generated_cpp_suffix}) + + list(APPEND test_sources + tests/PythonQtTestCleanup.cpp + tests/PythonQtTestCleanup.h + ) + QT4_WRAP_CPP(test_sources + tests/PythonQtTestCleanup.h + ) + endif() + set_property(SOURCE tests/PythonQtTestMain.cpp PROPERTY COMPILE_DEFINITIONS "main=tests_PythonQtTestMain") add_executable(PythonQtCppTests ${test_sources}) diff --git a/tests/PythonQtTestCleanup.cpp b/tests/PythonQtTestCleanup.cpp new file mode 100644 index 00000000..796c0eba --- /dev/null +++ b/tests/PythonQtTestCleanup.cpp @@ -0,0 +1,69 @@ +#include "PythonQtTestCleanup.h" +#include "PythonQt.h" +#include "PythonQt_QtBindings.h" + +void PythonQtTestCleanup::initTestCase() +{ +} + +void PythonQtTestCleanup::cleanupTestCase() +{ +} + +void PythonQtTestCleanup::init() +{ + // Initialize before each test + + PythonQt::init(PythonQt::IgnoreSiteModule); + PythonQt_init_QtBindings(); + + _helper = new PythonQtTestCleanupHelper(); + PythonQtObjectPtr main = PythonQt::self()->getMainModule(); + PythonQt::self()->addObject(main, "obj", _helper); +} + +void PythonQtTestCleanup::cleanup() +{ + // Finalize and cleanup after each test + + PythonQtObjectPtr main = PythonQt::self()->getMainModule(); + PythonQt::self()->removeVariable(main, "obj"); + delete _helper; + _helper = NULL; + + if (Py_IsInitialized()) { + Py_Finalize(); + } + + PythonQt::cleanup(); +} + +void PythonQtTestCleanup::testQtEnum() +{ + QVERIFY(_helper->runScript( + "import PythonQt.QtCore\n" \ + "x = PythonQt.QtCore.QFile.ReadOnly\n" \ + "obj.setPassed()" + )); +} + +void PythonQtTestCleanup::testCallQtMethodInDel() +{ + QVERIFY(_helper->runScript( + "import PythonQt.QtCore\n" \ + "class TimerWrapper(object):\n" \ + " def __init__(self):\n" \ + " self.timer = PythonQt.QtCore.QTimer()\n" \ + " def __del__(self):\n" \ + " self.timer.setSingleShot(True)\n" \ + "x = TimerWrapper()\n" \ + "obj.setPassed()\n" + )); +} + +bool PythonQtTestCleanupHelper::runScript(const char* script) +{ + _passed = false; + PyRun_SimpleString(script); + return _passed; +} diff --git a/tests/PythonQtTestCleanup.h b/tests/PythonQtTestCleanup.h new file mode 100644 index 00000000..2c4879e2 --- /dev/null +++ b/tests/PythonQtTestCleanup.h @@ -0,0 +1,45 @@ +#ifndef _PYTHONQTTESTCLEANUP_H +#define _PYTHONQTTESTCLEANUP_H + +#include "PythonQt.h" +#include + +class PythonQtTestCleanupHelper; + +//! Test PythonQt cleanup and Python interpreter finalization +class PythonQtTestCleanup : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void testQtEnum(); + void testCallQtMethodInDel(); + +private: + PythonQtTestCleanupHelper* _helper; +}; + +//! Test helper class +class PythonQtTestCleanupHelper : public QObject +{ + Q_OBJECT +public: + PythonQtTestCleanupHelper() : + _passed(false) { + }; + + bool runScript(const char* script); + +public Q_SLOTS: + void setPassed() { _passed = true; } + +private: + bool _passed; +}; + +#endif diff --git a/tests/PythonQtTestMain.cpp b/tests/PythonQtTestMain.cpp index 303ac0d0..ac3a37d8 100644 --- a/tests/PythonQtTestMain.cpp +++ b/tests/PythonQtTestMain.cpp @@ -41,6 +41,7 @@ #include "PythonQt.h" #include "PythonQtTests.h" +#include "PythonQtTestCleanup.h" #include @@ -60,6 +61,13 @@ int main(int argc, char *argv[]) PythonQt::cleanup(); + if (Py_IsInitialized()) { + Py_Finalize(); + } + + PythonQtTestCleanup cleanup; + failCount += QTest::qExec(&cleanup, argc, argv); + if (failCount>0) { std::cerr << "Tests failed: " << failCount << std::endl; } else { From 5a29ac6d1ab981b98fcb69764cef7037017ecf64 Mon Sep 17 00:00:00 2001 From: Max Smolens Date: Tue, 15 Sep 2015 15:45:30 -0400 Subject: [PATCH 07/14] Fix PythonQtSignalReceiver crash during cleanup This commit fixes a crash during PythonQt::cleanup(). While destroying the PythonQtPrivate instance, any remaining PythonQtSignalReceivers that are kept alive only by their parent object will be destroyed. The crash occurs when PythonQtSignalReceiver's destructor calls back into PythonQtPrivate::removeSignalEmitter() when the PythonQtPrivate instance is mostly destroyed. Includes test case that crashes without the fix. --- src/PythonQt.cpp | 4 ++++ tests/PythonQtTestCleanup.cpp | 21 ++++++++++++++++----- tests/PythonQtTestCleanup.h | 2 ++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index d4443528..1ad6242f 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -246,6 +246,10 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName) void PythonQt::cleanup() { if (_self) { + // Remove signal handlers in advance, since destroying them calls back into + // PythonQt::priv()->removeSignalEmitter() + _self->removeSignalHandlers(); + delete _self; _self = NULL; } diff --git a/tests/PythonQtTestCleanup.cpp b/tests/PythonQtTestCleanup.cpp index 796c0eba..b60c7023 100644 --- a/tests/PythonQtTestCleanup.cpp +++ b/tests/PythonQtTestCleanup.cpp @@ -26,16 +26,14 @@ void PythonQtTestCleanup::cleanup() { // Finalize and cleanup after each test - PythonQtObjectPtr main = PythonQt::self()->getMainModule(); - PythonQt::self()->removeVariable(main, "obj"); - delete _helper; - _helper = NULL; - if (Py_IsInitialized()) { Py_Finalize(); } PythonQt::cleanup(); + + delete _helper; + _helper = NULL; } void PythonQtTestCleanup::testQtEnum() @@ -61,6 +59,19 @@ void PythonQtTestCleanup::testCallQtMethodInDel() )); } +void PythonQtTestCleanup::testSignalReceiverCleanup() +{ + PythonQtObjectPtr main = PythonQt::self()->getMainModule(); + + // Test that PythonQtSignalReceiver is cleaned up properly, + // i.e. PythonQt::cleanup() doesn't segfault + main.evalScript( + "import PythonQt.QtCore\n" \ + "timer = PythonQt.QtCore.QTimer(obj)\n" \ + "timer.connect('destroyed()', obj.onDestroyed)\n" \ + ); +} + bool PythonQtTestCleanupHelper::runScript(const char* script) { _passed = false; diff --git a/tests/PythonQtTestCleanup.h b/tests/PythonQtTestCleanup.h index 2c4879e2..b2a15886 100644 --- a/tests/PythonQtTestCleanup.h +++ b/tests/PythonQtTestCleanup.h @@ -19,6 +19,7 @@ private Q_SLOTS: void testQtEnum(); void testCallQtMethodInDel(); + void testSignalReceiverCleanup(); private: PythonQtTestCleanupHelper* _helper; @@ -37,6 +38,7 @@ class PythonQtTestCleanupHelper : public QObject public Q_SLOTS: void setPassed() { _passed = true; } + void onDestroyed(QObject *) { } private: bool _passed; From 5ec0c911a282f5c4c0814182ef218ea0a1d0bdea Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Feb 2016 02:49:35 -0500 Subject: [PATCH 08/14] Fix compilation of test when PythonQt_Wrap_Qtcore option is disabled This commit fixes the following link error: PythonQtTestMain.cpp:(.text+0x1637): undefined reference to `PythonQtTestCleanup::~PythonQtTestCleanup()' --- CMakeLists.txt | 6 ++++-- tests/PythonQtTestMain.cpp | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2349303a..a68fe3eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -270,6 +270,8 @@ if(BUILD_TESTING) tests/PythonQtTestMain.cpp ) + set_property(SOURCE tests/PythonQtTestMain.cpp PROPERTY COMPILE_DEFINITIONS "main=tests_PythonQtTestMain") + list(APPEND test_sources tests/PythonQtTests.cpp tests/PythonQtTests.h @@ -289,9 +291,9 @@ if(BUILD_TESTING) QT4_WRAP_CPP(test_sources tests/PythonQtTestCleanup.h ) - endif() - set_property(SOURCE tests/PythonQtTestMain.cpp PROPERTY COMPILE_DEFINITIONS "main=tests_PythonQtTestMain") + set_property(SOURCE tests/PythonQtTestMain.cpp APPEND PROPERTY COMPILE_DEFINITIONS "PythonQt_Wrap_Qtcore") + endif() add_executable(PythonQtCppTests ${test_sources}) target_link_libraries(PythonQtCppTests PythonQt) diff --git a/tests/PythonQtTestMain.cpp b/tests/PythonQtTestMain.cpp index ac3a37d8..8b498ab4 100644 --- a/tests/PythonQtTestMain.cpp +++ b/tests/PythonQtTestMain.cpp @@ -65,6 +65,7 @@ int main(int argc, char *argv[]) Py_Finalize(); } +#ifdef PythonQt_Wrap_Qtcore PythonQtTestCleanup cleanup; failCount += QTest::qExec(&cleanup, argc, argv); @@ -73,6 +74,8 @@ int main(int argc, char *argv[]) } else { std::cout << "All tests passed successfully." << std::endl; } +#endif + return failCount; } From 0e643189f6d34b52a326232673055de4d3f9208a Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Feb 2016 03:30:06 -0500 Subject: [PATCH 09/14] Remove unused call to qt4_wrap_ui/qt4_add_resources associated with PythonQt library --- CMakeLists.txt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a68fe3eb..347aee6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,19 +208,9 @@ foreach(qtlib core gui network opengl sql svg uitools webkit xml xmlpatterns) endif() endforeach() -#----------------------------------------------------------------------------- -# UI files -set(ui_sources ) - -#----------------------------------------------------------------------------- -# Resources -set(qrc_sources ) - #----------------------------------------------------------------------------- # Do wrapping qt4_wrap_cpp(gen_moc_sources ${moc_sources}) -qt4_wrap_ui(gen_ui_sources ${ui_sources}) -qt4_add_resources(gen_qrc_sources ${qrc_sources}) #----------------------------------------------------------------------------- # Build the library @@ -232,8 +222,6 @@ include_directories( add_library(PythonQt SHARED ${sources} ${gen_moc_sources} - ${gen_ui_sources} - ${gen_qrc_sources} ) set_target_properties(PythonQt PROPERTIES DEFINE_SYMBOL PYTHONQT_EXPORTS) From 3f4e08b969d761dc04af0872c2f4dd0a07ca85b3 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Feb 2016 03:34:39 -0500 Subject: [PATCH 10/14] Add macro pythonqt_wrap_cpp removing the need for qt version specific macros --- CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 347aee6d..9450cd17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,10 @@ else() message(FATAL_ERROR "error: Qt4 was not found on your system. You probably need to set the QT_QMAKE_EXECUTABLE variable") endif() +macro(pythonqt_wrap_cpp) + qt4_wrap_cpp(${ARGV}) +endmacro() + #----------------------------------------------------------------------------- # The variable "generated_cpp_suffix" allows to conditionnally compile the generated wrappers # associated with the Qt version being used. @@ -210,7 +214,7 @@ endforeach() #----------------------------------------------------------------------------- # Do wrapping -qt4_wrap_cpp(gen_moc_sources ${moc_sources}) +pythonqt_wrap_cpp(gen_moc_sources ${moc_sources}) #----------------------------------------------------------------------------- # Build the library @@ -265,7 +269,7 @@ if(BUILD_TESTING) tests/PythonQtTests.h ) - QT4_WRAP_CPP(test_sources + pythonqt_wrap_cpp(test_sources tests/PythonQtTests.h ) @@ -276,7 +280,7 @@ if(BUILD_TESTING) tests/PythonQtTestCleanup.cpp tests/PythonQtTestCleanup.h ) - QT4_WRAP_CPP(test_sources + pythonqt_wrap_cpp(test_sources tests/PythonQtTestCleanup.h ) From 1f9de2a8fecad54736224b246f313a4c63d6996a Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Feb 2016 04:10:10 -0500 Subject: [PATCH 11/14] Re-factor code introducing qt_wrapped_libs variable --- CMakeLists.txt | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9450cd17..fab3aa74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,20 @@ cmake_minimum_required(VERSION 2.8) project(PythonQt) #----------------------------------------------------------------------------- +#---------------------------------------------------------------------------- +# Qt version + +# Set PythonQt_QT_VERSION +set(PythonQt_QT_VERSION 4) + +# Requirements +set(minimum_required_qt4_version "4.6.2") +set(minimum_required_qt_version ${minimum_required_qt${PythonQt_QT_VERSION}_version}) + +# Qt components +set(qt4libs core gui network opengl sql svg uitools webkit xml xmlpatterns) +set(qtlibs ${qt${PythonQt_QT_VERSION}libs}) + #----------------------------------------------------------------------------- # Python libraries @@ -40,17 +54,24 @@ if(NOT DEFINED PythonQt_INSTALL_INCLUDE_DIR) set(PythonQt_INSTALL_INCLUDE_DIR include/PythonQt) endif() -option(PythonQt_Wrap_QtAll "Make all Qt components available in python" OFF) +set(qt4_wrapped_libs ${qt4libs}) +set(qt_wrapped_libs ${qt${PythonQt_QT_VERSION}_wrapped_libs}) -set(qtlibs core gui network opengl sql svg uitools webkit xml xmlpatterns) -foreach(qtlib ${qtlibs}) +# Define PythonQt_Wrap_Qt* options +option(PythonQt_Wrap_QtAll "Make all Qt components available in python" OFF) +foreach(qtlib ${qt_wrapped_libs}) OPTION(PythonQt_Wrap_Qt${qtlib} "Make all of Qt${qtlib} available in python" OFF) endforeach() + # Force option if it applies if(PythonQt_Wrap_QtAll) - list(REMOVE_ITEM qtlibs xmlpatterns) # xmlpatterns wrapper does *NOT* build at all :( - foreach(qtlib ${qtlibs}) + set(_qt_wrapped_libs ${qt_wrapped_libs}) + + # XXX xmlpatterns wrapper does *NOT* build at all :( + list(REMOVE_ITEM _qt_wrapped_libs xmlpatterns) + + foreach(qtlib ${_qt_wrapped_libs}) if(NOT ${PythonQt_Wrap_Qt${qtlib}}) set(PythonQt_Wrap_Qt${qtlib} ON CACHE BOOL "Make all of Qt${qtlib} available in python" FORCE) message(STATUS "Enabling [PythonQt_Wrap_Qt${qtlib}] because of [PythonQt_Wrap_QtAll] evaluates to True") @@ -68,7 +89,6 @@ endif() #----------------------------------------------------------------------------- # Setup Qt -set(minimum_required_qt_version "4.6.2") find_package(Qt4) @@ -81,7 +101,7 @@ if(QT4_FOUND) endif() # Enable required qt module - foreach(qtlib network opengl sql svg uitools webkit xml xmlpatterns) + foreach(qtlib ${qt_wrapped_libs}) string(TOUPPER ${qtlib} qtlib_uppercase) if (NOT ${QT_QT${qtlib_uppercase}_FOUND}) message(FATAL_ERROR "QT_QT${qtlib_uppercase} *not* FOUND - Try to disable PythonQt_Wrap_Qt${qtlib}") @@ -185,7 +205,7 @@ set(moc_sources #----------------------------------------------------------------------------- # Add extra sources -foreach(qtlib core gui network opengl sql svg uitools webkit xml xmlpatterns) +foreach(qtlib ${qt_wrapped_libs}) if (${PythonQt_Wrap_Qt${qtlib}}) From a30fc88e99d1877b83422999ae7b8b3076802420 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Feb 2016 04:37:44 -0500 Subject: [PATCH 12/14] Refactor handling of "generated_cpp_suffix". This will allow to easily add new exceptions when adding support for Qt5.. --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fab3aa74..ba2e1388 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,9 +123,12 @@ endmacro() #----------------------------------------------------------------------------- # The variable "generated_cpp_suffix" allows to conditionnally compile the generated wrappers # associated with the Qt version being used. + +set(generated_cpp_suffix_46 _47) + set(generated_cpp_suffix "_${QT_VERSION_MAJOR}${QT_VERSION_MINOR}") -if("${generated_cpp_suffix}" STREQUAL "_46") - set(generated_cpp_suffix "_47") # Also use 4.7 wrappers for 4.6.x version +if(DEFINED generated_cpp_suffix_${QT_VERSION_MAJOR}${QT_VERSION_MINOR}) + set(generated_cpp_suffix "${generated_cpp_suffix_${QT_VERSION_MAJOR}${QT_VERSION_MINOR}}") endif() #----------------------------------------------------------------------------- From a4d2be5f9319584faea158df9d5b3d571c08128c Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Feb 2016 04:49:16 -0500 Subject: [PATCH 13/14] Add support for Qt5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on work from the following individual: * Melven Röhrig-Zöllner (https://sourceforge.net/p/pythonqt/discussion/631392/thread/5f20c176/) * Julien Finet (@finetjul): See #38) * Arnaud Barre (@alzathar):See #15 * Eric Heim (@eric-h):See #36 --- CMakeLists.txt | 122 ++++++++++++++++++----- generated_cpp_50/PythonQt_QtBindings.cpp | 58 +++++++++++ generated_cpp_50/PythonQt_QtBindings.h | 10 ++ generated_cpp_53/PythonQt_QtBindings.cpp | 58 +++++++++++ generated_cpp_53/PythonQt_QtBindings.h | 10 ++ generated_cpp_54/PythonQt_QtBindings.cpp | 58 +++++++++++ generated_cpp_54/PythonQt_QtBindings.h | 10 ++ 7 files changed, 303 insertions(+), 23 deletions(-) create mode 100644 generated_cpp_50/PythonQt_QtBindings.cpp create mode 100644 generated_cpp_50/PythonQt_QtBindings.h create mode 100644 generated_cpp_53/PythonQt_QtBindings.cpp create mode 100644 generated_cpp_53/PythonQt_QtBindings.h create mode 100644 generated_cpp_54/PythonQt_QtBindings.cpp create mode 100644 generated_cpp_54/PythonQt_QtBindings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ba2e1388..0d1deb31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,14 +14,33 @@ project(PythonQt) #---------------------------------------------------------------------------- # Qt version +# Sanity checks +if(DEFINED Qt5_DIR AND DEFINED QT_QMAKE_EXECUTABLE) + message(FATAL_ERROR + "${PROJECT_NAME} shoult NOT be configured setting both Qt5_DIR and QT_QMAKE_EXECUTABLE options. +To build with Qt4, specify QT_QMAKE_EXECUTABLE. To build with Qt5, specify Qt5_DIR.") +endif() + # Set PythonQt_QT_VERSION -set(PythonQt_QT_VERSION 4) +if(DEFINED Qt5_DIR) + message(STATUS "${PROJECT_NAME}: Setting PythonQt_QT_VERSION to 5 because Qt5_DIR is defined.") + set(PythonQt_QT_VERSION 5) +elseif(DEFINED QT_QMAKE_EXECUTABLE) + message(STATUS "${PROJECT_NAME}: Setting PythonQt_QT_VERSION to 4 because QT_QMAKE_EXECUTABLE is defined.") + set(PythonQt_QT_VERSION 4) +else() + set(PythonQt_QT_VERSION 4 CACHE STRING "Pick a version of Qt to use: 4 or 5") + # Set the possible values of Qt version for cmake-gui + set_property(CACHE PythonQt_QT_VERSION PROPERTY STRINGS "4" "5") +endif() # Requirements +set(minimum_required_qt5_version "5.3.0") set(minimum_required_qt4_version "4.6.2") set(minimum_required_qt_version ${minimum_required_qt${PythonQt_QT_VERSION}_version}) # Qt components +set(qt5libs Core Widgets Network OpenGL Sql Svg UiTools WebKitWidgets Xml XmlPatterns) set(qt4libs core gui network opengl sql svg uitools webkit xml xmlpatterns) set(qtlibs ${qt${PythonQt_QT_VERSION}libs}) @@ -54,15 +73,32 @@ if(NOT DEFINED PythonQt_INSTALL_INCLUDE_DIR) set(PythonQt_INSTALL_INCLUDE_DIR include/PythonQt) endif() +# Since the Qt bindings sources used for both Qt4 and Qt5 are +# grouped using Qt4 naming convention, qt_wrapped_libs variables are the +# same for the two Qt versions. set(qt4_wrapped_libs ${qt4libs}) +set(qt5_wrapped_libs ${qt4libs}) set(qt_wrapped_libs ${qt${PythonQt_QT_VERSION}_wrapped_libs}) +set(qt5_wrapped_lib_depends_gui Multimedia) + +set(qtlib_to_wraplib_Widgets gui) +set(qtlib_to_wraplib_WebKitWidgets webkit) + # Define PythonQt_Wrap_Qt* options option(PythonQt_Wrap_QtAll "Make all Qt components available in python" OFF) foreach(qtlib ${qt_wrapped_libs}) OPTION(PythonQt_Wrap_Qt${qtlib} "Make all of Qt${qtlib} available in python" OFF) endforeach() +# Set qtlib_to_wraplib_* variables +foreach(qtlib ${qtlibs}) + string(TOLOWER ${qtlib} qtlib_lowercase) + if(DEFINED qtlib_to_wraplib_${qtlib}) + set(qtlib_lowercase ${qtlib_to_wraplib_${qtlib}}) + endif() + set(qtlib_to_wraplib_${qtlib} ${qtlib_lowercase}) +endforeach() # Force option if it applies if(PythonQt_Wrap_QtAll) @@ -89,46 +125,86 @@ endif() #----------------------------------------------------------------------------- # Setup Qt +if(PythonQt_QT_VERSION VERSION_GREATER "4") -find_package(Qt4) + # Required components + set(qt_required_components Core Widgets) + foreach(qtlib ${qtlibs}) + set(qt_wrapped_lib ${qtlib_to_wraplib_${qtlib}}) + if(${PythonQt_Wrap_Qt${qt_wrapped_lib}}) + list(APPEND qt_required_components ${qtlib} ${qt${PythonQt_QT_VERSION}_wrapped_lib_depends_${qt_wrapped_lib}}) + endif() + endforeach() + if(BUILD_TESTING) + list(APPEND qt_required_components Test) + endif() + list(REMOVE_DUPLICATES qt_required_components) -if(QT4_FOUND) + message(STATUS "${PROJECT_NAME}: Required Qt components [${qt_required_components}]") + find_package(Qt5 ${minimum_required_qt_version} COMPONENTS ${qt_required_components} REQUIRED) - set(found_qt_version ${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}) + set(QT_LIBRARIES ) + foreach(qtlib ${qt_required_components}) + include_directories(${Qt5${qtlib}_INCLUDE_DIRS}) + add_definitions(${Qt5${qtlib}_DEFINITIONS}) + list(APPEND QT_LIBRARIES ${Qt5${qtlib}_LIBRARIES}) + endforeach() - if(${found_qt_version} VERSION_LESS ${minimum_required_qt_version}) - message(FATAL_ERROR "error: PythonQt requires Qt >= ${minimum_required_qt_version} -- you cannot use Qt ${found_qt_version}.") - endif() + set(QT_VERSION_MAJOR ${Qt5Core_VERSION_MAJOR}) + set(QT_VERSION_MINOR ${Qt5Core_VERSION_MINOR}) + + macro(pythonqt_wrap_cpp) + qt5_wrap_cpp(${ARGV}) + endmacro() - # Enable required qt module - foreach(qtlib ${qt_wrapped_libs}) - string(TOUPPER ${qtlib} qtlib_uppercase) - if (NOT ${QT_QT${qtlib_uppercase}_FOUND}) - message(FATAL_ERROR "QT_QT${qtlib_uppercase} *not* FOUND - Try to disable PythonQt_Wrap_Qt${qtlib}") +else() + + find_package(Qt4) + + if(QT4_FOUND) + + set(found_qt_version ${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}) + + if(${found_qt_version} VERSION_LESS ${minimum_required_qt_version}) + message(FATAL_ERROR "error: PythonQt requires Qt >= ${minimum_required_qt_version} -- you cannot use Qt ${found_qt_version}.") endif() - set(QT_USE_QT${qtlib_uppercase} ${PythonQt_Wrap_Qt${qtlib}}) - endforeach() - set(QT_USE_QTTEST ${BUILD_TESTING}) + # Enable required qt module + foreach(qtlib ${qt_wrapped_libs}) + string(TOUPPER ${qtlib} qtlib_uppercase) + if (NOT ${QT_QT${qtlib_uppercase}_FOUND}) + message(FATAL_ERROR "QT_QT${qtlib_uppercase} *not* FOUND - Try to disable PythonQt_Wrap_Qt${qtlib}") + endif() + set(QT_USE_QT${qtlib_uppercase} ${PythonQt_Wrap_Qt${qtlib}}) + endforeach() - include(${QT_USE_FILE}) -else() - message(FATAL_ERROR "error: Qt4 was not found on your system. You probably need to set the QT_QMAKE_EXECUTABLE variable") -endif() + # Enable QtTest in Qt4 is the option BUILD_TESTING was activated + set(QT_USE_QTTEST ${BUILD_TESTING}) -macro(pythonqt_wrap_cpp) - qt4_wrap_cpp(${ARGV}) -endmacro() + include(${QT_USE_FILE}) + else() + message(FATAL_ERROR "error: Qt4 was not found on your system. You probably need to set the QT_QMAKE_EXECUTABLE variable") + endif() + + macro(pythonqt_wrap_cpp) + qt4_wrap_cpp(${ARGV}) + endmacro() + +endif() #----------------------------------------------------------------------------- # The variable "generated_cpp_suffix" allows to conditionnally compile the generated wrappers # associated with the Qt version being used. set(generated_cpp_suffix_46 _47) +set(generated_cpp_suffix_52 _50) +set(generated_cpp_suffix_51 _50) set(generated_cpp_suffix "_${QT_VERSION_MAJOR}${QT_VERSION_MINOR}") if(DEFINED generated_cpp_suffix_${QT_VERSION_MAJOR}${QT_VERSION_MINOR}) set(generated_cpp_suffix "${generated_cpp_suffix_${QT_VERSION_MAJOR}${QT_VERSION_MINOR}}") +elseif(${QT_VERSION_MAJOR}.${QT_VERSION_MINOR} VERSION_GREATER "5.4") + set(generated_cpp_suffix "_54") endif() #----------------------------------------------------------------------------- @@ -216,7 +292,7 @@ foreach(qtlib ${qt_wrapped_libs}) set(file_prefix generated_cpp${generated_cpp_suffix}/com_trolltech_qt_${qtlib}/com_trolltech_qt_${qtlib}) - foreach(index RANGE 0 11) + foreach(index RANGE 0 12) # Source files if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file_prefix}${index}.cpp) diff --git a/generated_cpp_50/PythonQt_QtBindings.cpp b/generated_cpp_50/PythonQt_QtBindings.cpp new file mode 100644 index 00000000..7ae1630a --- /dev/null +++ b/generated_cpp_50/PythonQt_QtBindings.cpp @@ -0,0 +1,58 @@ + +#include "PythonQt_QtBindings.h" + +#include "PythonQt.h" + +void PythonQt_init_QtGui(PyObject*); +void PythonQt_init_QtSvg(PyObject*); +void PythonQt_init_QtSql(PyObject*); +void PythonQt_init_QtNetwork(PyObject*); +void PythonQt_init_QtCore(PyObject*); +void PythonQt_init_QtWebKit(PyObject*); +void PythonQt_init_QtOpenGL(PyObject*); +void PythonQt_init_QtXml(PyObject*); +void PythonQt_init_QtXmlPatterns(PyObject*); +void PythonQt_init_QtUiTools(PyObject*); + +PYTHONQT_EXPORT void PythonQt_init_QtBindings() + { + #ifdef PYTHONQT_WRAP_Qtcore + PythonQt_init_QtCore(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtgui + PythonQt_init_QtGui(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtnetwork + PythonQt_init_QtNetwork(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtopengl + PythonQt_init_QtOpenGL(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtsql + PythonQt_init_QtSql(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtsvg + PythonQt_init_QtSvg(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtuitools + PythonQt_init_QtUiTools(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtwebkit + PythonQt_init_QtWebKit(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtxml + PythonQt_init_QtXml(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtxmlpatterns + PythonQt_init_QtXmlPatterns(0); + #endif + }; diff --git a/generated_cpp_50/PythonQt_QtBindings.h b/generated_cpp_50/PythonQt_QtBindings.h new file mode 100644 index 00000000..03e96aed --- /dev/null +++ b/generated_cpp_50/PythonQt_QtBindings.h @@ -0,0 +1,10 @@ +#ifndef _PYTHONQT_QTBINDINGS_H +#define _PYTHONQT_QTBINDINGS_H + +#include "PythonQtSystem.h" + +/// Initialize Qt bindings enabled at configuration time +PYTHONQT_EXPORT void PythonQt_init_QtBindings(); + +#endif + diff --git a/generated_cpp_53/PythonQt_QtBindings.cpp b/generated_cpp_53/PythonQt_QtBindings.cpp new file mode 100644 index 00000000..7ae1630a --- /dev/null +++ b/generated_cpp_53/PythonQt_QtBindings.cpp @@ -0,0 +1,58 @@ + +#include "PythonQt_QtBindings.h" + +#include "PythonQt.h" + +void PythonQt_init_QtGui(PyObject*); +void PythonQt_init_QtSvg(PyObject*); +void PythonQt_init_QtSql(PyObject*); +void PythonQt_init_QtNetwork(PyObject*); +void PythonQt_init_QtCore(PyObject*); +void PythonQt_init_QtWebKit(PyObject*); +void PythonQt_init_QtOpenGL(PyObject*); +void PythonQt_init_QtXml(PyObject*); +void PythonQt_init_QtXmlPatterns(PyObject*); +void PythonQt_init_QtUiTools(PyObject*); + +PYTHONQT_EXPORT void PythonQt_init_QtBindings() + { + #ifdef PYTHONQT_WRAP_Qtcore + PythonQt_init_QtCore(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtgui + PythonQt_init_QtGui(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtnetwork + PythonQt_init_QtNetwork(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtopengl + PythonQt_init_QtOpenGL(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtsql + PythonQt_init_QtSql(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtsvg + PythonQt_init_QtSvg(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtuitools + PythonQt_init_QtUiTools(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtwebkit + PythonQt_init_QtWebKit(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtxml + PythonQt_init_QtXml(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtxmlpatterns + PythonQt_init_QtXmlPatterns(0); + #endif + }; diff --git a/generated_cpp_53/PythonQt_QtBindings.h b/generated_cpp_53/PythonQt_QtBindings.h new file mode 100644 index 00000000..03e96aed --- /dev/null +++ b/generated_cpp_53/PythonQt_QtBindings.h @@ -0,0 +1,10 @@ +#ifndef _PYTHONQT_QTBINDINGS_H +#define _PYTHONQT_QTBINDINGS_H + +#include "PythonQtSystem.h" + +/// Initialize Qt bindings enabled at configuration time +PYTHONQT_EXPORT void PythonQt_init_QtBindings(); + +#endif + diff --git a/generated_cpp_54/PythonQt_QtBindings.cpp b/generated_cpp_54/PythonQt_QtBindings.cpp new file mode 100644 index 00000000..7ae1630a --- /dev/null +++ b/generated_cpp_54/PythonQt_QtBindings.cpp @@ -0,0 +1,58 @@ + +#include "PythonQt_QtBindings.h" + +#include "PythonQt.h" + +void PythonQt_init_QtGui(PyObject*); +void PythonQt_init_QtSvg(PyObject*); +void PythonQt_init_QtSql(PyObject*); +void PythonQt_init_QtNetwork(PyObject*); +void PythonQt_init_QtCore(PyObject*); +void PythonQt_init_QtWebKit(PyObject*); +void PythonQt_init_QtOpenGL(PyObject*); +void PythonQt_init_QtXml(PyObject*); +void PythonQt_init_QtXmlPatterns(PyObject*); +void PythonQt_init_QtUiTools(PyObject*); + +PYTHONQT_EXPORT void PythonQt_init_QtBindings() + { + #ifdef PYTHONQT_WRAP_Qtcore + PythonQt_init_QtCore(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtgui + PythonQt_init_QtGui(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtnetwork + PythonQt_init_QtNetwork(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtopengl + PythonQt_init_QtOpenGL(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtsql + PythonQt_init_QtSql(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtsvg + PythonQt_init_QtSvg(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtuitools + PythonQt_init_QtUiTools(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtwebkit + PythonQt_init_QtWebKit(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtxml + PythonQt_init_QtXml(0); + #endif + + #ifdef PYTHONQT_WRAP_Qtxmlpatterns + PythonQt_init_QtXmlPatterns(0); + #endif + }; diff --git a/generated_cpp_54/PythonQt_QtBindings.h b/generated_cpp_54/PythonQt_QtBindings.h new file mode 100644 index 00000000..03e96aed --- /dev/null +++ b/generated_cpp_54/PythonQt_QtBindings.h @@ -0,0 +1,10 @@ +#ifndef _PYTHONQT_QTBINDINGS_H +#define _PYTHONQT_QTBINDINGS_H + +#include "PythonQtSystem.h" + +/// Initialize Qt bindings enabled at configuration time +PYTHONQT_EXPORT void PythonQt_init_QtBindings(); + +#endif + From 1b07b18129424c6e83d482b0a38d749fbb0a6ee9 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Tue, 24 May 2016 16:29:47 -0400 Subject: [PATCH 14/14] Add missing link to OpenGL library --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d1deb31..57ab1fc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,6 +192,12 @@ else() endif() +if(UNIX) + find_package(OpenGL) + if(OPENGL_FOUND) + list(APPEND QT_LIBRARIES ${OPENGL_LIBRARIES}) + endif() +endif() #----------------------------------------------------------------------------- # The variable "generated_cpp_suffix" allows to conditionnally compile the generated wrappers # associated with the Qt version being used.