Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(qdt export): initialization of qdt export #24

Merged
merged 17 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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