ENB: Notes about supporting Qt6

26 views
Skip to first unread message

Edward K. Ream

unread,
Apr 10, 2021, 5:39:41 PM4/10/21
to leo-editor
The ekr-qt branch contains a lot of new code. It may be months before the Qt6 code is as solid as the present (Qt4 and Qt5) code.

Overview

Qt6 breaks a lot of code in Leo. #1846 is a major project.

Qt6 requires code to access constants via specific enumeration. Qt4/5 code can access constants directly from QtCore.Qt or from various class constants. The old constants are integers.

Such direct access doesn't work in Qt6. Unlike as in Qt5, the constants do not reside directly in QtCore.Qt. Instead, the constants are true enumeration types, not ints. Qt6 code must know the name of the enumeration.  At present, Leo uses 41 such enumerations!

leoQt.py

leoQt.py is the main wrapper file. It now uses three helpers: leoQt4.py, leoQt5.py, and leoQt6.py. All of Leo's code, including plugins, should only ever import leoQt.py.

Modulo details, leoQt6.py is:

<< define all exported constants as None >>
if not g.in_bridge:
    try:
        from leo.core.leoQt6 import *
        isQt6 = True
    except Exception:
        try:
            from leo.core.leoQt5 import *
            isQt5 = True
        except Exception:
            try:
                from leo.core.leoQt4 import *
                isQt4 = True
            except Exception:
                pass


This framework simplifies the helpers. Leo's bridge gets None for all exported constants. Each helper only imports the modules that make sense.

Example compatibility code

To repeat, qt6 breaks lots of existing code. Here is an example of the required changes. Old:

if event.button() == QtCore.Qt.RightButton:
   ...


New:

MouseButtons = QtCore.Qt.MouseButtons if isQt6 else QtCore.Qt
if event.button() == MouseButtons.RightButton:
      ...

Yes, the new code is ugly. I considered and rejected some faux-clever ways that hid the complexities. Imo, it's better to show the differences between the old and new ways explicitly. The isQt6 constant marks all new code.

Other problems

Enumerations are not serializable. In nested_splitter.py, I had to convert enums to ints "by hand."

In pyqt4/6, the "exec" method (for widgets) is named "exec_". In pyqt6, the widget methods are named "exec."

As a workaround, leoQt.py injects the "exec_" methods into QWidget, allowing most code to use "exec_" as before. However, QApplication objects must call "exec". Apparently, the injection doesn't happen soon enough.

event.pos() doesn't exist in pyqt6. The workaround:

point = event.position().toPoint() if isQt6 else event.pos()

Summary

It wasn't pleasant making hundreds of changes to the code. However, in the long run the new code will be more robust. The isQt6 constant marks all the changes.

Users who run qt4 or qt5 should have few problems. The code should work exactly as before. I plan to wait only a week or so before merging ekr-qt into devel.

Alas, I could have missed places where new qt6 code is needed.  In the absence of unit tests, we will have to wait for bug reports from qt6 users. The testing period may take months.

All comments welcome.

Edward

P.S. The VR3 plugin doesn't seem to work as yet.

EKR

Reply all
Reply to author
Forward
0 new messages