sip6 converts all enums to python Enums, but ONLY creates
Enums with IntFlags types when the c++ type is an enum class : int.
Accordingly we need to patch back in all the operations which treat
enum values as ints, like |, &, bool, etc.
The long term solution here is to move all our c++ enums to enum
class, but that's not always straightforward and can break API
for plugins if it involves the signature of virtual methods.
Since this is an enum designed to be extended in c++ (eg by adding
values after Qt's user role), we have to do some fancy wrapping
in order to make these extended roles look like the types
acceptable to the PyQt function.
This has been designed to allow other functions to be wrapped
which rely on custom enum values.
Here we have to break with our previous approach of treating
null variants (NULL in Python) different to invalid qvariants (None in
Python)
There's simply NO way to construct null variants in PyQt6 -- they
are ALWAYS mapped across to Py_None.
This isn't as big a deal as it sounds, we already made the decision
in c++ code to move to invalid variants in favour of null variants.
Note that we STILL need the custom sip code here and can't rely
on base PyQt6 null variant conversion, as that relies on
QVariant::isNull when we must use QgsVariantUtils::isNull so
that the underlying type is correctly checked for null values
on Qt 6 builds.
Add a plugin metadata key for "supports_qt6". Plugins which can
safely be loaded in QGIS builds based on Qt6 can set
supportsQt6=yes
in their metadata.txt to advertise that they are safe to load
on Qt 6 builds
Since QVariant.Type doesn't exist in PyQt6, we patch it back
in by setting them to their QMetaType.Type equivalents.
This means that code which calls eg:
field = QgsField('my_field', QVariant.Int)
will work as usual under Qt5, while on Qt6 builds it will work
without change. (Since QVariant.Int is QMetaType.Type.Int, and
the PyQGIS6 sip conversion code will kick in and transparently
convert the QMetaType.Type.Int value to QVariant::Int when
calling the underlying c++ method)
QVariant::Type does not exist in PyQt6 as its been deprecated
and replaced with QMetaType::Type.
In order to avoid breaking PyQGIS API, we don't want to change
all our functions to use QMetaType::Type instead of QVariant::Type
(that can wait till QGIS 4.0). So instead we leave the c++/Qt 5
signatures as QVariant::Type, but accept QMetaType::Type values
for these functions under Qt 6 builds.
While QVariant::Type can be directly static_cast to QMetaType::Type,
the reverse is not true and many QMetaType::Type values don't
have exact counterparts in QVariant::Type.
So we use the logic:
- If no conversion is possible, QVariant::UserType will be returned.
Note that we don't use QVariant::Invalid, as the value DOES have
a type, it's just one which needs special handling (just like user
types do)
- Some conversions are lossy, in that the QVariant::Type cannot
represent the full range of values possible in QMetaType::Type.
In these cases the returned type will be an "expanded" type
capable of storing the full range of values possible in the
original type. Eg we map QMetaType::Type::Float to QVariant::Type::Double
QgsVariantUtils::variantTypeToMetaType is included for clarity/
completeness/future proof-ness, even though it currently can
be handled with just a simple static cast.
This ensures that we correctly apply filters to recent projections
too, eg so that a widget showing only vertical crs will ONLY show
recent VERTICAL crs, not every recent crs.