safe/gui/tools/wizard/wizard_dialog.py
# coding=utf-8
"""Wizard Dialog."""
import logging
from sqlite3 import OperationalError
from qgis.core import QgsProject
from qgis.PyQt.QtCore import QSettings, pyqtSignal, pyqtSlot
from qgis.PyQt.QtWidgets import QDialog, QMessageBox
from qgis.PyQt.QtGui import QPixmap, QIcon
from parameters.parameter_exceptions import InvalidValidationException
from safe.common.exceptions import (
HashNotFoundError,
NoKeywordsFoundError,
KeywordNotFoundError,
InvalidParameterError,
UnsupportedProviderError,
InaSAFEError,
MetadataReadError,
InvalidWizardStep)
from safe.definitions.constants import RECENT
from safe.definitions.hazard import continuous_hazard_unit
from safe.definitions.layer_geometry import (
layer_geometry_raster,
)
from safe.definitions.layer_modes import (
layer_mode_continuous, layer_mode_classified)
from safe.definitions.layer_purposes import (
layer_purpose_exposure, layer_purpose_aggregation, layer_purpose_hazard)
from safe.definitions.units import exposure_unit
from safe.definitions.utilities import get_compulsory_fields
from safe.gis.tools import geometry_type
from safe.gui.tools.wizard import STEP_KW, STEP_FC
from safe.gui.tools.wizard.utilities import layer_description_html
from safe.gui.tools.wizard.wizard_help import WizardHelp
from safe.gui.tools.wizard.wizard_strings import (
category_question_hazard,
category_question_exposure,
category_question_aggregation)
from safe.utilities.default_values import set_inasafe_default_value_qsetting
from safe.utilities.gis import (
is_polygon_layer)
from safe.utilities.i18n import tr
from safe.utilities.keyword_io import KeywordIO
from safe.utilities.qgis_utilities import display_warning_message_box
from safe.utilities.resources import get_ui_class, resources_path
from safe.utilities.utilities import (
get_error_message, is_keyword_version_supported)
from .step_fc00_functions1 import StepFcFunctions1
from .step_fc05_functions2 import StepFcFunctions2
from .step_fc15_hazlayer_origin import StepFcHazLayerOrigin
from .step_fc20_hazlayer_from_canvas import StepFcHazLayerFromCanvas
from .step_fc25_hazlayer_from_browser import StepFcHazLayerFromBrowser
from .step_fc30_explayer_origin import StepFcExpLayerOrigin
from .step_fc35_explayer_from_canvas import StepFcExpLayerFromCanvas
from .step_fc40_explayer_from_browser import StepFcExpLayerFromBrowser
from .step_fc45_disjoint_layers import StepFcDisjointLayers
from .step_fc50_agglayer_origin import StepFcAggLayerOrigin
from .step_fc55_agglayer_from_canvas import StepFcAggLayerFromCanvas
from .step_fc60_agglayer_from_browser import StepFcAggLayerFromBrowser
from .step_fc65_agglayer_disjoint import StepFcAggLayerDisjoint
from .step_fc70_extent import StepFcExtent
from .step_fc75_extent_disjoint import StepFcExtentDisjoint
from .step_fc85_summary import StepFcSummary
from .step_fc90_analysis import StepFcAnalysis
from .step_kw00_purpose import StepKwPurpose
from .step_kw05_subcategory import StepKwSubcategory
from .step_kw10_hazard_category import StepKwHazardCategory
from .step_kw13_band_selector import StepKwBandSelector
from .step_kw15_layermode import StepKwLayerMode
from .step_kw20_unit import StepKwUnit
from .step_kw25_classification import StepKwClassification
from .step_kw30_field import StepKwField
from .step_kw33_multi_classifications import StepKwMultiClassifications
from .step_kw40_classify import StepKwClassify
from .step_kw43_threshold import StepKwThreshold
from .step_kw44_fields_mapping import StepKwFieldsMapping
from .step_kw45_inasafe_fields import StepKwInaSAFEFields
from .step_kw47_default_inasafe_fields import StepKwDefaultInaSAFEFields
from .step_kw49_inasafe_raster_default_values import (
StepKwInaSAFERasterDefaultValues)
from .step_kw55_source import StepKwSource
from .step_kw57_extra_keywords import StepKwExtraKeywords
from .step_kw60_title import StepKwTitle
from .step_kw65_summary import StepKwSummary
__copyright__ = "Copyright 2016, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "info@inasafe.org"
__revision__ = '$Format:%H$'
LOGGER = logging.getLogger('InaSAFE')
FORM_CLASS = get_ui_class('wizard/wizard_dialog_base.ui')
class WizardDialog(QDialog, FORM_CLASS):
"""Dialog implementation class for the InaSAFE wizard."""
resized = pyqtSignal()
def __init__(self, parent=None, iface=None, dock=None):
"""Constructor for the dialog.
.. note:: In QtDesigner the advanced editor's predefined keywords
list should be shown in english always, so when adding entries to
cboKeyword, be sure to choose :safe_qgis:`Properties<<` and untick
the :safe_qgis:`translatable` property.
:param parent: Parent widget of this dialog.
:type parent: QWidget
:param iface: QGIS QgisAppInterface instance.
:type iface: QgisAppInterface
:param dock: Dock widget instance that we can notify of changes to
the keywords. Optional.
:type dock: Dock
"""
QDialog.__init__(self, parent)
self.setupUi(self)
self.setWindowTitle('InaSAFE')
icon = resources_path('img', 'icons', 'icon.png')
self.setWindowIcon(QIcon(icon))
# Constants
self.keyword_creation_wizard_name = tr(
'InaSAFE Keywords Creation Wizard')
self.ifcw_name = tr('InaSAFE Impact Function Centric Wizard')
# Note the keys should remain untranslated as we need to write
# english to the keywords file.
# Save reference to the QGIS interface and parent
self.iface = iface
self.parent = parent
self.dock = dock
self.suppress_warning_dialog = False
self.lblStep.clear()
# Set icons
self.lblMainIcon.setPixmap(
QPixmap(resources_path('img', 'icons', 'icon-white.svg')))
self.keyword_io = KeywordIO()
self.is_selected_layer_keywordless = False
self.parent_step = None
self.pbnBack.setEnabled(False)
self.pbnNext.setEnabled(False)
self.pbnCancel.released.connect(self.reject)
# Initialize attributes
self.existing_keywords = None
self.layer = None
self.hazard_layer = None
self.exposure_layer = None
self.aggregation_layer = None
self.step_kw_purpose = StepKwPurpose(self)
self.step_kw_subcategory = StepKwSubcategory(self)
self.step_kw_hazard_category = StepKwHazardCategory(self)
self.step_kw_band_selector = StepKwBandSelector(self)
self.step_kw_layermode = StepKwLayerMode(self)
self.step_kw_unit = StepKwUnit(self)
self.step_kw_classification = StepKwClassification(self)
self.step_kw_field = StepKwField(self)
self.step_kw_multi_classifications = StepKwMultiClassifications(self)
self.step_kw_classify = StepKwClassify(self)
self.step_kw_threshold = StepKwThreshold(self)
self.step_kw_fields_mapping = StepKwFieldsMapping(self)
self.step_kw_inasafe_fields = StepKwInaSAFEFields(self)
self.step_kw_default_inasafe_fields = StepKwDefaultInaSAFEFields(self)
self.step_kw_inasafe_raster_default_values = \
StepKwInaSAFERasterDefaultValues(self)
self.step_kw_source = StepKwSource(self)
self.step_kw_extra_keywords = StepKwExtraKeywords(self)
self.step_kw_title = StepKwTitle(self)
self.step_kw_summary = StepKwSummary(self)
self.step_fc_functions1 = StepFcFunctions1(self)
self.step_fc_functions2 = StepFcFunctions2(self)
self.step_fc_hazlayer_origin = StepFcHazLayerOrigin(self)
self.step_fc_hazlayer_from_canvas = StepFcHazLayerFromCanvas(self)
self.step_fc_hazlayer_from_browser = StepFcHazLayerFromBrowser(self)
self.step_fc_explayer_origin = StepFcExpLayerOrigin(self)
self.step_fc_explayer_from_canvas = StepFcExpLayerFromCanvas(self)
self.step_fc_explayer_from_browser = StepFcExpLayerFromBrowser(self)
self.step_fc_disjoint_layers = StepFcDisjointLayers(self)
self.step_fc_agglayer_origin = StepFcAggLayerOrigin(self)
self.step_fc_agglayer_from_canvas = StepFcAggLayerFromCanvas(self)
self.step_fc_agglayer_from_browser = StepFcAggLayerFromBrowser(self)
self.step_fc_agglayer_disjoint = StepFcAggLayerDisjoint(self)
self.step_fc_extent = StepFcExtent(self)
self.step_fc_extent_disjoint = StepFcExtentDisjoint(self)
self.step_fc_summary = StepFcSummary(self)
self.step_fc_analysis = StepFcAnalysis(self)
self.wizard_help = WizardHelp(self)
self.stackedWidget.addWidget(self.step_kw_purpose)
self.stackedWidget.addWidget(self.step_kw_subcategory)
self.stackedWidget.addWidget(self.step_kw_hazard_category)
self.stackedWidget.addWidget(self.step_kw_band_selector)
self.stackedWidget.addWidget(self.step_kw_layermode)
self.stackedWidget.addWidget(self.step_kw_unit)
self.stackedWidget.addWidget(self.step_kw_classification)
self.stackedWidget.addWidget(self.step_kw_field)
self.stackedWidget.addWidget(self.step_kw_multi_classifications)
self.stackedWidget.addWidget(self.step_kw_classify)
self.stackedWidget.addWidget(self.step_kw_threshold)
self.stackedWidget.addWidget(self.step_kw_fields_mapping)
self.stackedWidget.addWidget(self.step_kw_inasafe_fields)
self.stackedWidget.addWidget(self.step_kw_default_inasafe_fields)
self.stackedWidget.addWidget(
self.step_kw_inasafe_raster_default_values)
self.stackedWidget.addWidget(self.step_kw_source)
self.stackedWidget.addWidget(self.step_kw_extra_keywords)
self.stackedWidget.addWidget(self.step_kw_title)
self.stackedWidget.addWidget(self.step_kw_summary)
self.stackedWidget.addWidget(self.step_fc_functions1)
self.stackedWidget.addWidget(self.step_fc_functions2)
self.stackedWidget.addWidget(self.step_fc_hazlayer_origin)
self.stackedWidget.addWidget(self.step_fc_hazlayer_from_canvas)
self.stackedWidget.addWidget(self.step_fc_hazlayer_from_browser)
self.stackedWidget.addWidget(self.step_fc_explayer_origin)
self.stackedWidget.addWidget(self.step_fc_explayer_from_canvas)
self.stackedWidget.addWidget(self.step_fc_explayer_from_browser)
self.stackedWidget.addWidget(self.step_fc_disjoint_layers)
self.stackedWidget.addWidget(self.step_fc_agglayer_origin)
self.stackedWidget.addWidget(self.step_fc_agglayer_from_canvas)
self.stackedWidget.addWidget(self.step_fc_agglayer_from_browser)
self.stackedWidget.addWidget(self.step_fc_agglayer_disjoint)
self.stackedWidget.addWidget(self.step_fc_extent)
self.stackedWidget.addWidget(self.step_fc_extent_disjoint)
self.stackedWidget.addWidget(self.step_fc_summary)
self.stackedWidget.addWidget(self.step_fc_analysis)
self.stackedWidget.addWidget(self.wizard_help)
# QSetting
self.setting = QSettings()
# Wizard Steps
self.impact_function_steps = []
self.keyword_steps = []
self.on_help = False
self.resized.connect(self.after_resize)
def set_mode_label_to_keywords_creation(self):
"""Set the mode label to the Keywords Creation/Update mode."""
self.setWindowTitle(self.keyword_creation_wizard_name)
if self.get_existing_keyword('layer_purpose'):
mode_name = tr(
'Keywords update wizard for layer <b>{layer_name}</b>').format(
layer_name=self.layer.name())
else:
mode_name = tr(
'Keywords creation wizard for layer <b>{layer_name}</b>'
).format(layer_name=self.layer.name())
self.lblSubtitle.setText(mode_name)
def set_mode_label_to_ifcw(self):
"""Set the mode label to the IFCW."""
self.setWindowTitle(self.ifcw_name)
self.lblSubtitle.setText(tr(
'Use this wizard to run a guided impact assessment'))
def set_keywords_creation_mode(self, layer=None, keywords=None):
"""Set the Wizard to the Keywords Creation mode.
:param layer: Layer to set the keywords for
:type layer: QgsMapLayer
:param keywords: Keywords for the layer.
:type keywords: dict, None
"""
self.layer = layer or self.iface.mapCanvas().currentLayer()
if keywords is not None:
self.existing_keywords = keywords
else:
# Always read from metadata file.
try:
self.existing_keywords = self.keyword_io.read_keywords(
self.layer)
except (HashNotFoundError,
OperationalError,
NoKeywordsFoundError,
KeywordNotFoundError,
InvalidParameterError,
UnsupportedProviderError,
MetadataReadError):
self.existing_keywords = {}
self.set_mode_label_to_keywords_creation()
step = self.step_kw_purpose
step.set_widgets()
self.go_to_step(step)
def set_function_centric_mode(self):
"""Set the Wizard to the Function Centric mode."""
self.set_mode_label_to_ifcw()
step = self.step_fc_functions1
step.set_widgets()
self.go_to_step(step)
def field_keyword_for_the_layer(self):
"""Return the proper keyword for field for the current layer.
:returns: the field keyword
:rtype: str
"""
layer_purpose_key = self.step_kw_purpose.selected_purpose()['key']
if layer_purpose_key == layer_purpose_aggregation['key']:
return get_compulsory_fields(layer_purpose_key)['key']
elif layer_purpose_key in [
layer_purpose_exposure['key'], layer_purpose_hazard['key']]:
layer_subcategory_key = \
self.step_kw_subcategory.selected_subcategory()['key']
return get_compulsory_fields(
layer_purpose_key, layer_subcategory_key)['key']
else:
raise InvalidParameterError
def get_parent_mode_constraints(self):
"""Return the category and subcategory keys to be set in the
subordinate mode.
:returns: (the category definition, the hazard/exposure definition)
:rtype: (dict, dict)
"""
h, e, _hc, _ec = self.selected_impact_function_constraints()
if self.parent_step in [self.step_fc_hazlayer_from_canvas,
self.step_fc_hazlayer_from_browser]:
category = layer_purpose_hazard
subcategory = h
elif self.parent_step in [self.step_fc_explayer_from_canvas,
self.step_fc_explayer_from_browser]:
category = layer_purpose_exposure
subcategory = e
elif self.parent_step:
category = layer_purpose_aggregation
subcategory = None
else:
category = None
subcategory = None
return category, subcategory
def selected_impact_function_constraints(self):
"""Obtain impact function constraints selected by user.
:returns: Tuple of metadata of hazard, exposure,
hazard layer geometry and exposure layer geometry
:rtype: tuple
"""
hazard = self.step_fc_functions1.selected_value(
layer_purpose_hazard['key'])
exposure = self.step_fc_functions1.selected_value(
layer_purpose_exposure['key'])
hazard_geometry = self.step_fc_functions2.selected_value(
layer_purpose_hazard['key'])
exposure_geometry = self.step_fc_functions2.selected_value(
layer_purpose_exposure['key'])
return hazard, exposure, hazard_geometry, exposure_geometry
def is_layer_compatible(self, layer, layer_purpose=None, keywords=None):
"""Validate if a given layer is compatible for selected IF
as a given layer_purpose
:param layer: The layer to be validated
:type layer: QgsVectorLayer | QgsRasterLayer
:param layer_purpose: The layer_purpose the layer is validated for
:type layer_purpose: None, string
:param keywords: The layer keywords
:type keywords: None, dict
:returns: True if layer is appropriate for the selected role
:rtype: boolean
"""
# If not explicitly stated, find the desired purpose
# from the parent step
if not layer_purpose:
layer_purpose = self.get_parent_mode_constraints()[0]['key']
# If not explicitly stated, read the layer's keywords
if not keywords:
try:
keywords = self.keyword_io.read_keywords(layer)
if 'layer_purpose' not in keywords:
keywords = None
except (HashNotFoundError,
OperationalError,
NoKeywordsFoundError,
KeywordNotFoundError,
InvalidParameterError,
UnsupportedProviderError):
keywords = None
# Get allowed subcategory and layer_geometry from IF constraints
h, e, hc, ec = self.selected_impact_function_constraints()
if layer_purpose == 'hazard':
subcategory = h['key']
layer_geometry = hc['key']
elif layer_purpose == 'exposure':
subcategory = e['key']
layer_geometry = ec['key']
else:
# For aggregation layers, use a simplified test and return
if (keywords and 'layer_purpose' in keywords
and keywords['layer_purpose'] == layer_purpose):
return True
if not keywords and is_polygon_layer(layer):
return True
return False
# Compare layer properties with explicitly set constraints
# Reject if layer geometry doesn't match
if layer_geometry != self.get_layer_geometry_key(layer):
return False
# If no keywords, there's nothing more we can check.
# The same if the keywords version doesn't match
if not keywords or 'keyword_version' not in keywords:
return True
keyword_version = str(keywords['keyword_version'])
if not is_keyword_version_supported(keyword_version):
return True
# Compare layer keywords with explicitly set constraints
# Reject if layer purpose missing or doesn't match
if ('layer_purpose' not in keywords
or keywords['layer_purpose'] != layer_purpose):
return False
# Reject if layer subcategory doesn't match
if (layer_purpose in keywords
and keywords[layer_purpose] != subcategory):
return False
return True
def get_compatible_canvas_layers(self, category):
"""Collect layers from map canvas, compatible for the given category
and selected impact function
.. note:: Returns layers with keywords and layermode matching
the category and compatible with the selected impact function.
Also returns layers without keywords with layermode
compatible with the selected impact function.
:param category: The category to filter for.
:type category: string
:returns: Metadata of found layers.
:rtype: list of dicts
"""
# Collect compatible layers
layers = []
for layer in self.iface.mapCanvas().layers():
try:
keywords = self.keyword_io.read_keywords(layer)
if 'layer_purpose' not in keywords:
keywords = None
except (HashNotFoundError,
OperationalError,
NoKeywordsFoundError,
KeywordNotFoundError,
InvalidParameterError,
UnsupportedProviderError):
keywords = None
if self.is_layer_compatible(layer, category, keywords):
layers += [
{'id': layer.id(),
'name': layer.name(),
'keywords': keywords}]
# Move layers without keywords to the end
l1 = [_layer for _layer in layers if _layer['keywords']]
l2 = [_layer for _layer in layers if not _layer['keywords']]
layers = l1 + l2
return layers
def get_layer_geometry_key(self, layer=None):
"""Obtain layer mode of a given layer.
If no layer specified, the current layer is used
:param layer : layer to examine
:type layer: QgsMapLayer or None
:returns: The layer mode.
:rtype: str
"""
if not layer:
layer = self.layer
return geometry_type(layer)
def get_existing_keyword(self, keyword):
"""Obtain an existing keyword's value.
:param keyword: A keyword from keywords.
:type keyword: str
:returns: The value of the keyword.
:rtype: str, QUrl
"""
if self.existing_keywords is None:
return {}
if keyword is not None:
return self.existing_keywords.get(keyword, {})
else:
return {}
def get_layer_description_from_canvas(self, layer, purpose):
"""Obtain the description of a canvas layer selected by user.
:param layer: The QGIS layer.
:type layer: QgsMapLayer
:param purpose: The layer purpose of the layer to get the description.
:type purpose: string
:returns: description of the selected layer.
:rtype: string
"""
if not layer:
return ""
try:
keywords = self.keyword_io.read_keywords(layer)
if 'layer_purpose' not in keywords:
keywords = None
except (HashNotFoundError,
OperationalError,
NoKeywordsFoundError,
KeywordNotFoundError,
InvalidParameterError,
UnsupportedProviderError):
keywords = None
# set the current layer (e.g. for the keyword creation sub-thread)
self.layer = layer
if purpose == layer_purpose_hazard['key']:
self.hazard_layer = layer
elif purpose == layer_purpose_exposure['key']:
self.exposure_layer = layer
else:
self.aggregation_layer = layer
# Check if the layer is keywordless
if keywords and 'keyword_version' in keywords:
kw_ver = str(keywords['keyword_version'])
self.is_selected_layer_keywordless = (
not is_keyword_version_supported(kw_ver))
else:
self.is_selected_layer_keywordless = True
description = layer_description_html(layer, keywords)
return description
# ===========================
# NAVIGATION
# ===========================
def go_to_step(self, step):
"""Set the stacked widget to the given step, set up the buttons,
and run all operations that should start immediately after
entering the new step.
:param step: The step widget to be moved to.
:type step: WizardStep
"""
self.stackedWidget.setCurrentWidget(step)
# Disable the Next button unless new data already entered
self.pbnNext.setEnabled(step.is_ready_to_next_step())
# Enable the Back button unless it's not the first step
self.pbnBack.setEnabled(
step not in [self.step_kw_purpose, self.step_fc_functions1]
or self.parent_step is not None)
# Set Next button label
if (step in [self.step_kw_summary, self.step_fc_analysis]
and self.parent_step is None):
self.pbnNext.setText(tr('Finish'))
elif step == self.step_fc_summary:
self.pbnNext.setText(tr('Run'))
else:
self.pbnNext.setText(tr('Next'))
# Run analysis after switching to the new step
if step == self.step_fc_analysis:
self.step_fc_analysis.setup_and_run_analysis()
# Set lblSelectCategory label if entering the kw mode
# from the ifcw mode
if step == self.step_kw_purpose and self.parent_step:
if self.parent_step in [self.step_fc_hazlayer_from_canvas,
self.step_fc_hazlayer_from_browser]:
text_label = category_question_hazard
elif self.parent_step in [self.step_fc_explayer_from_canvas,
self.step_fc_explayer_from_browser]:
text_label = category_question_exposure
else:
text_label = category_question_aggregation
self.step_kw_purpose.lblSelectCategory.setText(text_label)
# prevents actions being handled twice
# noinspection PyPep8Naming
@pyqtSlot()
def on_pbnNext_released(self):
"""Handle the Next button release.
.. note:: This is an automatic Qt slot
executed when the Next button is released.
"""
current_step = self.get_current_step()
if current_step == self.step_kw_fields_mapping:
try:
self.step_kw_fields_mapping.get_field_mapping()
except InvalidValidationException as e:
display_warning_message_box(
self, tr('Invalid Field Mapping'), str(e))
return
if current_step.step_type == STEP_FC:
self.impact_function_steps.append(current_step)
elif current_step.step_type == STEP_KW:
self.keyword_steps.append(current_step)
else:
LOGGER.debug(current_step.step_type)
raise InvalidWizardStep
# Save keywords if it's the end of the keyword creation mode
if current_step == self.step_kw_summary:
self.save_current_keywords()
# After any step involving Browser, add selected layer to map canvas
if current_step in [self.step_fc_hazlayer_from_browser,
self.step_fc_explayer_from_browser,
self.step_fc_agglayer_from_browser]:
if not QgsProject.instance().mapLayersByName(
self.layer.name()):
QgsProject.instance().addMapLayers([self.layer])
# Make the layer visible. Might be hidden by default. See #2925
treeroot = QgsProject.instance().layerTreeRoot()
treelayer = treeroot.findLayer(self.layer.id())
if treelayer:
treelayer.setItemVisibilityChecked(True)
# After the extent selection, save the extent and disconnect signals
if current_step == self.step_fc_extent:
self.step_fc_extent.write_extent()
# Determine the new step to be switched
new_step = current_step.get_next_step()
if new_step is not None:
# Prepare the next tab
new_step.set_widgets()
else:
# Wizard complete
self.accept()
return
self.go_to_step(new_step)
# prevents actions being handled twice
# noinspection PyPep8Naming
@pyqtSlot()
def on_pbnBack_released(self):
"""Handle the Back button release.
.. note:: This is an automatic Qt slot
executed when the Back button is released.
"""
current_step = self.get_current_step()
if current_step.step_type == STEP_FC:
new_step = self.impact_function_steps.pop()
elif current_step.step_type == STEP_KW:
try:
new_step = self.keyword_steps.pop()
except IndexError:
new_step = self.impact_function_steps.pop()
else:
raise InvalidWizardStep
# set focus to table widgets, as the inactive selection style is gray
if new_step == self.step_fc_functions1:
self.step_fc_functions1.tblFunctions1.setFocus()
if new_step == self.step_fc_functions2:
self.step_fc_functions2.tblFunctions2.setFocus()
# Re-connect disconnected signals when coming back to the Extent step
if new_step == self.step_fc_extent:
self.step_fc_extent.set_widgets()
# Set Next button label
self.pbnNext.setText(tr('Next'))
self.pbnNext.setEnabled(True)
self.go_to_step(new_step)
# prevents actions being handled twice
# noinspection PyPep8Naming
@pyqtSlot()
def on_pbnHelp_released(self):
if self.on_help:
self.pbnHelp.setText(tr('Show help'))
self.wizard_help.restore_button_state()
self.stackedWidget.setCurrentWidget(self.wizard_help.wizard_step)
else:
self.pbnHelp.setText(tr('Hide help'))
self.wizard_help.show_help(self.get_current_step())
self.stackedWidget.setCurrentWidget(self.wizard_help)
self.on_help = not self.on_help
def get_current_step(self):
"""Return current step of the wizard.
:returns: Current step of the wizard.
:rtype: WizardStep instance
"""
return self.stackedWidget.currentWidget()
def get_keywords(self):
"""Obtain the state of the dialog as a keywords dict.
:returns: Keywords reflecting the state of the dialog.
:rtype: dict
"""
keywords = {}
inasafe_fields = {}
keywords['layer_geometry'] = self.get_layer_geometry_key()
if self.step_kw_purpose.selected_purpose():
keywords['layer_purpose'] = self.step_kw_purpose.\
selected_purpose()['key']
if self.step_kw_subcategory.selected_subcategory():
key = self.step_kw_purpose.selected_purpose()['key']
keywords[key] = self.step_kw_subcategory.\
selected_subcategory()['key']
if self.get_layer_geometry_key() == layer_geometry_raster['key']:
if self.step_kw_band_selector.selected_band():
keywords['active_band'] = self.step_kw_band_selector.\
selected_band()
if keywords['layer_purpose'] == layer_purpose_hazard['key']:
if self.step_kw_hazard_category.selected_hazard_category():
keywords['hazard_category'] \
= self.step_kw_hazard_category.\
selected_hazard_category()['key']
if self.step_kw_layermode.selected_layermode():
keywords['layer_mode'] = self.step_kw_layermode.\
selected_layermode()['key']
if self.step_kw_unit.selected_unit():
if self.step_kw_purpose.selected_purpose() == layer_purpose_hazard:
key = continuous_hazard_unit['key']
else:
key = exposure_unit['key']
keywords[key] = self.step_kw_unit.selected_unit()['key']
if self.step_kw_field.selected_fields():
field_key = self.field_keyword_for_the_layer()
inasafe_fields[field_key] = self.step_kw_field.selected_fields()
if self.step_kw_classification.selected_classification():
keywords['classification'] = self.step_kw_classification.\
selected_classification()['key']
if keywords['layer_purpose'] == layer_purpose_hazard['key']:
multi_classifications = self.step_kw_multi_classifications.\
get_current_state()
value_maps = multi_classifications.get('value_maps')
if value_maps is not None:
keywords['value_maps'] = value_maps
thresholds = multi_classifications.get('thresholds')
if thresholds is not None:
keywords['thresholds'] = thresholds
else:
if self.step_kw_layermode.selected_layermode():
layer_mode = self.step_kw_layermode.selected_layermode()
if layer_mode == layer_mode_continuous:
thresholds = self.step_kw_threshold.get_threshold()
if thresholds:
keywords['thresholds'] = thresholds
elif layer_mode == layer_mode_classified:
value_map = self.step_kw_classify.selected_mapping()
if value_map:
keywords['value_map'] = value_map
if self.step_kw_source.leSource.text():
keywords['source'] = self.step_kw_source.leSource.text()
if self.step_kw_source.leSource_url.text():
keywords['url'] = self.step_kw_source.leSource_url.text()
if self.step_kw_source.leSource_scale.text():
keywords['scale'] = self.step_kw_source.leSource_scale.text()
if self.step_kw_source.ckbSource_date.isChecked():
keywords['date'] = self.step_kw_source.dtSource_date.dateTime()
if self.step_kw_source.leSource_license.text():
keywords['license'] = self.step_kw_source.leSource_license.text()
if self.step_kw_title.leTitle.text():
keywords['title'] = self.step_kw_title.leTitle.text()
inasafe_fields.update(self.step_kw_inasafe_fields.get_inasafe_fields())
inasafe_fields.update(
self.step_kw_default_inasafe_fields.get_inasafe_fields())
inasafe_fields.update(
self.step_kw_fields_mapping.get_field_mapping()['fields'])
if inasafe_fields:
keywords['inasafe_fields'] = inasafe_fields
inasafe_default_values = {}
if keywords['layer_geometry'] == layer_geometry_raster['key']:
pass
# Notes(IS): Skipped assigning raster inasafe default value for
# now.
# inasafe_default_values = self.\
# step_kw_inasafe_raster_default_values.\
# get_inasafe_default_values()
else:
inasafe_default_values.update(
self.step_kw_default_inasafe_fields.get_inasafe_default_values(
))
inasafe_default_values.update(
self.step_kw_fields_mapping.get_field_mapping()['values'])
if inasafe_default_values:
keywords['inasafe_default_values'] = inasafe_default_values
if self.step_kw_subcategory.selected_subcategory():
subcategory = self.step_kw_subcategory.selected_subcategory()
if subcategory.get('extra_keywords'):
extra_keywords = self.step_kw_extra_keywords.\
get_extra_keywords()
if extra_keywords:
keywords['extra_keywords'] = extra_keywords
return keywords
def save_current_keywords(self):
"""Save keywords to the layer.
It will write out the keywords for the current layer.
This method is based on the KeywordsDialog class.
"""
current_keywords = self.get_keywords()
try:
self.keyword_io.write_keywords(
layer=self.layer, keywords=current_keywords)
except InaSAFEError as e:
error_message = get_error_message(e)
# noinspection PyCallByClass,PyTypeChecker,PyArgumentList
QMessageBox.warning(
self,
tr('InaSAFE'),
tr('An error was encountered when saving the following '
'keywords:\n {error_message}').format(
error_message=error_message.to_html()))
if self.dock is not None:
# noinspection PyUnresolvedReferences
self.dock.get_layers()
# Save default value to QSetting
if current_keywords.get('inasafe_default_values'):
for key, value in (
list(current_keywords['inasafe_default_values'].items())):
set_inasafe_default_value_qsetting(
self.setting, RECENT, key, value)
# Adapted from https://stackoverflow.com/a/43126946/1198772
def resizeEvent(self, event):
"""Emit custom signal when the window is re-sized.
:param event: The re-sized event.
:type event: QResizeEvent
"""
self.resized.emit()
return super(WizardDialog, self).resizeEvent(event)
def after_resize(self):
"""Method after resizing the window."""
# Reset the max height. 190 is a number that make it pretty
max_height = self.height() - 190
self.scrollAreaWidgetContents.setMaximumHeight(max_height)