Skip to content

Commit

Permalink
Merge pull request #97 from qgis/hackfest-2024
Browse files Browse the repository at this point in the history
New resources type support and bug fixing
  • Loading branch information
ismailsunni authored Sep 13, 2024
2 parents 4830f5b + 00b2e76 commit 1dd8b24
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 258 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ Unreleased
-->

## 0.2.0 - 2024-09-13

- Limit the name to be maximum 50 character
- Add filtering for 3D model and QGIS Layer Definition
- Add "add to QGIS" for layer definition
- Fix bug when resource give 404

## 0.1.2 - 2023-11-01

- Fix bug #85: failed to handle null thumbnail
Expand Down
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Features:
- Style will be added to your style manager
- Model will be added to your profile model directory and shown directly on your QGIS
- Geopackage will be downloaded on your disk and loaded directly
- 3D model has no option to add to QGIS directly (you can still download it and use it from 3D style panel)
- Layer Definition will be loaded directly

10. Download the resource on your disk (without adding to QGIS)
11. Open this help page
Expand Down
7 changes: 4 additions & 3 deletions qgis_hub_plugin/core/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@


def get_all_resources(force_update=False):
print("get_all_resources", force_update)
# Check if the response file exits
response_folder = Path(QgsApplication.qgisSettingsDirPath(), "qgis_hub")
response_folder.mkdir(parents=True, exist_ok=True)
Expand All @@ -18,10 +19,10 @@ def get_all_resources(force_update=False):
return json.load(f)

# TODO: download in the background
# hardcoded to get all resource, currently only ~160
# hardcoded to get all resource, currently only ~227
url = f"{BASE_URL}?limit=1000&format=json"

if download_file(url=url, file_path=response_file, force=force_update):
status = download_file(url=url, destination=response_file, force=force_update)
if status and response_file.exists():
with open(response_file) as f:
return json.load(f)
else:
Expand Down
2 changes: 1 addition & 1 deletion qgis_hub_plugin/core/custom_filter_proxy.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from qgis.PyQt.QtCore import QSortFilterProxyModel, Qt
from qgis.PyQt.QtCore import QSortFilterProxyModel

from qgis_hub_plugin.gui.constants import ResourceTypeRole, SortingRole
from qgis_hub_plugin.toolbelt import PlgLogger
Expand Down
3 changes: 3 additions & 0 deletions qgis_hub_plugin/gui/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
SortingRole = Qt.UserRole + 4


# Type of resources, based on the QGIS Hub API
class ResoureType:
Model = "Model"
Style = "Style"
Geopackage = "Geopackage"
Model3D = "3DModel"
LayerDefinition = "LayerDefinition"
120 changes: 78 additions & 42 deletions qgis_hub_plugin/gui/resource_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
from functools import partial
from pathlib import Path

from qgis.core import Qgis, QgsApplication, QgsProject, QgsStyle, QgsVectorLayer
from qgis.core import (
Qgis,
QgsApplication,
QgsLayerDefinition,
QgsProject,
QgsStyle,
QgsVectorLayer,
)
from qgis.gui import QgsMessageBar
from qgis.PyQt import uic
from qgis.PyQt.QtCore import (
Expand Down Expand Up @@ -38,7 +45,12 @@
)
from qgis_hub_plugin.gui.resource_item import AttributeSortingItem, ResourceItem
from qgis_hub_plugin.toolbelt import PlgLogger, PlgOptionsManager
from qgis_hub_plugin.utilities.common import download_file, download_resource_thumbnail
from qgis_hub_plugin.utilities.common import (
QGIS_HUB_DIR,
download_file,
download_resource_thumbnail,
)
from qgis_hub_plugin.utilities.exception import DownloadError
from qgis_hub_plugin.utilities.qgis_util import show_busy_cursor

UI_CLASS = uic.loadUiType(
Expand Down Expand Up @@ -130,9 +142,12 @@ def __init__(self, parent=None, iface=None):
)
self.buttonBox.rejected.connect(self.store_setting)

# TODO(IS): Make it dynamic
self.checkBoxGeopackage.stateChanged.connect(self.update_resource_filter)
self.checkBoxStyle.stateChanged.connect(self.update_resource_filter)
self.checkBoxModel.stateChanged.connect(self.update_resource_filter)
self.checkBoxModel3D.stateChanged.connect(self.update_resource_filter)
self.checkBoxLayerDefinition.stateChanged.connect(self.update_resource_filter)

# Match with the size of the thumbnail
self.iconSizeSlider.setMinimum(20)
Expand Down Expand Up @@ -205,6 +220,7 @@ def restore_setting(self):

@show_busy_cursor
def populate_resources(self, force_update=False):
self.log(f"Populating resources {force_update}")
if force_update or not self.resources:
response = get_all_resources(force_update=force_update)

Expand Down Expand Up @@ -238,11 +254,15 @@ def update_checkbox_states(self):
geopackage_checked = self.checkBoxGeopackage.isChecked()
style_checked = self.checkBoxStyle.isChecked()
model_checked = self.checkBoxModel.isChecked()
model3d_checked = self.checkBoxModel3D.isChecked()
layer_definition_checked = self.checkBoxLayerDefinition.isChecked()

self.checkbox_states = {
ResoureType.Geopackage: geopackage_checked,
ResoureType.Style: style_checked,
ResoureType.Model: model_checked,
ResoureType.Model3D: model3d_checked,
ResoureType.LayerDefinition: layer_definition_checked,
}

def update_resource_filter(self):
Expand Down Expand Up @@ -307,6 +327,11 @@ def update_custom_button(self):
self.addQGISPushButton.setToolTip(
self.tr("Download and load the layers to QGIS")
)
elif self.selected_resource.resource_type == ResoureType.LayerDefinition:
self.addQGISPushButton.setText(self.tr("Add Layer to QGIS"))
self.addQGISPushButton.setToolTip(
self.tr("Load the layer definition to QGIS")
)
else:
self.addQGISPushButton.setVisible(False)

Expand Down Expand Up @@ -371,11 +396,9 @@ def download_resource(self):
if not file_path.endswith(file_extension):
file_path = file_path + file_extension

if not download_resource_file(resource.file, file_path):
self.show_warning_message(f"Download failed for {resource.name}")
else:
try:
download_file(resource.file, Path(file_path))
self.show_success_message(f"Downloaded {resource.name} to {file_path}")

if self.checkBoxOpenDirectory.isChecked():
QDesktopServices.openUrl(
QUrl.fromLocalFile(str(Path(file_path).parent))
Expand All @@ -384,14 +407,21 @@ def download_resource(self):
self.plg_settings.set_value_from_key(
"download_location", str(Path(file_path).parent)
)
except DownloadError as e:
self.show_error_message(str(e))

def add_resource_to_qgis(self):
if self.selected_resource.resource_type == ResoureType.Model:
self.add_model_to_qgis()
elif self.selected_resource.resource_type == ResoureType.Style:
self.add_style_to_qgis()
elif self.selected_resource.resource_type == ResoureType.Geopackage:
self.add_geopackage_to_qgis()
try:
if self.selected_resource.resource_type == ResoureType.Model:
self.add_model_to_qgis()
elif self.selected_resource.resource_type == ResoureType.Style:
self.add_style_to_qgis()
elif self.selected_resource.resource_type == ResoureType.Geopackage:
self.add_geopackage_to_qgis()
elif self.selected_resource.resource_type == ResoureType.LayerDefinition:
self.add_layer_definition_to_qgis()
except DownloadError as e:
self.show_error_message(str(e))

@show_busy_cursor
def add_model_to_qgis(self):
Expand All @@ -403,23 +433,12 @@ def add_model_to_qgis(self):
if not custom_model_directory.exists():
custom_model_directory.mkdir(parents=True, exist_ok=True)

file_path = os.path.join(
custom_model_directory, os.path.basename(resource.file)
)
file_path = custom_model_directory / os.path.basename(resource.file)

if download_resource_file(resource.file, file_path):
# Refreshing the processing toolbox
QgsApplication.processingRegistry().providerById(
"model"
).refreshAlgorithms()
self.show_success_message(
self.tr(f"Model {resource.name} is added to QGIS")
)

else:
self.show_warning_message(
self.tr(f"Download failed for model {resource.name}")
)
download_file(resource.file, file_path)
# Refreshing the processing toolbox
QgsApplication.processingRegistry().providerById("model").refreshAlgorithms()
self.show_success_message(self.tr(f"Model {resource.name} is added to QGIS"))

@show_busy_cursor
def add_geopackage_to_qgis(self):
Expand All @@ -442,9 +461,7 @@ def add_geopackage_to_qgis(self):
if not file_path.endswith(file_extension):
file_path = file_path + file_extension

if not download_resource_file(resource.file, file_path):
self.show_error_message(self.tr(f"Download failed for {resource.name}"))
return
download_file(resource.file, file_path)

extract_location = Path(os.path.dirname(file_path))
current_project = QgsProject.instance()
Expand Down Expand Up @@ -526,7 +543,7 @@ def add_style_to_qgis(self):
"/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
)
tempfile_path = Path(tempdir, resource.file.split("/")[-1])
download_resource_file(resource.file, tempfile_path, True)
download_file(resource.file, tempfile_path, True)

# Add to QGIS style library
style = QgsStyle().defaultStyle()
Expand All @@ -540,6 +557,35 @@ def add_style_to_qgis(self):
self.tr(f"Style {resource.name} is not added to QGIS")
)

@show_busy_cursor
def add_layer_definition_to_qgis(self):
resource = self.selected_resource

layer_definition_dir = QGIS_HUB_DIR / "layer_definitions"
file_path = layer_definition_dir / f"{resource.name}.qlr"

if not layer_definition_dir.exists():
layer_definition_dir.mkdir(parents=True, exist_ok=True)

download_file(resource.file, file_path)

current_project = QgsProject.instance()

self.load_layer_definition(current_project, resource.name, file_path)

def load_layer_definition(self, project, layer_name, layer_definition_file_path):
success, message = QgsLayerDefinition.loadLayerDefinition(
str(layer_definition_file_path), project, project.layerTreeRoot()
)
if success:
self.show_success_message(
self.tr(f"Successfully load layer definition: {layer_name}")
)
else:
self.show_error_message(
self.tr(f"Failed to load layer definition: {layer_name}: {message}")
)

def update_title_bar(self):
num_total_resources = len(self.resources)
num_selected_resources = self.proxy_model.rowCount()
Expand Down Expand Up @@ -579,13 +625,3 @@ def resize_columns(self):

def update_icon_size(self, size):
self.listViewResources.setIconSize(QSize(size, size))


# TODO: do it QGIS task to have
def download_resource_file(url: str, file_path: str, force: bool = False):
resource_path = Path(file_path)
download_file(url, resource_path, force)
if resource_path.exists():
return resource_path
else:
return None
20 changes: 20 additions & 0 deletions qgis_hub_plugin/gui/resource_browser.ui
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,26 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxModel3D">
<property name="text">
<string>3D Model</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxLayerDefinition">
<property name="text">
<string>QGIS Layer</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
Expand Down
2 changes: 1 addition & 1 deletion qgis_hub_plugin/gui/resource_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __init__(self, params: dict):
self.thumbnail = params.get("thumbnail")

# Custom attribute
self.setText(self.name)
self.setText(self.name[:50] + "..." if len(self.name) > 50 else self.name)
self.setToolTip(f"{self.name} by {self.creator}")
thumbnail_path = download_resource_thumbnail(self.thumbnail, self.uuid)
if thumbnail_path:
Expand Down
Loading

0 comments on commit 1dd8b24

Please sign in to comment.