Skip to content

QGIS 4 / Qt6 Compatibility Issues - #804

@osundwajeff

Description

@osundwajeff

QGIS 4 / Qt6 Compatibility Issues - Grouped Summary

This document groups the 47 compatibility issues by type/pattern for easier understanding.

Issue Categories

  1. Build & Configuration Issues (3 issues)
  2. Qt Enum Reorganizations (35 issues)
  3. API Renames & Removals (3 issues)
  4. Type Strictness (int -> enum) (5 issues)
  5. Plugin-Specific Bugs (1 issue)

1. Build & Configuration Issues

Issue 1: Version Constraint Too Restrictive

File: config.json:5

qgisMaximumVersion set to 3.99.

Fix: Change to 4.99


Issue 2: Direct PyQt5 Import

File: gui/scenario_item_widget.py:6

Direct import from PyQt5 instead of using qgis.PyQt compatibility layer.

Fix: Use from qgis.PyQt.QtWidgets import ...


Issue 3: Compiled Resources Using pyrcc5

Files: admin.py:319, generated resources.py

pyrcc5 generates PyQt5-specific code, but it's deprecated in Qt6.

Fix: Resources are handled differently in Qt6.


2. Qt Enum Reorganizations

Pattern: Qt6 moved most enums into typed sub-namespaces.

  • Qt5: Qt.AlignLeft, QDialog.Accepted, etc.
  • Qt6: Qt.AlignmentFlag.AlignLeft, QDialog.DialogCode.Accepted, etc.

Possible Solution: Create file e.g. qt_compat.py with compatibility layer providing unified names for both Qt5 and Qt6.

Affected Enum Categories

A. Qt Core Enums (Issues 4, 7, 12, 16, 17, 22, 23, 24, 34, 35, 46)

Issue Enum Type Qt5 Example Qt6 Namespace
4 Alignment flags Qt.AlignLeft Qt.AlignmentFlag.AlignLeft
7 User role Qt.UserRole Qt.ItemDataRole.UserRole
12 Sort order Qt.AscendingOrder Qt.SortOrder.AscendingOrder
16 Orientation Qt.Horizontal Qt.Orientation.Horizontal
17 Check state Qt.Checked Qt.CheckState.Checked
22 Display role Qt.DisplayRole Qt.ItemDataRole.DisplayRole
23 Colors Qt.red Qt.GlobalColor.red
24 Case sensitivity Qt.CaseSensitive Qt.CaseSensitivity.CaseSensitive
34 Event types QEvent.Resize QEvent.Type.Resize
35 Window flags Qt.WindowMinimizeButtonHint Qt.WindowType.WindowMinimizeButtonHint
46 Item data roles Qt.DisplayRole Qt.ItemDataRole.DisplayRole

Files affected: 50+ across gui/, models/, lib/
QtCompat names: AlignLeft, UserRole, SortAscending, Horizontal, CheckStateChecked, DisplayRole, ColorRed, CaseSensitive, EventResize, WindowMinimizeButtonHint


B. Widget Enums (Issues 8, 9, 10, 11, 15, 21, 26, 27, 28, 29, 30, 31, 32, 33, 47)

Issue Widget Class Qt5 Example Qt6 Namespace
8 QToolButton QToolButton.MenuButtonPopup QToolButton.ToolButtonPopupMode.MenuButtonPopup
9 QSizePolicy QSizePolicy.Fixed QSizePolicy.Policy.Fixed
10 QAbstractItemView QAbstractItemView.ExtendedSelection QAbstractItemView.SelectionMode.ExtendedSelection
11 QAbstractItemView QAbstractItemView.InternalMove QAbstractItemView.DragDropMode.InternalMove
15 Qt Item flags Qt.ItemIsEnabled Qt.ItemFlag.ItemIsEnabled
21 QHeaderView QHeaderView.Stretch QHeaderView.ResizeMode.Stretch
26 QDialog QDialog.Accepted QDialog.DialogCode.Accepted
27 QWizard pixmaps QWizard.LogoPixmap QWizard.WizardPixmap.LogoPixmap
28 QDialogButtonBox QDialogButtonBox.Ok QDialogButtonBox.StandardButton.Ok
29 QFrame QFrame.StyledPanel QFrame.Shape.StyledPanel
30 QWizard buttons QWizard.HelpButton QWizard.WizardButton.HelpButton
31 Brush styles Qt.SolidPattern Qt.BrushStyle.SolidPattern
32 Match flags Qt.MatchFixedString Qt.MatchFlag.MatchFixedString
33 QItemSelectionModel QItemSelectionModel.ClearAndSelect QItemSelectionModel.SelectionFlag.ClearAndSelect
47 QStyle QStyle.SP_DialogCloseButton QStyle.StandardPixmap.SP_DialogCloseButton

Files affected: 40+ across gui/, models/
QtCompat names: MenuButtonPopup, SizePolicyFixed, SelectionExtendedSelection, DragDropInternalMove, ItemIsEnabled, HeaderStretch, DialogAccepted, WizardLogoPixmap, ButtonOk, FrameStyledPanel, WizardHelpButton, BrushSolidPattern, MatchFixedString, SelectionClearAndSelect, StyleSP_DialogCloseButton


C. File Dialog Enums (Issue 19)

Qt5 Qt6
QFileDialog.DontUseNativeDialog QFileDialog.Option.DontUseNativeDialog

Files: gui/qgis_cplus_main.py, gui/activity_widget.py
QtCompat name: FileDialogDontUseNativeDialog


D. Network Enums (Issues 36, 37, 40, 44)

Issue Enum Type Qt5 Example Qt6 Namespace
36 Network errors QNetworkReply.NoError QNetworkReply.NetworkError.NoError
37 Request attributes QNetworkRequest.HttpStatusCodeAttribute QNetworkRequest.Attribute.HttpStatusCodeAttribute
40 Known headers QNetworkRequest.ContentTypeHeader QNetworkRequest.KnownHeaders.ContentTypeHeader
44 Known headers QNetworkRequest.ETagHeader QNetworkRequest.KnownHeaders.ETagHeader

Files: api/request.py, trends_earth/api.py, trends_earth/download.py
QtCompat names: NetworkNoError, RequestHttpStatusCodeAttribute, RequestContentTypeHeader, RequestETagHeader


E. Other Qt Enums (Issues 38, 42)

Issue Enum Type Qt5 Example Qt6 Namespace
38 QTextCursor QTextCursor.End QTextCursor.MoveOperation.End
42 QIODevice QIODevice.ReadOnly QIODevice.OpenModeFlag.ReadOnly

Files: gui/qgis_cplus_main.py, lib/reports/generator.py, gui/component_item_model.py
QtCompat names: CursorEnd, IODeviceReadOnly


3. API Renames & Removals

Issue 5: QStandardItem.UserType Renamed

Pattern: UserType -> UserType()
Files: 11 occurrences across gui/
Fix: Added item_user_type() helper function


Issue 6: QTreeWidgetItem.UserType Renamed

Pattern: UserType -> UserType()
Files: 2 occurrences
Fix: Added tree_item_user_type() helper function


Issue 14: QFontDatabase API Changed to Static Methods

Pattern: Instance methods -> static methods
Example: QFontDatabase().families() -> QFontDatabase.families()
Files: lib/reports/generator.py:2140,2180


Issue 18: Qt.ItemIsTristate Removed

Qt5: Qt.ItemIsTristate existed
Qt6: Removed entirely
Files: gui/qgis_cplus_main.py:904
Fix: Removed usage, functionality unchanged


Issue 25: QDialog.exec_() Renamed to exec()

Pattern: exec_() -> exec()
Reason: Python 3 no longer has exec keyword
Files: 35 occurrences across 16 files
Fix: Used sed to replace all exec_() calls


4. Type Strictness (int -> enum)

Pattern: QGIS 4/Qt6 APIs that previously accepted integers now require proper enum types.

Issue 20: QgsMessageLog.logMessage() Level Parameter

File: utils.py:96
Problem: level parameter rejects int, requires Qgis.MessageLevel enum
Fix: Use try/except to handle both Qgis.MessageLevel.Info (QGIS 4) and Qgis.Info (QGIS 3)


Issue 39: Qgis.MessageLevel Enum

File: utils.py:96-111
Problem: Similar to #20, cascading effects from int level usage
Fix: Proper enum usage with try/except fallback


Issue 41: QgsMessageBar.pushMessage() Level Parameter

File: gui/qgis_cplus_main.py:2805-2835
Problem: level parameter rejects int
Fix: Added int-to-enum conversion map in show_message() method:

if isinstance(level, int):
    level_map = {
        0: Qgis.MessageLevel.Info,
        1: Qgis.MessageLevel.Warning,
        2: Qgis.MessageLevel.Critical,
        3: Qgis.MessageLevel.Success,
    }
    level = level_map.get(level, level)

Issue 45: QgsLayoutTableColumn.setHAlignment() Requires Enum

File: models/report.py:143
Problem: When MetricColumn dataclass is deserialized from storage, alignment enum becomes int
Fix: Added int-to-enum conversion in to_qgs_column():

if isinstance(alignment, int):
    alignment_map = {
        1: QtCompat.AlignLeft,
        2: QtCompat.AlignRight,
        4: QtCompat.AlignHCenter,
    }
    alignment = alignment_map.get(alignment, QtCompat.AlignHCenter)

Issue 13: Qt Dock Widget Area Enums (also type strictness)

Similar pattern where DockWidgetArea enums now require proper types.


5. Plugin-Specific Bugs

Issue 43: Settings Enum Not Converted Before QSettings Usage

File: conf.py:318, 340-343
Problem: set_value() and get_value() converted Settings enum to .value AFTER using it in f-string
Symptom: "Empty key passed" warnings, memory corruption crash
Root cause:

# WRONG:
self.settings.setValue(f"{BASE_GROUP_NAME}/{name}", value)  # name is enum
if isinstance(name, Settings):
    name = name.value  # Too late!

# CORRECT:
if isinstance(name, Settings):
    name = name.value  # Convert BEFORE using
self.settings.setValue(f"{BASE_GROUP_NAME}/{name}", value)

Why it crashed: When enum used in f-string without .value, Python called __str__() producing "Settings.BASE_DIR" instead of "advanced/base_dir". Qt6 stricter validation led to memory corruption.


Summary Statistics

  • Total Issues: 47
  • Qt Enum Reorganizations: 35 (74%)
  • API Renames: 3 (6%)
  • Type Strictness: 5 (11%)
  • Build/Config: 3 (6%)
  • Plugin Bugs: 1 (2%)

Key Takeaways

  1. Vast majority (74%) are Qt6 enum reorganizations following the same pattern
  2. Single solution handles most issues: The qt_compat.py compatibility layer
  3. Type strictness is new: Qt6/QGIS 4 no longer accept integers where enums are expected
  4. Systematic approach works: Find enum usage -> Add to QtCompat -> Replace usage -> Test

Testing Environment

  • QGIS Version: 3.99.0 Master (Qt6 build)
  • Build Source: Local build from QGIS master branch
  • OS: Fedora 43 Linux
  • Python: 3.10.x

Note: For detailed information about each issue including error messages, code examples, and specific file locations, see QGIS4_COMPATIBILITY_ISSUES.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions