class AnalysisHandler(QObject): """Analysis handler for the dock and the wizard.""" analysisDone = pyqtSignal(bool) # noinspection PyUnresolvedReferences def __init__(self, parent): """Constructor for the class. :param parent: Parent widget i.e. the wizard dialog. :type parent: QWidget """ QtCore.QObject.__init__(self) self.parent = parent # Do not delete this self.iface = parent.iface self.keyword_io = KeywordIO() self.impact_function_manager = ImpactFunctionManager() self.extent = Extent(self.iface) self.analysis = None # Values for settings these get set in read_settings. self.run_in_thread_flag = None self.zoom_to_impact_flag = None self.hide_exposure_flag = None self.clip_hard = None self.show_intermediate_layers = None self.show_rubber_bands = False self.last_analysis_rubberband = None # This is a rubber band to show what the AOI of the # next analysis will be. Also added in 2.1.0 self.next_analysis_rubberband = None self.read_settings() def enable_signal_receiver(self): """Setup dispatcher for all available signal from Analysis. .. note:: Adapted from the dock """ dispatcher.connect( self.show_busy, signal=BUSY_SIGNAL) dispatcher.connect( self.hide_busy, signal=NOT_BUSY_SIGNAL) dispatcher.connect( self.completed, signal=ANALYSIS_DONE_SIGNAL) # noinspection PyArgumentEqualDefault dispatcher.connect( self.show_dynamic_message, signal=DYNAMIC_MESSAGE_SIGNAL) # noinspection PyArgumentEqualDefault dispatcher.connect( self.parent.wvResults.static_message_event, signal=STATIC_MESSAGE_SIGNAL, sender=dispatcher.Any) # noinspection PyArgumentEqualDefault dispatcher.connect( self.parent.wvResults.error_message_event, signal=ERROR_MESSAGE_SIGNAL, sender=dispatcher.Any) def disable_signal_receiver(self): """Remove dispatcher for all available signal from Analysis. .. note:: Adapted from the dock """ dispatcher.disconnect( self.show_busy, signal=BUSY_SIGNAL) dispatcher.disconnect( self.hide_busy, signal=NOT_BUSY_SIGNAL) dispatcher.disconnect( self.completed, signal=ANALYSIS_DONE_SIGNAL) dispatcher.disconnect( self.show_dynamic_message, signal=DYNAMIC_MESSAGE_SIGNAL) def show_static_message(self, message): """Send a static message to the message viewer. Static messages cause any previous content in the MessageViewer to be replaced with new content. .. note:: Copied from the dock :param message: An instance of our rich message class. :type message: Message """ dispatcher.send( signal=STATIC_MESSAGE_SIGNAL, sender=self, message=message) def show_dynamic_message(self, sender, message): """Send a dynamic message to the message viewer. Dynamic messages are appended to any existing content in the MessageViewer. .. note:: Modified from the dock :param sender: The object that sent the message. :type sender: Object, None :param message: An instance of our rich message class. :type message: Message """ # TODO Hardcoded step - may overflow, if number of messages increase self.parent.pbProgress.setValue(self.parent.pbProgress.value() + 15) self.parent.wvResults.dynamic_message_event(sender, message) def show_error_message(self, error_message): """Send an error message to the message viewer. Error messages cause any previous content in the MessageViewer to be replaced with new content. .. note:: Copied from the dock :param error_message: An instance of our rich error message class. :type error_message: ErrorMessage """ dispatcher.send( signal=ERROR_MESSAGE_SIGNAL, sender=self, message=error_message) self.hide_busy() def read_settings(self): """Restore settings from QSettings. Do this on init and after changing options in the options dialog. """ settings = QSettings() flag = bool(settings.value( 'inasafe/showRubberBands', False, type=bool)) self.extent.show_rubber_bands = flag try: extent = settings.value('inasafe/analysis_extent', '', type=str) crs = settings.value('inasafe/analysis_extent_crs', '', type=str) except TypeError: # Any bogus stuff in settings and we just clear them extent = '' crs = '' if extent != '' and crs != '': extent = extent_string_to_array(extent) try: self.extent.user_extent = QgsRectangle(*extent) self.extent.user_extent_crs = QgsCoordinateReferenceSystem(crs) self.extent.show_user_analysis_extent() except TypeError: self.extent.user_extent = None self.extent.user_extent_crs = None flag = settings.value( 'inasafe/useThreadingFlag', False, type=bool) self.run_in_thread_flag = flag flag = settings.value( 'inasafe/setZoomToImpactFlag', True, type=bool) self.zoom_to_impact_flag = flag # whether exposure layer should be hidden after model completes flag = settings.value( 'inasafe/setHideExposureFlag', False, type=bool) self.hide_exposure_flag = flag # whether to 'hard clip' layers (e.g. cut buildings in half if they # lie partially in the AOI self.clip_hard = settings.value('inasafe/clip_hard', False, type=bool) # whether to show or not postprocessing generated layers self.show_intermediate_layers = settings.value( 'inasafe/show_intermediate_layers', False, type=bool) # whether to show or not dev only options self.developer_mode = settings.value( 'inasafe/developer_mode', False, type=bool) # whether to show or not a custom Logo self.organisation_logo_path = settings.value( 'inasafe/organisation_logo_path', default_organisation_logo_path(), type=str) flag = bool(settings.value( 'inasafe/showOrganisationLogoInDockFlag', True, type=bool)) def show_busy(self): """Lock buttons and enable the busy cursor.""" self.parent.pbnNext.setEnabled(False) self.parent.pbnBack.setEnabled(False) self.parent.pbnCancel.setEnabled(False) QtGui.qApp.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor)) self.parent.repaint() QtGui.qApp.processEvents() def hide_busy(self): """Unlock buttons A helper function to indicate processing is done.""" self.parent.pbnNext.setEnabled(True) self.parent.pbnBack.setEnabled(True) self.parent.pbnCancel.setEnabled(True) self.parent.repaint() QtGui.qApp.restoreOverrideCursor() def analysis_error(self, exception, message): """A helper to spawn an error and halt processing. An exception will be logged, busy status removed and a message displayed. .. note:: Copied from the dock :param message: an ErrorMessage to display :type message: ErrorMessage, Message :param exception: An exception that was raised :type exception: Exception """ self.hide_busy() LOGGER.exception(message) message = get_error_message(exception, context=message) self.show_error_message(message) self.analysisDone.emit(False) def setup_and_run_analysis(self): """Setup and execute the analysis""" self.enable_signal_receiver() self.show_busy() self.init_analysis() try: self.analysis.setup_analysis() except InsufficientOverlapError as e: raise e self.extent.show_last_analysis_extent( self.analysis.clip_parameters[1]) # Start the analysis self.analysis.run_analysis() self.disable_signal_receiver() def init_analysis(self): """Setup analysis to make it ready to work. .. note:: Copied or adapted from the dock """ self.analysis = Analysis() # Layers self.analysis.hazard_layer = self.parent.hazard_layer self.analysis.exposure_layer = self.parent.exposure_layer self.analysis.aggregation_layer = self.parent.aggregation_layer # TODO test if the implement aggregation layer works! # noinspection PyTypeChecker self.analysis.hazard_keyword = self.keyword_io.read_keywords( self.parent.hazard_layer) self.analysis.exposure_keyword = self.keyword_io.read_keywords( self.parent.exposure_layer) # Need to check since aggregation layer is not mandatory if self.analysis.aggregation_layer: self.analysis.aggregation_keyword = self.keyword_io.read_keywords( self.parent.aggregation_layer) # Impact Function impact_function = self.impact_function_manager.get( self.parent.selected_function()['id']) impact_function.parameters = self.parent.if_params self.analysis.impact_function = impact_function # Variables self.analysis.clip_hard = self.clip_hard self.analysis.show_intermediate_layers = self.show_intermediate_layers self.analysis.run_in_thread_flag = self.run_in_thread_flag self.analysis.map_canvas = self.iface.mapCanvas() # Extent if self.parent.rbExtentUser.isChecked(): self.analysis.user_extent = self.extent.user_extent else: self.analysis.user_extent = None self.analysis.user_extent_crs = self.extent.user_extent_crs self.analysis.clip_to_viewport = self.parent.rbExtentScreen.isChecked() def completed(self): """Slot activated when the process is done. .. note:: Adapted from the dock """ # Try to run completion code try: from datetime import datetime LOGGER.debug(datetime.now()) LOGGER.debug('get engine impact layer') LOGGER.debug(self.analysis is None) engine_impact_layer = self.analysis.get_impact_layer() # Load impact layer into QGIS qgis_impact_layer = read_impact_layer(engine_impact_layer) report = self.show_results( qgis_impact_layer, engine_impact_layer) except Exception, e: # pylint: disable=W0703 # FIXME (Ole): This branch is not covered by the tests self.analysis_error(e, self.tr('Error loading impact layer.')) else:
class AnalysisHandler(QObject): """Analysis handler for the dock and the wizard.""" analysisDone = pyqtSignal(bool) # noinspection PyUnresolvedReferences def __init__(self, parent): """Constructor for the class. :param parent: Parent widget i.e. the wizard dialog. :type parent: QWidget """ QtCore.QObject.__init__(self) self.parent = parent # Do not delete this self.iface = parent.iface self.keyword_io = KeywordIO() self.extent = Extent(self.iface) self.analysis = None # Values for settings these get set in read_settings. self.run_in_thread_flag = None self.zoom_to_impact_flag = None self.hide_exposure_flag = None self.clip_hard = None self.show_intermediate_layers = None self.show_rubber_bands = False self.last_analysis_rubberband = None # This is a rubber band to show what the AOI of the # next analysis will be. Also added in 2.1.0 self.next_analysis_rubberband = None self.read_settings() def enable_signal_receiver(self): """Setup dispatcher for all available signal from Analysis. .. note:: Adapted from the dock """ dispatcher.connect(self.show_busy, signal=BUSY_SIGNAL) dispatcher.connect(self.hide_busy, signal=NOT_BUSY_SIGNAL) dispatcher.connect(self.completed, signal=ANALYSIS_DONE_SIGNAL) # noinspection PyArgumentEqualDefault dispatcher.connect(self.show_dynamic_message, signal=DYNAMIC_MESSAGE_SIGNAL) # noinspection PyArgumentEqualDefault dispatcher.connect(self.parent.wvResults.static_message_event, signal=STATIC_MESSAGE_SIGNAL, sender=dispatcher.Any) # noinspection PyArgumentEqualDefault dispatcher.connect(self.parent.wvResults.error_message_event, signal=ERROR_MESSAGE_SIGNAL, sender=dispatcher.Any) def disable_signal_receiver(self): """Remove dispatcher for all available signal from Analysis. .. note:: Adapted from the dock """ dispatcher.disconnect(self.show_busy, signal=BUSY_SIGNAL) dispatcher.disconnect(self.hide_busy, signal=NOT_BUSY_SIGNAL) dispatcher.disconnect(self.completed, signal=ANALYSIS_DONE_SIGNAL) dispatcher.disconnect(self.show_dynamic_message, signal=DYNAMIC_MESSAGE_SIGNAL) def show_static_message(self, message): """Send a static message to the message viewer. Static messages cause any previous content in the MessageViewer to be replaced with new content. .. note:: Copied from the dock :param message: An instance of our rich message class. :type message: Message """ dispatcher.send(signal=STATIC_MESSAGE_SIGNAL, sender=self, message=message) def show_dynamic_message(self, sender, message): """Send a dynamic message to the message viewer. Dynamic messages are appended to any existing content in the MessageViewer. .. note:: Modified from the dock :param sender: The object that sent the message. :type sender: Object, None :param message: An instance of our rich message class. :type message: Message """ # TODO Hardcoded step - may overflow, if number of messages increase self.parent.pbProgress.setValue(self.parent.pbProgress.value() + 15) self.parent.wvResults.dynamic_message_event(sender, message) def show_error_message(self, error_message): """Send an error message to the message viewer. Error messages cause any previous content in the MessageViewer to be replaced with new content. .. note:: Copied from the dock :param error_message: An instance of our rich error message class. :type error_message: ErrorMessage """ dispatcher.send(signal=ERROR_MESSAGE_SIGNAL, sender=self, message=error_message) self.hide_busy() def read_settings(self): """Restore settings from QSettings. Do this on init and after changing options in the options dialog. """ settings = QSettings() flag = bool(settings.value('inasafe/showRubberBands', False, type=bool)) self.extent.show_rubber_bands = flag try: extent = settings.value('inasafe/analysis_extent', '', type=str) crs = settings.value('inasafe/analysis_extent_crs', '', type=str) except TypeError: # Any bogus stuff in settings and we just clear them extent = '' crs = '' if extent != '' and crs != '': extent = extent_string_to_array(extent) try: self.extent.user_extent = QgsRectangle(*extent) self.extent.user_extent_crs = QgsCoordinateReferenceSystem(crs) self.extent.show_user_analysis_extent() except TypeError: self.extent.user_extent = None self.extent.user_extent_crs = None flag = settings.value('inasafe/useThreadingFlag', False, type=bool) self.run_in_thread_flag = flag flag = settings.value('inasafe/setZoomToImpactFlag', True, type=bool) self.zoom_to_impact_flag = flag # whether exposure layer should be hidden after model completes flag = settings.value('inasafe/setHideExposureFlag', False, type=bool) self.hide_exposure_flag = flag # whether to 'hard clip' layers (e.g. cut buildings in half if they # lie partially in the AOI self.clip_hard = settings.value('inasafe/clip_hard', False, type=bool) # whether to show or not postprocessing generated layers self.show_intermediate_layers = settings.value( 'inasafe/show_intermediate_layers', False, type=bool) # whether to show or not dev only options self.developer_mode = settings.value('inasafe/developer_mode', False, type=bool) # whether to show or not a custom Logo self.organisation_logo_path = settings.value( 'inasafe/organisation_logo_path', default_organisation_logo_path(), type=str) flag = bool( settings.value('inasafe/showOrganisationLogoInDockFlag', True, type=bool)) def show_busy(self): """Lock buttons and enable the busy cursor.""" self.parent.pbnNext.setEnabled(False) self.parent.pbnBack.setEnabled(False) self.parent.pbnCancel.setEnabled(False) QtGui.qApp.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor)) self.parent.repaint() QtGui.qApp.processEvents() def hide_busy(self): """Unlock buttons A helper function to indicate processing is done.""" self.parent.pbnNext.setEnabled(True) self.parent.pbnBack.setEnabled(True) self.parent.pbnCancel.setEnabled(True) self.parent.repaint() QtGui.qApp.restoreOverrideCursor() def analysis_error(self, exception, message): """A helper to spawn an error and halt processing. An exception will be logged, busy status removed and a message displayed. .. note:: Copied from the dock :param message: an ErrorMessage to display :type message: ErrorMessage, Message :param exception: An exception that was raised :type exception: Exception """ self.hide_busy() LOGGER.exception(message) message = get_error_message(exception, context=message) self.show_error_message(message) self.analysisDone.emit(False) def setup_and_run_analysis(self): """Setup and execute the analysis""" self.enable_signal_receiver() self.show_busy() self.init_analysis() try: self.analysis.setup_analysis() except InsufficientOverlapError as e: raise e self.extent.show_last_analysis_extent(self.analysis.clip_parameters[1]) # Start the analysis self.analysis.run_analysis() self.disable_signal_receiver() def init_analysis(self): """Setup analysis to make it ready to work. .. note:: Copied or adapted from the dock """ self.analysis = Analysis() # Layers self.analysis.hazard_layer = self.parent.hazard_layer self.analysis.exposure_layer = self.parent.exposure_layer self.analysis.aggregation_layer = self.parent.aggregation_layer # TODO test if the implement aggregation layer works! # noinspection PyTypeChecker self.analysis.hazard_keyword = self.keyword_io.read_keywords( self.parent.hazard_layer) self.analysis.exposure_keyword = self.keyword_io.read_keywords( self.parent.exposure_layer) # Need to check since aggregation layer is not mandatory if self.analysis.aggregation_layer: self.analysis.aggregation_keyword = self.keyword_io.read_keywords( self.parent.aggregation_layer) # Impact Function self.analysis.impact_function_id = self.parent.selected_function( )['id'] self.analysis.impact_function_parameters = self.parent.if_params # Variables self.analysis.clip_hard = self.clip_hard self.analysis.show_intermediate_layers = self.show_intermediate_layers self.analysis.run_in_thread_flag = self.run_in_thread_flag self.analysis.map_canvas = self.iface.mapCanvas() # Extent if self.parent.rbExtentUser.isChecked(): self.analysis.user_extent = self.extent.user_extent else: self.analysis.user_extent = None self.analysis.user_extent_crs = self.extent.user_extent_crs self.analysis.clip_to_viewport = self.parent.rbExtentScreen.isChecked() def completed(self): """Slot activated when the process is done. .. note:: Adapted from the dock """ # Try to run completion code try: from datetime import datetime LOGGER.debug(datetime.now()) LOGGER.debug('get engine impact layer') LOGGER.debug(self.analysis is None) engine_impact_layer = self.analysis.get_impact_layer() # Load impact layer into QGIS qgis_impact_layer = read_impact_layer(engine_impact_layer) report = self.show_results(qgis_impact_layer, engine_impact_layer) except Exception, e: # pylint: disable=W0703 # FIXME (Ole): This branch is not covered by the tests self.analysis_error(e, self.tr('Error loading impact layer.')) else: