Skip to content

Commit

Permalink
Merge pull request #24 from jmkerloch/feat-qdt-profile-export
Browse files Browse the repository at this point in the history
feat(qdt export): initialization of qdt export
  • Loading branch information
jmkerloch authored Oct 4, 2024
2 parents d064aa1 + e024193 commit 1fbad82
Show file tree
Hide file tree
Showing 13 changed files with 684 additions and 110 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Unreleased

## 0.5.0-beta1 - 2024-10-04

- add tab to export profile for QGIS Deployment Toolbelt
- add modern plugin's packaging using QGIS Plugin CI
- apply Python coding rules to whole codebase (PEP8)
- remove dead code
Expand Down
41 changes: 6 additions & 35 deletions profile_manager/gui/interface_handler.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from pathlib import Path

from qgis.core import Qgis, QgsApplication, QgsMessageLog
from qgis.PyQt.QtCore import Qt, QVariant
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QDialog, QListWidgetItem
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import QDialog

from profile_manager.datasources.dataservices.datasource_provider import (
DATA_SOURCE_SEARCH_LOCATIONS,
Expand Down Expand Up @@ -73,36 +72,8 @@ def populate_profile_listings(self):
Also updates button states according to resulting selections.
"""
profile_names = self.profile_manager.qgs_profile_manager.allProfiles()
active_profile_name = Path(QgsApplication.qgisSettingsDirPath()).name

self.dlg.comboBoxNamesSource.blockSignals(True)
self.dlg.comboBoxNamesTarget.blockSignals(True)
self.dlg.list_profiles.blockSignals(True)

self.dlg.comboBoxNamesSource.clear()
self.dlg.comboBoxNamesTarget.clear()
self.dlg.list_profiles.clear()
for i, name in enumerate(profile_names):
# Init source profiles combobox
self.dlg.comboBoxNamesSource.addItem(name)
if name == active_profile_name:
font = self.dlg.comboBoxNamesSource.font()
font.setItalic(True)
self.dlg.comboBoxNamesSource.setItemData(i, QVariant(font), Qt.FontRole)
# Init target profiles combobox
self.dlg.comboBoxNamesTarget.addItem(name)
if name == active_profile_name:
font = self.dlg.comboBoxNamesTarget.font()
font.setItalic(True)
self.dlg.comboBoxNamesTarget.setItemData(i, QVariant(font), Qt.FontRole)
# Add profiles to list view
list_item = QListWidgetItem(QIcon("../icon.png"), name)
if name == active_profile_name:
font = list_item.font()
font.setItalic(True)
list_item.setFont(font)
self.dlg.list_profiles.addItem(list_item)
active_profile_name = Path(QgsApplication.qgisSettingsDirPath()).name

self.dlg.comboBoxNamesSource.setCurrentText(active_profile_name)

Expand Down Expand Up @@ -150,7 +121,7 @@ def setup_connections(self):
self.dlg.comboBoxNamesTarget.currentIndexChanged.connect(
self.conditionally_enable_import_button
)
self.dlg.list_profiles.currentItemChanged.connect(
self.dlg.list_profiles.selectionModel().selectionChanged.connect(
self.conditionally_enable_profile_buttons
)

Expand Down Expand Up @@ -223,7 +194,7 @@ def conditionally_enable_profile_buttons(self):
Called when profile selection changes in the Profiles tab.
"""
# A profile must be selected
if self.dlg.list_profiles.currentItem() is None:
if self.dlg.get_list_selection_profile_name() is None:
self.dlg.removeProfileButton.setToolTip(
self.tr("Please choose a profile to remove")
)
Expand All @@ -238,7 +209,7 @@ def conditionally_enable_profile_buttons(self):
self.dlg.copyProfileButton.setEnabled(False)
# Some actions can/should not be done on the currently active profile
elif (
self.dlg.list_profiles.currentItem().text()
self.dlg.get_list_selection_profile_name()
== Path(QgsApplication.qgisSettingsDirPath()).name
):
self.dlg.removeProfileButton.setToolTip(
Expand Down
74 changes: 74 additions & 0 deletions profile_manager/gui/mdl_profiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from pathlib import Path

from qgis.core import QgsApplication, QgsUserProfile, QgsUserProfileManager
from qgis.PyQt.QtCore import QModelIndex, QObject, Qt
from qgis.PyQt.QtGui import QStandardItemModel

from profile_manager.profiles.utils import qgis_profiles_path


class ProfileListModel(QStandardItemModel):
"""QStandardItemModel to display available QGIS profile list"""

NAME_COL = 0

def __init__(self, parent: QObject = None):
"""
QStandardItemModel for profile list display
Args:
parent: QObject parent
"""
super().__init__(parent)
self.setHorizontalHeaderLabels([self.tr("Name")])

self.profile_manager = QgsUserProfileManager(str(qgis_profiles_path()))

# Connect to profile changes
self.profile_manager.setNewProfileNotificationEnabled(True)
self.profile_manager.profilesChanged.connect(self._update_available_profiles)

# Initialization of available profiles
self._update_available_profiles()

def flags(self, index: QModelIndex) -> Qt.ItemFlags:
"""Define flags for an index.
Used to disable edition.
Args:
index (QModelIndex): data index
Returns:
Qt.ItemFlags: flags
"""
default_flags = super().flags(index)
return default_flags & ~Qt.ItemIsEditable # Disable editing

def _update_available_profiles(self) -> None:
"""Update model with all available profiles in manager"""
self.removeRows(0, self.rowCount())
for profile_name in self.profile_manager.allProfiles():
self.insert_profile(profile_name)

def insert_profile(self, profile_name: str) -> None:
"""Insert profile in model
Args:
profile_name (str): profile name
"""
# Get user profile
profile: QgsUserProfile = self.profile_manager.profileForName(profile_name)
if profile:
row = self.rowCount()
self.insertRow(row)
self.setData(self.index(row, self.NAME_COL), profile.name())
self.setData(
self.index(row, self.NAME_COL), profile.icon(), Qt.DecorationRole
)

active_profile_folder_name = Path(QgsApplication.qgisSettingsDirPath()).name
profile_folder_name = Path(profile.folder()).name
if profile_folder_name == active_profile_folder_name:
font = QgsApplication.font()
font.setItalic(True)
self.setData(self.index(row, self.NAME_COL), font, Qt.FontRole)
70 changes: 8 additions & 62 deletions profile_manager/profile_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from os import path
from pathlib import Path
from shutil import copytree
from sys import platform

# PyQGIS
from qgis.core import Qgis, QgsMessageLog, QgsUserProfileManager
Expand All @@ -49,6 +48,7 @@
from profile_manager.gui.interface_handler import InterfaceHandler
from profile_manager.profile_manager_dialog import ProfileManagerDialog
from profile_manager.profiles.profile_action_handler import ProfileActionHandler
from profile_manager.profiles.utils import get_profile_qgis_ini_path, qgis_profiles_path
from profile_manager.utils import adjust_to_operating_system, wait_cursor


Expand Down Expand Up @@ -251,38 +251,7 @@ def run(self):

def set_paths(self):
"""Sets various OS and profile dependent paths"""
home_path = Path.home()
if platform.startswith("win32"):
self.qgis_profiles_path = (
f"{home_path}/AppData/Roaming/QGIS/QGIS3/profiles".replace("\\", "/")
)
self.ini_path = (
self.qgis_profiles_path
+ "/"
+ self.dlg.comboBoxNamesSource.currentText()
+ "/QGIS/QGIS3.ini"
)
self.operating_system = "windows"
elif platform == "darwin":
self.qgis_profiles_path = (
f"{home_path}/Library/Application Support/QGIS/QGIS3/profiles"
)
self.ini_path = (
self.qgis_profiles_path
+ "/"
+ self.dlg.comboBoxNamesSource.currentText()
+ "/qgis.org/QGIS3.ini"
)
self.operating_system = "mac"
else:
self.qgis_profiles_path = f"{home_path}/.local/share/QGIS/QGIS3/profiles"
self.ini_path = (
self.qgis_profiles_path
+ "/"
+ self.dlg.comboBoxNamesSource.currentText()
+ "/QGIS/QGIS3.ini"
)
self.operating_system = "unix"
self.qgis_profiles_path = str(qgis_profiles_path())

self.backup_path = adjust_to_operating_system(
str(Path.home()) + "/QGIS Profile Manager Backup/"
Expand Down Expand Up @@ -487,36 +456,13 @@ def get_profile_paths(self) -> tuple[str, str]:

def get_ini_paths(self):
"""Gets path to current chosen source and target qgis.ini file"""
if self.operating_system == "mac":
ini_path_source = adjust_to_operating_system(
self.qgis_profiles_path
+ "/"
+ self.dlg.comboBoxNamesSource.currentText()
+ "/qgis.org/QGIS3.ini"
)
ini_path_target = adjust_to_operating_system(
self.qgis_profiles_path
+ "/"
+ self.dlg.comboBoxNamesTarget.currentText()
+ "/qgis.org/QGIS3.ini"
)
else:
ini_path_source = adjust_to_operating_system(
self.qgis_profiles_path
+ "/"
+ self.dlg.comboBoxNamesSource.currentText()
+ "/QGIS/QGIS3.ini"
)
ini_path_target = adjust_to_operating_system(
self.qgis_profiles_path
+ "/"
+ self.dlg.comboBoxNamesTarget.currentText()
+ "/QGIS/QGIS3.ini"
)

ini_paths = {
"source": ini_path_source,
"target": ini_path_target,
"source": str(
get_profile_qgis_ini_path(self.dlg.comboBoxNamesSource.currentText())
),
"target": str(
get_profile_qgis_ini_path(self.dlg.comboBoxNamesTarget.currentText())
),
}

return ini_paths
Expand Down
94 changes: 94 additions & 0 deletions profile_manager/profile_manager_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,22 @@
***************************************************************************/
"""

# standard
import os
from pathlib import Path
from typing import Optional

# pyQGIS
from qgis.PyQt import QtWidgets, uic
from qgis.PyQt.QtWidgets import QMessageBox

# plugin
from profile_manager.gui.mdl_profiles import ProfileListModel
from profile_manager.qdt_export.profile_export import (
QDTProfileInfos,
export_profile_for_qdt,
get_qdt_profile_infos_from_file,
)

# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
Expand All @@ -41,3 +54,84 @@ def __init__(self, parent=None):
# http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
# #widgets-and-dialogs-with-auto-connect
self.setupUi(self)

self.profile_mdl = ProfileListModel(self)
self.qdt_export_profile_cbx.setModel(self.profile_mdl)
self.export_qdt_button.clicked.connect(self.export_qdt_handler)
self.export_qdt_button.setEnabled(False)
self.qdt_file_widget.fileChanged.connect(self._qdt_export_dir_changed)

self.comboBoxNamesSource.setModel(self.profile_mdl)
self.comboBoxNamesTarget.setModel(self.profile_mdl)
self.list_profiles.setModel(self.profile_mdl)

def get_list_selection_profile_name(self) -> Optional[str]:
"""Get selected profile name from list
Returns:
Optional[str]: selected profile name, None if no profile selected
"""
index = self.list_profiles.selectionModel().currentIndex()
if index.isValid():
return self.list_profiles.model().data(index, ProfileListModel.NAME_COL)
return None

def _qdt_export_dir_changed(self) -> None:
"""Update UI when QDT export dir is changed:
- enabled/disable button
- define QDTProfileInformations if profile.json file is available
"""
export_dir = self.qdt_file_widget.filePath()
if export_dir:
self.export_qdt_button.setEnabled(True)
profile_json = Path(export_dir) / "profile.json"
if profile_json.exists():
self._set_qdt_profile_infos(
get_qdt_profile_infos_from_file(profile_json)
)
else:
self.export_qdt_button.setEnabled(False)

def _get_qdt_profile_infos(self) -> QDTProfileInfos:
"""Get QDTProfileInfos from UI
Returns:
QDTProfileInfos: QDT Profile Information
"""
return QDTProfileInfos(
description=self.qdt_description_edit.toPlainText(),
email=self.qdt_email_edit.text(),
version=self.qdt_version_edit.text(),
qgis_min_version=self.qdt_qgis_min_version_edit.text(),
qgis_max_version=self.qdt_qgis_max_version_edit.text(),
)

def _set_qdt_profile_infos(self, qdt_profile_infos: QDTProfileInfos) -> None:
"""Set QDTProfileInfos in UI
Args:
qdt_profile_infos (QDTProfileInfos): QDT Profile Information
"""
self.qdt_description_edit.setPlainText(qdt_profile_infos.description)
self.qdt_email_edit.setText(qdt_profile_infos.email)
self.qdt_version_edit.setText(qdt_profile_infos.version)
self.qdt_qgis_min_version_edit.setText(qdt_profile_infos.qgis_min_version)
self.qdt_qgis_max_version_edit.setText(qdt_profile_infos.qgis_max_version)

def export_qdt_handler(self) -> None:
"""Export selected profile as QDT profile"""
profile_path = self.qdt_file_widget.filePath()
if profile_path:
source_profile_name = self.qdt_export_profile_cbx.currentText()
export_profile_for_qdt(
profile_name=source_profile_name,
export_path=Path(profile_path),
qdt_profile_infos=self._get_qdt_profile_infos(),
clear_export_path=self.qdt_clear_export_folder_checkbox.isChecked(),
export_inactive_plugin=self.qdt_inactive_plugin_export_checkbox.isChecked(),
)
QMessageBox.information(
self,
self.tr("QDT profile export"),
self.tr("QDT profile have been successfully exported."),
)
Loading

0 comments on commit 1fbad82

Please sign in to comment.