Skip to content

Commit

Permalink
make plugin compatibile with Qt6
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbruy committed Jan 13, 2025
1 parent 2957994 commit a06bf1f
Show file tree
Hide file tree
Showing 16 changed files with 146 additions and 111 deletions.
6 changes: 3 additions & 3 deletions Mergin/attachment_fields_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@


class AttachmentFieldsModel(QStandardItemModel):
LAYER_ID = Qt.UserRole + 1
FIELD_NAME = Qt.UserRole + 2
EXPRESSION = Qt.UserRole + 3
LAYER_ID = Qt.ItemDataRole.UserRole + 1
FIELD_NAME = Qt.ItemDataRole.UserRole + 2
EXPRESSION = Qt.ItemDataRole.UserRole + 3

def __init__(self, parent=None):
super().__init__(parent)
Expand Down
2 changes: 1 addition & 1 deletion Mergin/clone_project_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self, user_info, default_workspace=None):
self.invalid = False

def validate_input(self):
is_writable = bool(self.ui.projectNamespace.currentData(Qt.UserRole))
is_writable = bool(self.ui.projectNamespace.currentData(Qt.ItemDataRole.UserRole))
if not is_writable:
msg = "You do not have permissions to create a project in this workspace!"
self.ui.edit_project_name.setEnabled(False)
Expand Down
10 changes: 5 additions & 5 deletions Mergin/collapsible_message_box.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import QMessageBox
from qgis.PyQt import QtWidgets


class CollapsibleBox(QtWidgets.QWidget):
def __init__(self, text, details, title="Mergin Maps error", parent=None):
msg = QMessageBox()
msg.setWindowTitle(title)
msg.setTextFormat(Qt.RichText)
msg.setTextFormat(Qt.TextFormat.RichText)
msg.setText(text)
msg.setIcon(QMessageBox.Icon.Warning)
msg.setStandardButtons(QMessageBox.Close)
msg.setDefaultButton(QMessageBox.Close)
msg.setStandardButtons(QMessageBox.StandardButton.Close)
msg.setDefaultButton(QMessageBox.StandardButton.Close)
msg.setDetailedText(details)
msg.exec()
2 changes: 1 addition & 1 deletion Mergin/configuration_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def writeSettings(self):
return mc

def test_connection(self):
QApplication.setOverrideCursor(Qt.WaitCursor)
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
ok, msg = test_server_connection(self.server_url(), self.ui.username.text(), self.ui.password.text())
QApplication.restoreOverrideCursor()
self.ui.test_status.setText(msg)
Expand Down
38 changes: 20 additions & 18 deletions Mergin/create_project_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
QWizard,
)

from qgis.core import QgsProject, QgsLayerTreeNode, QgsLayerTreeModel
from qgis.core import QgsProject, QgsLayerTreeNode, QgsLayerTreeModel, NULL
from qgis.utils import iface

from .utils import (
Expand Down Expand Up @@ -170,7 +170,7 @@ def set_info(self, info=None):
def check_input(self):
"""Check if entered path is not already a Mergin Maps project dir and has at most a single QGIS project file."""
# TODO: check if the project exists on the server
if not self.project_owner_cbo.currentData(Qt.UserRole):
if not self.project_owner_cbo.currentData(Qt.ItemDataRole.UserRole):
self.create_warning("You do not have permissions to create a project in this workspace!")
return
proj_name = self.project_name_ledit.text().strip()
Expand Down Expand Up @@ -261,8 +261,8 @@ def columnCount(self, parent):
return 4

def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal:
if role == Qt.DisplayRole:
if orientation == Qt.AlignmentFlag.Horizontal:
if role == Qt.ItemDataRole.DisplayRole:
if section == self.LAYER_COL:
return "Layer"
elif section == self.PACK_COL:
Expand All @@ -281,8 +281,10 @@ def index(self, row, column, parent):
return idx

def toggle_item(self, idx):
is_checked = self.data(idx, Qt.CheckStateRole) == Qt.Checked
self.setData(idx, Qt.Unchecked if is_checked else Qt.Checked, Qt.CheckStateRole)
is_checked = self.data(idx, Qt.ItemDataRole.CheckStateRole) == Qt.CheckState.Checked
self.setData(
idx, Qt.CheckState.Unchecked if is_checked else Qt.CheckState.Checked, Qt.ItemDataRole.CheckStateRole
)

def map_layer(self, idx):
if idx.column() == self.LAYER_COL:
Expand All @@ -307,17 +309,17 @@ def data(self, idx, role):
return self.layer_tree_model.data(self.mapToSource(idx), role)
layer = self.map_layer(idx)
if not layer:
return QVariant()
if role == Qt.CheckStateRole or role == Qt.UserRole:
return NULL
if role == Qt.ItemDataRole.CheckStateRole or role == Qt.ItemDataRole.UserRole:
state = self.layers_state[layer.id()]
if idx.column() == state:
return Qt.Checked
return Qt.CheckState.Checked
else:
return Qt.Unchecked
return QVariant()
return Qt.CheckState.Unchecked
return NULL

def setData(self, index, value, role):
if role == Qt.CheckStateRole:
if role == Qt.ItemDataRole.CheckStateRole:
layer = self.map_layer(index)
if not layer:
return False
Expand Down Expand Up @@ -357,14 +359,14 @@ def node_shown(self, node):

def flags(self, idx):
if idx.column() == self.LAYER_COL:
return Qt.ItemIsEnabled
return Qt.ItemFlags.ItemIsEnabled
layer = self.map_layer(idx)
if not layer:
return Qt.NoItemFlags
return Qt.ItemFlags.NoItemFlags
else:
enabled_flags = Qt.ItemIsEnabled | Qt.ItemIsEditable | Qt.ItemIsUserCheckable
enabled_flags = Qt.ItemFlags.ItemIsEnabled | Qt.ItemFlags.ItemIsEditable | Qt.ItemFlags.ItemIsUserCheckable
if idx.column() == self.LAYER_COL:
return Qt.ItemIsEnabled
return Qt.ItemFlags.ItemIsEnabled
elif idx.column() == self.PACK_COL:
return enabled_flags if layer.id() in self.packable else Qt.ItemFlags()
elif idx.column() in (self.KEEP_COL, self.IGNORE_COL):
Expand All @@ -386,7 +388,7 @@ def __init__(self, parent=None):
self.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)

self.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
self.setEditTriggers(QTreeView.NoEditTriggers)
self.setEditTriggers(QAbstractItemView.EditTriggers.NoEditTriggers)

self.clicked.connect(self.model().toggle_item)

Expand Down Expand Up @@ -463,7 +465,7 @@ def accept(self):
reload_project = False
failed_packaging = []

QApplication.setOverrideCursor(Qt.WaitCursor)
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
QApplication.processEvents()

if not self.init_page.cur_proj_no_pack_btn.isChecked():
Expand Down
4 changes: 2 additions & 2 deletions Mergin/diff_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.ui = uic.loadUi(ui_file, self)

with OverrideCursor(Qt.WaitCursor):
with OverrideCursor(Qt.CursorShape.WaitCursor):
QgsGui.instance().enableAutoGeometryRestore(self)
settings = QSettings()
state = settings.value("Mergin/changesViewerSplitterSize")
Expand Down Expand Up @@ -80,7 +80,7 @@ def __init__(self, parent=None):
self.toolbar.setIconSize(iface.iconSize())

self.map_canvas.enableAntiAliasing(True)
self.map_canvas.setSelectionColor(QColor(Qt.cyan))
self.map_canvas.setSelectionColor(QColor(Qt.GlobalColor.cyan))
self.pan_tool = QgsMapToolPan(self.map_canvas)
self.map_canvas.setMapTool(self.pan_tool)

Expand Down
2 changes: 2 additions & 0 deletions Mergin/metadata.txt
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,5 @@ experimental=False

; deprecated flag (applies to the whole plugin and not only to the uploaded version)
deprecated=False

supportsQt6=yes
46 changes: 30 additions & 16 deletions Mergin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
# Copyright Lutra Consulting Limited

from math import floor
import sip

try:
import sip
except ImportError:
from PyQt6 import sip
import os
import shutil
from pathlib import Path
Expand Down Expand Up @@ -284,8 +288,8 @@ def show_browser_panel(self):
q += "Would you like to open it and see your Mergin projects?"
if not browser.isVisible():
res = QMessageBox.question(None, "Mergin Maps - QGIS Browser Panel", q)
if res == QMessageBox.Yes:
self.iface.addDockWidget(Qt.LeftDockWidgetArea, browser)
if res == QMessageBox.StandardButton.Yes:
self.iface.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, browser)

def configure(self):
"""Open plugin configuration dialog."""
Expand Down Expand Up @@ -327,7 +331,9 @@ def show_no_workspaces_dialog(self):
"Click on the button below to create one. \n\n"
"A minimum of one workspace is required to use Mergin Maps."
)
msg_box = QMessageBox(QMessageBox.Icon.Critical, "You do not have any workspace", msg, QMessageBox.Close)
msg_box = QMessageBox(
QMessageBox.Icon.Critical, "You do not have any workspace", msg, QMessageBox.StandardButton.Close
)
create_button = msg_box.addButton("Create workspace", msg_box.ActionRole)
create_button.clicked.disconnect()
create_button.clicked.connect(partial(self.open_configured_url, "/workspaces"))
Expand Down Expand Up @@ -615,13 +621,13 @@ def clone_remote_project(self):
self.mc.clone_project(self.project_name, dlg.project_name, dlg.project_namespace)
except (URLError, ClientError) as e:
msg = "Failed to clone project {}:\n\n{}".format(self.project_name, str(e))
QMessageBox.critical(None, "Clone project", msg, QMessageBox.Close)
QMessageBox.critical(None, "Clone project", msg, QMessageBox.StandardButton.Close)
return
except LoginError as e:
login_error_message(e)
return
msg = "Mergin Maps project cloned successfully."
QMessageBox.information(None, "Clone project", msg, QMessageBox.Close)
QMessageBox.information(None, "Clone project", msg, QMessageBox.StandardButton.Close)
self.parent().reload()
# we also need to reload My projects group as the cloned project could appear there
group_items = self.project_manager.get_mergin_browser_groups()
Expand All @@ -630,17 +636,17 @@ def clone_remote_project(self):

def remove_remote_project(self):
dlg = RemoveProjectDialog(self.project_name)
if dlg.exec() == QDialog.Rejected:
if dlg.exec() == QDialog.DialogCode.Rejected:
return

try:
self.mc.delete_project(self.project_name)
msg = "Mergin Maps project removed successfully."
QMessageBox.information(None, "Remove project", msg, QMessageBox.Close)
QMessageBox.information(None, "Remove project", msg, QMessageBox.StandardButton.Close)
self.parent().reload()
except (URLError, ClientError) as e:
msg = "Failed to remove project {}:\n\n{}".format(self.project_name, str(e))
QMessageBox.critical(None, "Remove project", msg, QMessageBox.Close)
QMessageBox.critical(None, "Remove project", msg, QMessageBox.StandardButton.Close)
except LoginError as e:
login_error_message(e)

Expand Down Expand Up @@ -697,9 +703,13 @@ def remove_local_project(self):
"Do you want to proceed?".format(self.project_name)
)
btn_reply = QMessageBox.question(
None, "Remove local project", msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes
None,
"Remove local project",
msg,
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.Yes,
)
if btn_reply == QMessageBox.No:
if btn_reply == QMessageBox.StandardButton.No:
return

if os.path.exists(self.path):
Expand All @@ -710,9 +720,13 @@ def remove_local_project(self):
"Proceed anyway?".format(self.project_name)
)
btn_reply = QMessageBox.question(
None, "Remove local project", msg, QMessageBox.No | QMessageBox.No, QMessageBox.Yes
None,
"Remove local project",
msg,
QMessageBox.StandardButton.No | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.Yes,
)
if btn_reply == QMessageBox.No:
if btn_reply == QMessageBox.StandardButton.No:
return

cur_proj.clear()
Expand All @@ -739,7 +753,7 @@ def remove_local_project(self):
f"Failed to delete your project {self.project_name} because it is open.\n"
"You might need to close project or QGIS to remove its files."
)
QMessageBox.critical(None, "Project delete", msg, QMessageBox.Close)
QMessageBox.critical(None, "Project delete", msg, QMessageBox.StandardButton.Close)
return

settings = QSettings()
Expand All @@ -761,11 +775,11 @@ def clone_remote_project(self):
try:
self.mc.clone_project(self.project_name, dlg.project_name, dlg.project_namespace)
msg = "Mergin Maps project cloned successfully."
QMessageBox.information(None, "Clone project", msg, QMessageBox.Close)
QMessageBox.information(None, "Clone project", msg, QMessageBox.StandardButton.Close)
self.parent().reload()
except (URLError, ClientError) as e:
msg = "Failed to clone project {}:\n\n{}".format(self.project_name, str(e))
QMessageBox.critical(None, "Clone project", msg, QMessageBox.Close)
QMessageBox.critical(None, "Clone project", msg, QMessageBox.StandardButton.Close)
except LoginError as e:
login_error_message(e)

Expand Down
2 changes: 1 addition & 1 deletion Mergin/processing/algs/download_vector_tiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def processAlgorithm(self, parameters, context, feedback):

req = QgsBlockingNetworkRequest()
res = req.get(nr, False, feedback)
if res == QgsBlockingNetworkRequest.NoError:
if res == QgsBlockingNetworkRequest.ErrorCode.NoError:
data = req.reply().content()

comp_obj = zlib.compressobj(
Expand Down
26 changes: 13 additions & 13 deletions Mergin/project_selection_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ class SyncStatus(Enum):


class ProjectsModel(QStandardItemModel):
PROJECT = Qt.UserRole + 1
NAME = Qt.UserRole + 2
NAMESPACE = Qt.UserRole + 3
NAME_WITH_NAMESPACE = Qt.UserRole + 4
STATUS = Qt.UserRole + 5
LOCAL_DIRECTORY = Qt.UserRole + 6
ICON = Qt.UserRole + 7
PROJECT = Qt.ItemDataRole.UserRole + 1
NAME = Qt.ItemDataRole.UserRole + 2
NAMESPACE = Qt.ItemDataRole.UserRole + 3
NAME_WITH_NAMESPACE = Qt.ItemDataRole.UserRole + 4
STATUS = Qt.ItemDataRole.UserRole + 5
LOCAL_DIRECTORY = Qt.ItemDataRole.UserRole + 6
ICON = Qt.ItemDataRole.UserRole + 7

def __init__(self, projects=None):
super(ProjectsModel, self).__init__()
Expand Down Expand Up @@ -77,7 +77,7 @@ def createItems(projects):
icon = "refresh.svg"

name_with_namespace = f"{project['namespace']}/{project['name']}"
item.setData(name_with_namespace, Qt.DisplayRole)
item.setData(name_with_namespace, Qt.ItemDataRole.DisplayRole)
item.setData(name_with_namespace, ProjectsModel.NAME_WITH_NAMESPACE)
item.setData(project, ProjectsModel.PROJECT)
item.setData(project["name"], ProjectsModel.NAME)
Expand Down Expand Up @@ -144,20 +144,20 @@ def paint(self, painter, option, index):
iconRect = iconRect.marginsRemoved(QMargins(12, 12, 12, 12))

painter.save()
if option.state & QStyle.State_Selected:
if option.state & QStyle.StateFlag.State_Selected:
painter.fillRect(borderRect, option.palette.highlight())
painter.drawRect(borderRect)
painter.setFont(nameFont)
if self.show_namespace:
text = index.data(ProjectsModel.NAME_WITH_NAMESPACE)
else:
text = index.data(ProjectsModel.NAME)
elided_text = fm.elidedText(text, Qt.ElideRight, nameRect.width())
painter.drawText(nameRect, Qt.AlignLeading, elided_text)
elided_text = fm.elidedText(text, Qt.TextElideMode.ElideRight, nameRect.width())
painter.drawText(nameRect, Qt.AlignmentFlag.AlignLeading, elided_text)
painter.setFont(option.font)
fm = QFontMetrics(QFont(option.font))
elided_status = fm.elidedText(index.data(ProjectsModel.STATUS), Qt.ElideRight, infoRect.width())
painter.drawText(infoRect, Qt.AlignLeading, elided_status)
elided_status = fm.elidedText(index.data(ProjectsModel.STATUS), Qt.TextElideModeElideRight, infoRect.width())
painter.drawText(infoRect, Qt.AlignmentFlag.AlignLeading, elided_status)
icon = index.data(ProjectsModel.ICON)
if icon:
icon = QIcon(icon_path(icon))
Expand Down
4 changes: 2 additions & 2 deletions Mergin/project_settings_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,11 @@ def check_project(self, state):
"to place required files.",
)
self.chk_tracking_enabled.blockSignals(True)
self.chk_tracking_enabled.setCheckState(Qt.Unchecked)
self.chk_tracking_enabled.setCheckState(Qt.CheckState.Unchecked)
self.chk_tracking_enabled.blockSignals(False)

def setup_tracking(self):
if self.chk_tracking_enabled.checkState() == Qt.Unchecked:
if self.chk_tracking_enabled.checkState() == Qt.CheckState.Unchecked:
return

# check if tracking layer already exists
Expand Down
Loading

0 comments on commit a06bf1f

Please sign in to comment.