PyQt5/PyQt6 Compatibility Implementation
The plugin features a PyQt5/PyQt6 compatibility implementation trying to make it work across different QGIS installations using either PyQt5 or PyQt6. The compatibility layer should be dropped when QGIS fully transitions to PyQt6.
Overview
The PyQt5/PyQt6 compatibility layer detects the PyQt version at runtime and provides unified interfaces for PyQt-specific functionality.
Some Differences Between PyQt5 and PyQt6
QVariant changes: In PyQt6,
QVariantis deprecated and Python native types are used insteadQMetaType changes: API restructuring with nested enum access patterns
Dialog result constants: Moved from class attributes to enum values (
QDialog.Accepted→QDialog.DialogCode.Accepted)Cursor shape constants: Restructured into nested enums (
Qt.ArrowCursor→Qt.CursorShape.ArrowCursor)Enum structure: Proper Python enums in PyQt6 vs class attributes in PyQt5
Compatibility Module Implementation
Core Implementation
The main compatibility logic is implemented in dip_strike_tools/toolbelt/qt_compat.py, which provides:
Runtime PyQt version detection: Automatically detects whether PyQt5 or PyQt6 is being used
QVariant compatibility: Handles the deprecation of
QVariantin PyQt6QMetaType compatibility: Provides unified access to type constants
Dialog result constants: Unified access to dialog result values
Cursor shape constants: Handles enum structure changes in PyQt6
Key Components
1. Version Detection
from dip_strike_tools.toolbelt import IS_PYQT5, IS_PYQT6, get_qt_version_info
# Runtime detection
if IS_PYQT6:
# PyQt6 specific code
else:
# PyQt5 specific code
# Get detailed version info
info = get_qt_version_info()
2. QVariant Compatibility
from dip_strike_tools.toolbelt import QVariant, qvariant_cast
# Use QVariant types consistently
field.setType(QVariant.Double) # Works in both PyQt5 and PyQt6
field.setType(QVariant.String) # Works in both PyQt5 and PyQt6
# Cast values safely
value = qvariant_cast(raw_value, QVariant.Double)
3. QMetaType Compatibility
from dip_strike_tools.toolbelt import QMetaTypeWrapper as QMetaType
# Use QMetaType consistently
field.setType(QMetaType.Double) # Works in both versions
field.setType(QMetaType.QString) # Works in both versions
4. Dialog Result Constants
from dip_strike_tools.toolbelt import DIALOG_ACCEPTED, DIALOG_REJECTED
# Check dialog results consistently
if dialog.result() == DIALOG_ACCEPTED:
# Handle accepted dialog
elif dialog.result() == DIALOG_REJECTED:
# Handle rejected dialog
5. Enhanced Enum Handling
from dip_strike_tools.toolbelt import enum_value, get_dialog_result
# Generic enum value access
value = enum_value(SomeEnum, "ValueName")
# Specific dialog result handling
accepted = get_dialog_result(QDialog, "Accepted") # Works in both PyQt5/6
Usage Patterns
Field Type Setting
from dip_strike_tools.toolbelt import QMetaTypeWrapper as QMetaType
# Create a field with double type
field = QgsField()
field.setName("value_field")
field.setType(QMetaType.Double) # Works in both PyQt5/6
Value Handling
from dip_strike_tools.toolbelt import QVariant, qvariant_cast
# Safe value extraction
def extract_value(qvariant_value):
if qvariant_value is None:
return None
elif isinstance(qvariant_value, QVariant):
if qvariant_value.isNull():
return None
else:
return qvariant_value.value()
else:
return qvariant_value
Type Checking
from dip_strike_tools.toolbelt import QVariant
# Check field types consistently
if field.type() == QVariant.Double:
# Handle double field
elif field.type() == QVariant.String:
# Handle string field
Maintenance
When adding new PyQt-dependent code:
Import from toolbelt: Use compatibility imports instead of direct PyQt imports
Check compatibility module: Add new compatibility patterns if needed
Test both versions: Ensure new code works with both PyQt5 and PyQt6
Update documentation: Document any new compatibility patterns