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

Version 1.0.6 #543

Merged
merged 4 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions CHANGELIST.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# QRiS Plugin

## [1.0.6] 2024 DEC 06

### Added
- Specify a default export folder in settings #537

### Fixed
- Bug with missing DCE Layer Features in project export #539
- Update Project path on browse to path button on New Project Form #538
- Bug with Ignore Fields radio button on Import Features Form #542


## [1.0.5] 2024 NOV 15

### Added
Expand Down
2 changes: 1 addition & 1 deletion __version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.0.5"
__version__ = "1.0.6"
73 changes: 56 additions & 17 deletions src/view/frm_export_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox
from qgis.core import QgsVectorLayer, QgsMessageLog
from qgis.utils import iface
from qgis.PyQt.QtCore import QSettings

from rsxml.project_xml import Project, MetaData, Meta, ProjectBounds, Coords, BoundingBox, Realization, Geopackage, GeopackageLayer, GeoPackageDatasetTypes, Dataset

Expand All @@ -29,8 +31,12 @@

from .utilities import add_standard_form_buttons, message_box

# Puting these here for now to avoid circular imports - source: qris_toolbar.py
ORGANIZATION = 'Riverscapes'
APPNAME = 'QRiS'

PROJECT_MACHINE_NAME = 'RiverscapesStudio'
DEFAULT_EXPORT_PATH = 'default_export_path'

class FrmExportProject(QtWidgets.QDialog):

Expand All @@ -47,7 +53,11 @@ def __init__(self, parent, project: QRiSProject, outpath: str = None):
self.setupUi()

if outpath is not None:
self.set_output_path(outpath)
self.base_folder = outpath
else:
settings = QSettings(ORGANIZATION, APPNAME)
self.base_folder = settings.value(DEFAULT_EXPORT_PATH, '').replace("/", "\\")
self.set_output_path()

# populate the AOI combo box with aoi names
for aoi_id, aoi in self.qris_project.masks.items():
Expand Down Expand Up @@ -231,13 +241,34 @@ def update_check_state(self, item: QtGui.QStandardItem):
self.export_layers_model.itemChanged.connect(self.handle_item_changed)
self.update_check_state(item.parent())

def set_output_path(self, outpath: str):
def set_output_path(self):

if self.base_folder == "":
return

name = ""

# outpath = parse_posix_path(os.path.join(self.basepath, project_name.replace(" ", "_")))
if self.chk_apply_name_to_output.isChecked():
name = self.txt_rs_name.text().replace(" ", "_").replace(".", "_").replace(",", "_").replace(";", "_").replace(":", "_").replace("/", "_").replace("\\", "_").replace("?", "_").replace("!", "_").replace("'", "_").replace('"', "_").replace("<", "_").replace(">", "_").replace("|", "_").replace("*", "_").replace("(", "_").replace(")", "_").replace("[", "_").replace("]", "_").replace("{", "_").replace("}", "_")

outpath = os.path.join(self.base_folder, name)
self.txt_outpath.setText(outpath)

def accept(self) -> None:

if self.txt_rs_name.text() == "":
message_box("Project Name", "Please enter a name for the Riverscapes project.")
return

if self.txt_outpath.text() == "":
message_box("Output Path", "Please select an output path for the Riverscapes project.")
return

# make sure the start of the output path is a valid drive letter
if not os.path.exists(self.txt_outpath.text()[0:2]):
message_box("Invalid Output Path", "The output path is invalid. Please select a valid output path.")
return

# check if output directory is empty. If so, prompt user to overwrite or cancel
if os.path.exists(self.txt_outpath.text()):
if len(os.listdir(self.txt_outpath.text())) > 0:
Expand Down Expand Up @@ -274,11 +305,15 @@ def accept(self) -> None:

# create a new project folder if it doesn't exist
if not os.path.exists(self.txt_outpath.text()):
os.mkdir(self.txt_outpath.text())
os.makedirs(self.txt_outpath.text())

# copy the geopackage layers to the new project folder
out_name = 'qris.gpkg' # os.path.split(self.qris_project.project_file)[1]
out_geopackage = os.path.abspath(os.path.join(self.txt_outpath.text(), out_name).replace("\\", "/"))

# Refrfesh the map canvas to ensure all layers are flushed to disk before copying
iface.mapCanvas().refreshAllLayers()

shutil.copy(self.qris_project.project_file, out_geopackage)

# Project Bounds
Expand Down Expand Up @@ -1006,8 +1041,8 @@ def browse_path(self):
ret = msg.exec_()
if ret == QtWidgets.QMessageBox.Cancel:
return

self.set_output_path(path)
self.base_folder = path.replace("/", "\\")
self.set_output_path()

def change_project_bounds(self):

Expand Down Expand Up @@ -1062,16 +1097,20 @@ def setupUi(self):
self.txt_rs_name.textChanged.connect(self.set_output_path)
self.grid.addWidget(self.txt_rs_name, 0, 1, 1, 1)

self.chk_apply_name_to_output = QtWidgets.QCheckBox("Use project name as output folder")
self.chk_apply_name_to_output.setChecked(True)
self.chk_apply_name_to_output.clicked.connect(self.set_output_path)
self.grid.addWidget(self.chk_apply_name_to_output, 1, 1, 1, 1)

# add label and horizontal layout with textbox and small button for output path
self.lbl_output = QtWidgets.QLabel("Output Path")
self.lbl_output.setToolTip("Select the folder where the Riverscapes project will be saved")
self.grid.addWidget(self.lbl_output, 1, 0, 1, 1)
self.grid.addWidget(self.lbl_output, 2, 0, 1, 1)

self.horiz_output = QtWidgets.QHBoxLayout()
self.grid.addLayout(self.horiz_output, 1, 1, 1, 1)
self.grid.addLayout(self.horiz_output, 2, 1, 1, 1)

self.txt_outpath = QtWidgets.QLineEdit()
self.txt_outpath.setReadOnly(True)
self.horiz_output.addWidget(self.txt_outpath)

self.btn_output = QtWidgets.QPushButton("...")
Expand All @@ -1082,10 +1121,10 @@ def setupUi(self):
# Project Bounds
self.lbl_project_bounds = QtWidgets.QLabel("Project Bounds")
self.lbl_project_bounds.setToolTip("Select the extent of the project. This is used for display purposes on the Riverscapes Data Exchange")
self.grid.addWidget(self.lbl_project_bounds, 2, 0, 1, 1, QtCore.Qt.AlignTop)
self.grid.addWidget(self.lbl_project_bounds, 3, 0, 1, 1, QtCore.Qt.AlignTop)

self.vert_project_bounds = QtWidgets.QVBoxLayout()
self.grid.addLayout(self.vert_project_bounds, 2, 1, 1, 1)
self.grid.addLayout(self.vert_project_bounds, 3, 1, 1, 1)

self.opt_project_bounds_all = QtWidgets.QRadioButton("Use all QRiS layers")
self.opt_project_bounds_all.setToolTip("Use the extent of all QRiS layers in the project")
Expand All @@ -1109,7 +1148,7 @@ def setupUi(self):
# New or Existing Project
self.lbl_new_or_existing = QtWidgets.QLabel("Export Type")
self.lbl_new_or_existing.setToolTip("Select whether to create a new Riverscapes Studio project or update an existing project")
self.grid.addWidget(self.lbl_new_or_existing, 3, 0, 1, 1)
self.grid.addWidget(self.lbl_new_or_existing, 4, 0, 1, 1)

self.group_new_or_existing = QtWidgets.QButtonGroup(self)

Expand All @@ -1118,16 +1157,16 @@ def setupUi(self):
self.group_new_or_existing.addButton(self.rdo_new)
self.rdo_new.setChecked(True)
self.rdo_new.clicked.connect(self.change_new_or_existing)
self.grid.addWidget(self.rdo_new, 3, 1, 1, 1)
self.grid.addWidget(self.rdo_new, 4, 1, 1, 1)

self.rdo_existing = QtWidgets.QRadioButton("Update Existing Riverscapes Studio Project")
self.rdo_existing.setToolTip("Update a previously uploaded Riverscapes Studio project")
self.group_new_or_existing.addButton(self.rdo_existing)
self.rdo_existing.clicked.connect(self.change_new_or_existing)
self.grid.addWidget(self.rdo_existing, 4, 1, 1, 1)
self.grid.addWidget(self.rdo_existing, 5, 1, 1, 1)

self.horiz_existing = QtWidgets.QHBoxLayout()
self.grid.addLayout(self.horiz_existing, 5, 1, 1, 1)
self.grid.addLayout(self.horiz_existing, 6, 1, 1, 1)

self.lbl_existing = QtWidgets.QLabel("Existing Project rs.xml file")
self.lbl_existing.setEnabled(False)
Expand All @@ -1146,12 +1185,12 @@ def setupUi(self):

# add multiline box for description
self.lbl_description = QtWidgets.QLabel("Description")
self.grid.addWidget(self.lbl_description, 6, 0, 1, 1, QtCore.Qt.AlignTop)
self.grid.addWidget(self.lbl_description, 7, 0, 1, 1, QtCore.Qt.AlignTop)

self.txt_description = QtWidgets.QTextEdit()
self.txt_description.setReadOnly(False)
self.txt_description.setText(self.qris_project.description)
self.grid.addWidget(self.txt_description, 6, 1, 1, 1)
self.grid.addWidget(self.txt_description, 7, 1, 1, 1)

# add vertical spacer
self.vert.addStretch()
Expand Down
5 changes: 2 additions & 3 deletions src/view/frm_import_dce_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,8 @@ def on_rdoImport_clicked(self):
self.field_status = []
# set all combo boxes to 'Do Not Import'
for i in range(self.tblFields.rowCount()):
combo: QtWidgets.QComboBox = self.tblFields.cellWidget(i, 2)
self.field_status.append(combo.currentIndex())
combo.setCurrentIndex(0)
chk_retain: QtWidgets.QCheckBox = self.tblFields.cellWidget(i, 2)
chk_retain.setChecked(False)
self.tblFields.setEnabled(False)

def setupUi(self):
Expand Down
34 changes: 34 additions & 0 deletions src/view/frm_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from ..model.project import Project
from ..model.metric import METRIC_SCHEMA, insert_metric

from .frm_export_project import DEFAULT_EXPORT_PATH

DOCK_WIDGET_LOCATION = 'dock_widget_location'
REMOVE_LAYERS_ON_CLOSE = 'remove_layers_on_close'

Expand Down Expand Up @@ -41,6 +43,10 @@ def __init__(self, settings: QSettings, qris_project: Project):
else:
self.chk_remove_layers_on_close.setChecked(False)

# Get the default export path
default_export_path = settings.value(DEFAULT_EXPORT_PATH, '')
self.txt_path_export.setText(default_export_path)

self.load_metrics()

def accept(self):
Expand All @@ -55,6 +61,11 @@ def accept(self):
else:
self.settings.setValue(REMOVE_LAYERS_ON_CLOSE, False)

if self.txt_path_export.text() != '':
self.settings.setValue(DEFAULT_EXPORT_PATH, self.txt_path_export.text())
else:
self.settings.setValue(DEFAULT_EXPORT_PATH, '')

super().accept()

def load_metrics(self):
Expand Down Expand Up @@ -248,6 +259,11 @@ def save_metrics(self):
else:
QMessageBox.information(self, "Save Metrics", "No new metrics to save.")

def select_export_path(self):
path = QFileDialog.getExistingDirectory(self, "Select default export path")
if path:
self.txt_path_export.setText(path)

def setup_ui(self):

self.resize(500, 300)
Expand All @@ -260,6 +276,24 @@ def setup_ui(self):

self.vertGeneral = QVBoxLayout()

horiz_export_path = QHBoxLayout()
self.vertGeneral.addLayout(horiz_export_path)

lbl_path_export = QLabel("Default Export Path")
horiz_export_path.addWidget(lbl_path_export)

self.txt_path_export = QLineEdit()
self.txt_path_export.setReadOnly(True)
horiz_export_path.addWidget(self.txt_path_export)

btn_path_export = QPushButton("...")
btn_path_export.clicked.connect(self.select_export_path)
horiz_export_path.addWidget(btn_path_export)

btn_clear_path_export = QPushButton("Clear")
btn_clear_path_export.clicked.connect(lambda: self.txt_path_export.setText(''))
horiz_export_path.addWidget(btn_clear_path_export)

self.chk_remove_layers_on_close = QCheckBox("Remove QRiS Project map layers on project close")
self.chk_remove_layers_on_close.setToolTip("Check this box to remove all layers from the project when it is closed.")
self.vertGeneral.addWidget(self.chk_remove_layers_on_close)
Expand Down