Avoid loading conflicting version of PyQt
If Python's search path (sys.path) happens to contain a different version of PyQt, loading that PyQt will load a conflicting version of Qt that'll crash Krita when used.
Due to the hacky try/except we use for trying to import the right PyQt version, this can easily be triggered on Krita 5.3. But even if we found a better way, user plugins importing the wrong PyQt version should ideally not crash Krita with such an obscure message as "QWidget: Must construct a QApplication before a QWidget".
So this patch checks for the wrong PyQt and removes its parent folder from sys.path before it can be loaded. That removes any neighboring modules as well (such as the contents of a site-packages directory), but if the user wants those they can re-add them or disable their PyQt. (We could also go to the trouble of re-adding them automatically if we wanted.)
In order to detect the wrong PyQt version, this adds a new scripting function
pykrita.qt_major_version().
It also moves the check for PyQt in krita/__init__.py to actually happen before
anything else tries to import PyQt.
Test Plan
- Put PyQt6 somewhere on Python's sys.path and run Krita 5.3. You should get a message like
Found PyQt6 in '/Users/name/Library/Python/3.13/lib/python/site-packages', removing from Python's search path to avoid crashes.
instead of
objc[48800]: Class QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE is implemented in both /kd/_install/lib/QtCore.framework/Versions/5/QtCore (0x109ea12e0) and /Users/name/Library/Python/3.13/lib/python/site-packages/PyQt6/Qt6/lib/QtCore.framework/Versions/A/QtCore (0x165f60730). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[48800]: Class KeyValueObserver is implemented in both /kd/_install/lib/QtCore.framework/Versions/5/QtCore (0x109ea1308) and /Users/name/Library/Python/3.13/lib/python/site-packages/PyQt6/Qt6/lib/QtCore.framework/Versions/A/QtCore (0x165f60758). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[48800]: Class RunLoopModeTracker is implemented in both /kd/_install/lib/QtCore.framework/Versions/5/QtCore (0x109ea1358) and /Users/name/Library/Python/3.13/lib/python/site-packages/PyQt6/Qt6/lib/QtCore.framework/Versions/A/QtCore (0x165f607f8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
/kd/_install/lib/krita-python-libs/krita added to PYTHONPATH
QWidget: Must construct a QApplication before a QWidget
and a crash.
Formalities Checklist
- I confirmed this builds.
- I confirmed Krita ran and the relevant functions work.
- I tested the relevant unit tests and can confirm they are not broken. (If not possible, don't hesitate to ask for help!)
- I made sure my commits build individually and have good descriptions as per KDE guidelines.
- I made sure my code conforms to the standards set in the HACKING file.
- I can confirm the code is licensed and attributed appropriately, and that unattributed code is mine, as per KDE Licensing Policy.
- Does the patch add a user-visible feature? If yes, is there a documentation MR ready for it at Krita Documentation Repository?
Reminder: the reviewer is responsible for merging the patch, this is to ensure at the least two people can build the patch. In case a patch breaks the build, both the author and the reviewer should be contacted to fix the build. If this is not possible, the commits shall be reverted, and a notification with the reasoning and any relevant logs shall be sent to the mailing list, kimageshop@kde.org.