class DebugVSPlugin(QObject): def __init__(self, iface): super().__init__() self.iface = iface self.ptvsd = None try: import ptvsd self.ptvsd = ptvsd except: pass self.port = 5678 self.host = 'localhost' self.actionsScript = [] self.toolButton = QToolButton() self.toolButton.setMenu(QMenu()) self.toolButton.setPopupMode(QToolButton.MenuButtonPopup) self.toolBtnAction = self.iface.addToolBarWidget(self.toolButton) self.msgBar = iface.messageBar() self.pluginName = 'DebugVS' self.nameActionEnable = 'Enable Debug for Visual Studio' self.action = None # Check exist sys.argv - /ptvsd/.../pydevd_process_net_command if not hasattr(sys, 'argv'): sys.argv = [] def initGui(self): # Action Run icon = QIcon(os.path.join(os.path.dirname(__file__), 'code.svg')) self.actionEnable = QAction(icon, self.nameActionEnable, self.iface.mainWindow()) self.actionEnable.setToolTip(self.nameActionEnable) self.actionEnable.triggered.connect(self.enable) self.iface.addPluginToMenu(f"&{self.nameActionEnable}", self.actionEnable) # Action Load Script title = 'Load script' icon = QgsApplication.getThemeIcon('mActionScriptOpen.svg') self.actionLoad = QAction(icon, title, self.iface.mainWindow()) self.actionLoad.setToolTip(title) self.actionLoad.triggered.connect(self.load) self.iface.addPluginToMenu(f"&{self.nameActionEnable}", self.actionLoad) # m = self.toolButton.menu() m.addAction(self.actionEnable) m.addAction(self.actionLoad) self.toolButton.setDefaultAction(self.actionEnable) def unload(self): for action in [self.actionEnable, self.actionLoad ] + self.actionsScript: self.iface.removePluginMenu(f"&{self.nameActionEnable}", action) self.iface.removeToolBarIcon(action) self.iface.unregisterMainWindowAction(action) self.iface.removeToolBarIcon(self.toolBtnAction) def _addActionScript(self, filename): icon = QgsApplication.getThemeIcon('processingScript.svg') title = os.path.split(filename)[-1] action = QAction(icon, title, self.iface.mainWindow()) action.setToolTip(filename) action.triggered.connect(self.run) m = self.toolButton.menu() m.addAction(action) self.actionsScript.append(action) def _existsActionScript(self, filename): filenames = [a.toolTip() for a in self.actionsScript] return filename in filenames def _checkEnable(self): if not self.ptvsd.is_attached(): self.msgBar.popWidget() msg = f"{self.nameActionEnable} AND attach in Visual Studio Code" self.msgBar.pushWarning(self.pluginName, msg) return False return True @pyqtSlot(bool) def enable(self, checked): self.msgBar.popWidget() if self.ptvsd is None: self.msgBar.pushCritical(self.pluginName, "Need install ptvsd: pip3 install ptvsd") return msgPort = f'"request": "attach", "Port": {self.port}, "host": "{self.host}"' if self.ptvsd.is_attached(): self.msgBar.pushWarning( self.pluginName, f"Remote Debug for Visual Studio is active({msgPort})") return t_, self.port = self.ptvsd.enable_attach(address=(self.host, self.port)) msgPort = f'"request": "attach", "Port": {self.port}, "host": "{self.host}"' self.msgBar.pushInfo( self.pluginName, f"Remote Debug for Visual Studio is running({msgPort})") @pyqtSlot(bool) def load(self, checked): if not self._checkEnable(): return filename, _ = QFileDialog.getOpenFileName(None, 'Debug script', '', 'Python Files (*.py)') if not filename: return self.ptvsd.wait_for_attach() execfile_(filename) if not self._existsActionScript(filename): self._addActionScript(filename) @pyqtSlot(bool) def run(self, checked): if not self._checkEnable(): return action = self.sender() filename = action.toolTip() self.ptvsd.wait_for_attach() execfile_(filename)
class Plugin(): """The QGIS interface implementation for the InaSAFE plugin. This class acts as the 'glue' between QGIS and our custom logic. It creates a toolbar and menu bar entry and launches the InaSAFE user interface if these are activated. """ def __init__(self, iface): """Class constructor. On instantiation, the plugin instance will be assigned a copy of the QGIS iface object which will allow this plugin to access and manipulate the running QGIS instance that spawned it. :param iface:Quantum GIS iface instance. This instance is automatically passed to the plugin by QGIS when it loads the plugin. :type iface: QgisAppInterface """ # Save reference to the QGIS interface self.iface = iface self.dock_widget = None # Actions self.action_add_layers = None self.action_add_osm_layer = None self.action_add_petabencana_layer = None self.action_batch_runner = None self.action_dock = None self.action_extent_selector = None self.action_field_mapping = None self.action_multi_exposure = None self.action_function_centric_wizard = None self.action_import_dialog = None self.action_keywords_wizard = None self.action_minimum_needs = None self.action_minimum_needs_config = None self.action_multi_buffer = None self.action_options = None self.action_run_tests = None self.action_save_scenario = None self.action_shake_converter = None self.action_show_definitions = None self.action_toggle_rubberbands = None self.action_metadata_converter = None self.translator = None self.toolbar = None self.wizard = None self.actions = [] # list of all QActions we create for InaSAFE self.message_bar_item = None # Flag indicating if toolbar should show only common icons or not self.full_toolbar = False # print self.tr('InaSAFE') # For enable/disable the keyword editor icon self.iface.currentLayerChanged.connect(self.layer_changed) developer_mode = setting('developer_mode', False, expected_type=bool) self.hide_developer_buttons = (inasafe_release_status == 'final' and not developer_mode) # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('Plugin', message) def add_action(self, action, add_to_toolbar=True, add_to_legend=False): """Add a toolbar icon to the InaSAFE toolbar. :param action: The action that should be added to the toolbar. :type action: QAction :param add_to_toolbar: Flag indicating whether the action should also be added to the InaSAFE toolbar. Defaults to True. :type add_to_toolbar: bool :param add_to_legend: Flag indicating whether the action should also be added to the layer legend menu. Default to False. :type add_to_legend: bool """ # store in the class list of actions for easy plugin unloading self.actions.append(action) self.iface.addPluginToMenu(self.tr('InaSAFE'), action) if add_to_toolbar: self.toolbar.addAction(action) if add_to_legend: # The id is the action name without spaces, tabs ... self.iface.addCustomActionForLayerType(action, self.tr('InaSAFE'), QgsMapLayer.VectorLayer, True) self.iface.addCustomActionForLayerType(action, self.tr('InaSAFE'), QgsMapLayer.RasterLayer, True) def _create_dock_toggle_action(self): """Create action for plugin dockable window (show/hide).""" # pylint: disable=W0201 icon = resources_path('img', 'icons', 'icon.svg') self.action_dock = QAction(QIcon(icon), self.tr('Toggle InaSAFE Dock'), self.iface.mainWindow()) self.action_dock.setObjectName('InaSAFEDockToggle') self.action_dock.setStatusTip(self.tr('Show/hide InaSAFE dock widget')) self.action_dock.setWhatsThis(self.tr('Show/hide InaSAFE dock widget')) self.action_dock.setCheckable(True) self.action_dock.setChecked(True) self.action_dock.triggered.connect(self.toggle_dock_visibility) self.add_action(self.action_dock) # -------------------------------------- # Create action for keywords creation wizard # ------------------------------------- def _create_keywords_wizard_action(self): """Create action for keywords creation wizard.""" icon = resources_path('img', 'icons', 'show-keyword-wizard.svg') self.action_keywords_wizard = QAction( QIcon(icon), self.tr('Keywords Creation Wizard'), self.iface.mainWindow()) self.action_keywords_wizard.setStatusTip( self.tr('Open InaSAFE keywords creation wizard')) self.action_keywords_wizard.setWhatsThis( self.tr('Open InaSAFE keywords creation wizard')) self.action_keywords_wizard.setEnabled(False) self.action_keywords_wizard.triggered.connect( self.show_keywords_wizard) self.add_action(self.action_keywords_wizard, add_to_legend=True) def _create_analysis_wizard_action(self): """Create action for IF-centric wizard.""" icon = resources_path('img', 'icons', 'show-wizard.svg') self.action_function_centric_wizard = QAction( QIcon(icon), self.tr('Impact Function Centric Wizard'), self.iface.mainWindow()) self.action_function_centric_wizard.setStatusTip( self.tr('Open InaSAFE impact function centric wizard')) self.action_function_centric_wizard.setWhatsThis( self.tr('Open InaSAFE impact function centric wizard')) self.action_function_centric_wizard.setEnabled(True) self.action_function_centric_wizard.triggered.connect( self.show_function_centric_wizard) self.add_action(self.action_function_centric_wizard) def _create_options_dialog_action(self): """Create action for options dialog.""" icon = resources_path('img', 'icons', 'configure-inasafe.svg') self.action_options = QAction(QIcon(icon), self.tr('Options'), self.iface.mainWindow()) self.action_options.setStatusTip( self.tr('Open InaSAFE options dialog')) self.action_options.setWhatsThis( self.tr('Open InaSAFE options dialog')) self.action_options.triggered.connect(self.show_options) self.add_action(self.action_options, add_to_toolbar=self.full_toolbar) def _create_minimum_needs_action(self): """Create action for minimum needs dialog.""" icon = resources_path('img', 'icons', 'show-minimum-needs.svg') self.action_minimum_needs = QAction( QIcon(icon), self.tr('Minimum Needs Calculator'), self.iface.mainWindow()) self.action_minimum_needs.setStatusTip( self.tr('Open InaSAFE minimum needs calculator')) self.action_minimum_needs.setWhatsThis( self.tr('Open InaSAFE minimum needs calculator')) self.action_minimum_needs.triggered.connect(self.show_minimum_needs) self.add_action(self.action_minimum_needs, add_to_toolbar=self.full_toolbar) def _create_multi_buffer_action(self): """Create action for multi buffer dialog.""" icon = resources_path('img', 'icons', 'show-multi-buffer.svg') self.action_multi_buffer = QAction(QIcon(icon), self.tr('Multi Buffer'), self.iface.mainWindow()) self.action_multi_buffer.setStatusTip( self.tr('Open InaSAFE multi buffer')) self.action_multi_buffer.setWhatsThis( self.tr('Open InaSAFE multi buffer')) self.action_multi_buffer.triggered.connect(self.show_multi_buffer) self.add_action(self.action_multi_buffer, add_to_toolbar=self.full_toolbar) def _create_minimum_needs_options_action(self): """Create action for global minimum needs dialog.""" icon = resources_path('img', 'icons', 'show-global-minimum-needs.svg') self.action_minimum_needs_config = QAction( QIcon(icon), self.tr('Minimum Needs Configuration'), self.iface.mainWindow()) self.action_minimum_needs_config.setStatusTip( self.tr('Open InaSAFE minimum needs configuration')) self.action_minimum_needs_config.setWhatsThis( self.tr('Open InaSAFE minimum needs configuration')) self.action_minimum_needs_config.triggered.connect( self.show_minimum_needs_configuration) self.add_action(self.action_minimum_needs_config, add_to_toolbar=self.full_toolbar) def _create_shakemap_converter_action(self): """Create action for converter dialog.""" icon = resources_path('img', 'icons', 'show-converter-tool.svg') self.action_shake_converter = QAction(QIcon(icon), self.tr('Shakemap Converter'), self.iface.mainWindow()) self.action_shake_converter.setStatusTip( self.tr('Open InaSAFE Converter')) self.action_shake_converter.setWhatsThis( self.tr('Open InaSAFE Converter')) self.action_shake_converter.triggered.connect( self.show_shakemap_importer) self.add_action(self.action_shake_converter, add_to_toolbar=self.full_toolbar) def _create_batch_runner_action(self): """Create action for batch runner dialog.""" icon = resources_path('img', 'icons', 'show-batch-runner.svg') self.action_batch_runner = QAction(QIcon(icon), self.tr('Batch Runner'), self.iface.mainWindow()) self.action_batch_runner.setStatusTip(self.tr('Open Batch Runner')) self.action_batch_runner.setWhatsThis(self.tr('Open Batch Runner')) self.action_batch_runner.triggered.connect(self.show_batch_runner) self.add_action(self.action_batch_runner, add_to_toolbar=self.full_toolbar) def _create_save_scenario_action(self): """Create action for save scenario dialog.""" icon = resources_path('img', 'icons', 'save-as-scenario.svg') self.action_save_scenario = QAction(QIcon(icon), self.tr('Save Current Scenario'), self.iface.mainWindow()) message = self.tr('Save current scenario to text file') self.action_save_scenario.setStatusTip(message) self.action_save_scenario.setWhatsThis(message) # noinspection PyUnresolvedReferences self.action_save_scenario.triggered.connect(self.save_scenario) self.add_action(self.action_save_scenario, add_to_toolbar=self.full_toolbar) def _create_osm_downloader_action(self): """Create action for import OSM Dialog.""" icon = resources_path('img', 'icons', 'show-osm-download.svg') self.action_import_dialog = QAction( QIcon(icon), self.tr('OpenStreetMap Downloader'), self.iface.mainWindow()) self.action_import_dialog.setStatusTip( self.tr('OpenStreetMap Downloader')) self.action_import_dialog.setWhatsThis( self.tr('OpenStreetMap Downloader')) self.action_import_dialog.triggered.connect(self.show_osm_downloader) self.add_action(self.action_import_dialog, add_to_toolbar=True) def _create_geonode_uploader_action(self): """Create action for Geonode uploader dialog.""" icon = resources_path('img', 'icons', 'geonode.png') label = tr('Geonode Uploader') self.action_geonode = QAction(QIcon(icon), label, self.iface.mainWindow()) self.action_geonode.setStatusTip(label) self.action_geonode.setWhatsThis(label) self.action_geonode.triggered.connect(self.show_geonode_uploader) self.add_action(self.action_geonode, add_to_toolbar=False) def _create_add_osm_layer_action(self): """Create action for import OSM Dialog.""" icon = resources_path('img', 'icons', 'add-osm-tiles-layer.svg') self.action_add_osm_layer = QAction( QIcon(icon), self.tr('Add OpenStreetMap Tile Layer'), self.iface.mainWindow()) self.action_add_osm_layer.setStatusTip( self.tr('Add OpenStreetMap Tile Layer')) self.action_add_osm_layer.setWhatsThis( self.tr('Use this to add an OSM layer to your map. ' 'It needs internet access to function.')) self.action_add_osm_layer.triggered.connect(self.add_osm_layer) self.add_action(self.action_add_osm_layer, add_to_toolbar=True) def _create_show_definitions_action(self): """Create action for showing definitions / help.""" icon = resources_path('img', 'icons', 'show-inasafe-help.svg') self.action_show_definitions = QAction(QIcon(icon), self.tr('InaSAFE Help'), self.iface.mainWindow()) self.action_show_definitions.setStatusTip(self.tr('Show InaSAFE Help')) self.action_show_definitions.setWhatsThis( self. tr('Use this to show a document describing all InaSAFE concepts.')) self.action_show_definitions.triggered.connect(self.show_definitions) self.add_action(self.action_show_definitions, add_to_toolbar=True) def _create_metadata_converter_action(self): """Create action for showing metadata converter dialog.""" icon = resources_path('img', 'icons', 'show-metadata-converter.svg') self.action_metadata_converter = QAction( QIcon(icon), self.tr('InaSAFE Metadata Converter'), self.iface.mainWindow()) self.action_metadata_converter.setStatusTip( self.tr('Convert metadata from version 4.3 to version 3.5.')) self.action_metadata_converter.setWhatsThis( self.tr('Use this tool to convert metadata 4.3 to version 3.5')) self.action_metadata_converter.triggered.connect( self.show_metadata_converter) self.add_action(self.action_metadata_converter, add_to_toolbar=self.full_toolbar) def _create_field_mapping_action(self): """Create action for showing field mapping dialog.""" icon = resources_path('img', 'icons', 'show-mapping-tool.svg') self.action_field_mapping = QAction( QIcon(icon), self.tr('InaSAFE Field Mapping Tool'), self.iface.mainWindow()) self.action_field_mapping.setStatusTip( self.tr('Assign field mapping to layer.')) self.action_field_mapping.setWhatsThis( self.tr('Use this tool to assign field mapping in layer.')) self.action_field_mapping.setEnabled(False) self.action_field_mapping.triggered.connect(self.show_field_mapping) self.add_action(self.action_field_mapping, add_to_toolbar=self.full_toolbar) def _create_multi_exposure_action(self): """Create action for showing the multi exposure tool.""" self.action_multi_exposure = QAction( QIcon(resources_path('img', 'icons', 'show-multi-exposure.svg')), self.tr('InaSAFE Multi Exposure Tool'), self.iface.mainWindow()) self.action_multi_exposure.setStatusTip( self.tr('Open the multi exposure tool.')) self.action_multi_exposure.setWhatsThis( self.tr('Open the multi exposure tool.')) self.action_multi_exposure.setEnabled(True) self.action_multi_exposure.triggered.connect(self.show_multi_exposure) self.add_action(self.action_multi_exposure, add_to_toolbar=self.full_toolbar) def _create_add_petabencana_layer_action(self): """Create action for import OSM Dialog.""" icon = resources_path('img', 'icons', 'add-petabencana-layer.svg') self.action_add_petabencana_layer = QAction( QIcon(icon), self.tr('Add PetaBencana Flood Layer'), self.iface.mainWindow()) self.action_add_petabencana_layer.setStatusTip( self.tr('Add PetaBencana Flood Layer')) self.action_add_petabencana_layer.setWhatsThis( self.tr('Use this to add a PetaBencana layer to your map. ' 'It needs internet access to function.')) self.action_add_petabencana_layer.triggered.connect( self.add_petabencana_layer) self.add_action(self.action_add_petabencana_layer, add_to_toolbar=self.full_toolbar) def _create_rubber_bands_action(self): """Create action for toggling rubber bands.""" icon = resources_path('img', 'icons', 'toggle-rubber-bands.svg') self.action_toggle_rubberbands = QAction( QIcon(icon), self.tr('Toggle Scenario Outlines'), self.iface.mainWindow()) message = self.tr('Toggle rubber bands showing scenario extents.') self.action_toggle_rubberbands.setStatusTip(message) self.action_toggle_rubberbands.setWhatsThis(message) # Set initial state self.action_toggle_rubberbands.setCheckable(True) flag = setting('showRubberBands', False, expected_type=bool) self.action_toggle_rubberbands.setChecked(flag) # noinspection PyUnresolvedReferences self.action_toggle_rubberbands.triggered.connect( self.dock_widget.toggle_rubber_bands) self.add_action(self.action_toggle_rubberbands) def _create_analysis_extent_action(self): """Create action for analysis extent dialog.""" icon = resources_path('img', 'icons', 'set-extents-tool.svg') self.action_extent_selector = QAction(QIcon(icon), self.tr('Set Analysis Area'), self.iface.mainWindow()) self.action_extent_selector.setStatusTip( self.tr('Set the analysis area for InaSAFE')) self.action_extent_selector.setWhatsThis( self.tr('Set the analysis area for InaSAFE')) self.action_extent_selector.triggered.connect( self.show_extent_selector) self.add_action(self.action_extent_selector) def _create_test_layers_action(self): """Create action for adding layers (developer mode, non final only).""" if self.hide_developer_buttons: return icon = resources_path('img', 'icons', 'add-test-layers.svg') self.action_add_layers = QAction(QIcon(icon), self.tr('Add Test Layers'), self.iface.mainWindow()) self.action_add_layers.setStatusTip(self.tr('Add test layers')) self.action_add_layers.setWhatsThis(self.tr('Add test layers')) self.action_add_layers.triggered.connect(self.add_test_layers) self.add_action(self.action_add_layers) def _create_run_test_action(self): """Create action for running tests (developer mode, non final only).""" if self.hide_developer_buttons: return default_package = str(setting('testPackage', 'safe', expected_type=str)) msg = self.tr('Run tests in %s' % default_package) self.test_button = QToolButton() self.test_button.setMenu(QMenu()) self.test_button.setPopupMode(QToolButton.MenuButtonPopup) icon = resources_path('img', 'icons', 'run-tests.svg') self.action_run_tests = QAction(QIcon(icon), msg, self.iface.mainWindow()) self.action_run_tests.setStatusTip(msg) self.action_run_tests.setWhatsThis(msg) self.action_run_tests.triggered.connect(self.run_tests) self.test_button.menu().addAction(self.action_run_tests) self.test_button.setDefaultAction(self.action_run_tests) self.action_select_package = QAction(QIcon(icon), self.tr('Select package'), self.iface.mainWindow()) self.action_select_package.setStatusTip(self.tr('Select Test Package')) self.action_select_package.setWhatsThis(self.tr('Select Test Package')) self.action_select_package.triggered.connect(self.select_test_package) self.test_button.menu().addAction(self.action_select_package) self.toolbar.addWidget(self.test_button) self.add_action(self.action_run_tests, add_to_toolbar=False) self.add_action(self.action_select_package, add_to_toolbar=False) def _create_dock(self): """Create dockwidget and tabify it with the legend.""" # Import dock here as it needs to be imported AFTER i18n is set up from safe.gui.widgets.dock import Dock self.dock_widget = Dock(self.iface) self.dock_widget.setObjectName('InaSAFE-Dock') self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock_widget) legend_tab = self.iface.mainWindow().findChild(QApplication, 'Legend') if legend_tab: self.iface.mainWindow().tabifyDockWidget(legend_tab, self.dock_widget) self.dock_widget.raise_() # noinspection PyPep8Naming def initGui(self): """Gui initialisation procedure (for QGIS plugin api). .. note:: Don't change the name of this method from initGui! This method is called by QGIS and should be used to set up any graphical user interface elements that should appear in QGIS by default (i.e. before the user performs any explicit action with the plugin). """ self.toolbar = self.iface.addToolBar('InaSAFE') self.toolbar.setObjectName('InaSAFEToolBar') self.dock_widget = None # Now create the actual dock self._create_dock() # And all the menu actions # Configuration Group self._create_dock_toggle_action() self._create_options_dialog_action() self._create_minimum_needs_options_action() self._create_analysis_extent_action() self._create_rubber_bands_action() self._add_spacer_to_menu() self._create_keywords_wizard_action() self._create_analysis_wizard_action() self._add_spacer_to_menu() self._create_field_mapping_action() self._create_multi_exposure_action() self._create_metadata_converter_action() self._create_osm_downloader_action() self._create_add_osm_layer_action() self._create_add_petabencana_layer_action() self._create_geonode_uploader_action() self._create_shakemap_converter_action() self._create_minimum_needs_action() self._create_multi_buffer_action() self._create_test_layers_action() self._create_run_test_action() self._add_spacer_to_menu() self._create_batch_runner_action() self._create_save_scenario_action() self._add_spacer_to_menu() self._create_show_definitions_action() # Hook up a slot for when the dock is hidden using its close button # or view-panels # self.dock_widget.visibilityChanged.connect(self.toggle_inasafe_action) # Also deal with the fact that on start of QGIS dock may already be # hidden. self.action_dock.setChecked(self.dock_widget.isVisible()) self.iface.initializationCompleted.connect( partial(self.show_welcome_message)) def _add_spacer_to_menu(self): """Create a spacer to the menu to separate action groups.""" separator = QAction(self.iface.mainWindow()) separator.setSeparator(True) self.iface.addPluginToMenu(self.tr('InaSAFE'), separator) @staticmethod def clear_modules(): """Unload inasafe functions and try to return QGIS to before InaSAFE. .. todo:: I think this function can be removed. TS. """ # next lets force remove any inasafe related modules modules = [] for module in sys.modules: if 'inasafe' in module: # Check if it is really one of our modules i.e. exists in the # plugin directory tokens = module.split('.') path = '' for myToken in tokens: path += os.path.sep + myToken parent = os.path.abspath( os.path.join(__file__, os.path.pardir, os.path.pardir)) full_path = os.path.join(parent, path + '.py') if os.path.exists(os.path.abspath(full_path)): LOGGER.debug('Removing: %s' % module) modules.append(module) for module in modules: del (sys.modules[module]) for module in sys.modules: if 'inasafe' in module: print(module) # Lets also clean up all the path additions that were made package_path = os.path.abspath( os.path.join(os.path.dirname(__file__), os.path.pardir)) LOGGER.debug('Path to remove: %s' % package_path) # We use a list comprehension to ensure duplicate entries are removed LOGGER.debug(sys.path) sys.path = [y for y in sys.path if package_path not in y] LOGGER.debug(sys.path) def unload(self): """GUI breakdown procedure (for QGIS plugin api). .. note:: Don't change the name of this method from unload! This method is called by QGIS and should be used to *remove* any graphical user interface elements that should appear in QGIS. """ # Remove the plugin menu item and icon if self.wizard: self.wizard.deleteLater() for myAction in self.actions: self.iface.removePluginMenu(self.tr('InaSAFE'), myAction) self.iface.removeToolBarIcon(myAction) self.iface.removeCustomActionForLayerType(myAction) self.iface.mainWindow().removeDockWidget(self.dock_widget) self.iface.mainWindow().removeToolBar(self.toolbar) self.dock_widget.setVisible(False) self.dock_widget.destroy() self.iface.currentLayerChanged.disconnect(self.layer_changed) # Unload QGIS expressions loaded by the plugin. for qgis_expression in list(qgis_expressions().keys()): QgsExpression.unregisterFunction(qgis_expression) def toggle_inasafe_action(self, checked): """Check or un-check the toggle inaSAFE toolbar button. This slot is called when the user hides the inaSAFE panel using its close button or using view->panels. :param checked: True if the dock should be shown, otherwise False. :type checked: bool """ self.action_dock.setChecked(checked) # Run method that performs all the real work def toggle_dock_visibility(self): """Show or hide the dock widget.""" if self.dock_widget.isVisible(): self.dock_widget.setVisible(False) else: self.dock_widget.setVisible(True) self.dock_widget.raise_() def add_test_layers(self): """Add standard test layers.""" from safe.test.utilities import load_standard_layers load_standard_layers() rect = QgsRectangle(106.806, -6.195, 106.837, -6.167) self.iface.mapCanvas().setExtent(rect) def select_test_package(self): """Select the test package.""" default_package = 'safe' user_package = str( setting('testPackage', default_package, expected_type=str)) test_package, _ = QInputDialog.getText( self.iface.mainWindow(), self.tr('Select the python test package'), self.tr('Select the python test package'), QLineEdit.Normal, user_package) if test_package == '': test_package = default_package set_setting('testPackage', test_package) msg = self.tr('Run tests in %s' % test_package) self.action_run_tests.setWhatsThis(msg) self.action_run_tests.setText(msg) def run_tests(self): """Run unit tests in the python console.""" from qgis.PyQt.QtWidgets import QDockWidget main_window = self.iface.mainWindow() action = main_window.findChild(QAction, 'mActionShowPythonDialog') action.trigger() package = str(setting('testPackage', 'safe', expected_type=str)) for child in main_window.findChildren(QDockWidget, 'PythonConsole'): if child.objectName() == 'PythonConsole': child.show() for widget in child.children(): if 'PythonConsoleWidget' in str(widget.__class__): # print "Console widget found" shell = widget.shell shell.runCommand( 'from inasafe.test_suite import test_package') shell.runCommand('test_package(\'%s\')' % package) break def show_extent_selector(self): """Show the extent selector widget for defining analysis extents.""" # import here only so that it is AFTER i18n set up from safe.gui.tools.extent_selector_dialog import ExtentSelectorDialog widget = ExtentSelectorDialog( self.iface, self.iface.mainWindow(), extent=self.dock_widget.extent.user_extent, crs=self.dock_widget.extent.crs) widget.clear_extent.connect( self.dock_widget.extent.clear_user_analysis_extent) widget.extent_defined.connect( self.dock_widget.define_user_analysis_extent) # This ensures that run button state is updated on dialog close widget.extent_selector_closed.connect( self.dock_widget.validate_impact_function) # Needs to be non modal to support hide -> interact with map -> show widget.show() # non modal def show_minimum_needs(self): """Show the minimum needs dialog.""" # import here only so that it is AFTER i18n set up from safe.gui.tools.minimum_needs.needs_calculator_dialog import ( NeedsCalculatorDialog) dialog = NeedsCalculatorDialog(self.iface.mainWindow()) dialog.exec_() def show_minimum_needs_configuration(self): """Show the minimum needs dialog.""" # import here only so that it is AFTER i18n set up from safe.gui.tools.minimum_needs.needs_manager_dialog import ( NeedsManagerDialog) dialog = NeedsManagerDialog(parent=self.iface.mainWindow(), dock=self.dock_widget) dialog.exec_() # modal def show_options(self): """Show the options dialog.""" # import here only so that it is AFTER i18n set up from safe.gui.tools.options_dialog import OptionsDialog dialog = OptionsDialog(iface=self.iface, parent=self.iface.mainWindow()) dialog.show_option_dialog() if dialog.exec_(): # modal self.dock_widget.read_settings() from safe.gui.widgets.message import getting_started_message send_static_message(self.dock_widget, getting_started_message()) # Issue #4734, make sure to update the combobox after update the # InaSAFE option self.dock_widget.get_layers() def show_welcome_message(self): """Show the welcome message.""" # import here only so that it is AFTER i18n set up from safe.gui.tools.options_dialog import OptionsDialog # Do not show by default show_message = False previous_version = StrictVersion(setting('previous_version')) current_version = StrictVersion(inasafe_version) # Set previous_version to the current inasafe_version set_setting('previous_version', inasafe_version) if setting('always_show_welcome_message', expected_type=bool): # Show if it the setting said so show_message = True elif previous_version < current_version: # Always show if the user installed new version show_message = True # Allow to disable welcome message when running automated tests if os.environ.get('INASAFE_DISABLE_WELCOME_MESSAGE', False): show_message = False if show_message: dialog = OptionsDialog(iface=self.iface, parent=self.iface.mainWindow()) dialog.show_welcome_dialog() if dialog.exec_(): # modal self.dock_widget.read_settings() def show_keywords_wizard(self): """Show the keywords creation wizard.""" # import here only so that it is AFTER i18n set up from safe.gui.tools.wizard.wizard_dialog import WizardDialog if self.iface.activeLayer() is None: return # Don't break an existing wizard session if accidentally clicked if self.wizard and self.wizard.isVisible(): return # Prevent spawning multiple copies since the IFCW is non modal if not self.wizard: self.wizard = WizardDialog(self.iface.mainWindow(), self.iface, self.dock_widget) self.wizard.set_keywords_creation_mode() self.wizard.exec_() # modal def show_function_centric_wizard(self): """Show the function centric wizard.""" # import here only so that it is AFTER i18n set up from safe.gui.tools.wizard.wizard_dialog import WizardDialog # Don't break an existing wizard session if accidentally clicked if self.wizard and self.wizard.isVisible(): return # Prevent spawning multiple copies since it is non modal if not self.wizard: self.wizard = WizardDialog(self.iface.mainWindow(), self.iface, self.dock_widget) self.wizard.set_function_centric_mode() # non-modal in order to hide for selecting user extent self.wizard.show() def show_shakemap_importer(self): """Show the converter dialog.""" # import here only so that it is AFTER i18n set up from safe.gui.tools.shake_grid.shakemap_converter_dialog import ( ShakemapConverterDialog) dialog = ShakemapConverterDialog(self.iface.mainWindow(), self.iface, self.dock_widget) dialog.exec_() # modal def show_multi_buffer(self): """Show the multi buffer tool.""" from safe.gui.tools.multi_buffer_dialog import (MultiBufferDialog) dialog = MultiBufferDialog(self.iface.mainWindow(), self.iface, self.dock_widget) dialog.exec_() # modal def show_osm_downloader(self): """Show the OSM buildings downloader dialog.""" from safe.gui.tools.osm_downloader_dialog import OsmDownloaderDialog dialog = OsmDownloaderDialog(self.iface.mainWindow(), self.iface) # otherwise dialog is never deleted dialog.setAttribute(Qt.WA_DeleteOnClose, True) dialog.show() # non modal def show_geonode_uploader(self): """Show the Geonode uploader dialog.""" from safe.gui.tools.geonode_uploader import GeonodeUploaderDialog dialog = GeonodeUploaderDialog(self.iface.mainWindow()) dialog.show() # non modal def add_osm_layer(self): """Add OSM tile layer to the map. This uses a gdal wrapper around the OSM tile service - see the WorldOSM.gdal file for how it is constructed. """ path = resources_path('osm', 'WorldOSM.gdal') layer = QgsRasterLayer(path, self.tr('OpenStreetMap')) project = QgsProject.instance() # Try to add it as the last layer in the list # False flag prevents layer being added to legend project.addMapLayer(layer, False) root = QgsProject.instance().layerTreeRoot() index = len(root.findLayers()) + 1 # LOGGER.info('Inserting layer %s at position %s' % ( # layer.source(), index)) root.insertLayer(index, layer) project.addMapLayer(layer) def show_definitions(self): """Show InaSAFE Definitions (a report showing all key metadata).""" from safe.utilities.help import show_help from safe.gui.tools.help import definitions_help show_help(definitions_help.definitions_help()) def show_field_mapping(self): """Show InaSAFE Field Mapping.""" from safe.gui.tools.field_mapping_dialog import FieldMappingDialog dialog = FieldMappingDialog( parent=self.iface.mainWindow(), iface=self.iface, ) if dialog.exec_(): # modal LOGGER.debug('Show field mapping accepted') self.dock_widget.layer_changed(self.iface.activeLayer()) else: LOGGER.debug('Show field mapping not accepted') def show_metadata_converter(self): """Show InaSAFE Metadata Converter.""" from safe.gui.tools.metadata_converter_dialog import ( MetadataConverterDialog) dialog = MetadataConverterDialog( parent=self.iface.mainWindow(), iface=self.iface, ) dialog.exec_() def show_multi_exposure(self): """Show InaSAFE Multi Exposure.""" from safe.gui.tools.multi_exposure_dialog import MultiExposureDialog dialog = MultiExposureDialog(self.iface.mainWindow(), self.iface) dialog.exec_() # modal def add_petabencana_layer(self): """Add petabencana layer to the map. This uses the PetaBencana API to fetch the latest floods in JK. See https://data.petabencana.id/floods """ from safe.gui.tools.peta_bencana_dialog import PetaBencanaDialog dialog = PetaBencanaDialog(self.iface.mainWindow(), self.iface) dialog.show() # non modal def show_batch_runner(self): """Show the batch runner dialog.""" from safe.gui.tools.batch.batch_dialog import BatchDialog dialog = BatchDialog(parent=self.iface.mainWindow(), iface=self.iface, dock=self.dock_widget) dialog.exec_() # modal def save_scenario(self): """Save current scenario to text file.""" from safe.gui.tools.save_scenario import SaveScenarioDialog dialog = SaveScenarioDialog(iface=self.iface, dock=self.dock_widget) dialog.save_scenario() def layer_changed(self, layer): """Enable or disable keywords editor icon when active layer changes. :param layer: The layer that is now active. :type layer: QgsMapLayer """ if not layer: enable_keyword_wizard = False elif not hasattr(layer, 'providerType'): enable_keyword_wizard = False elif layer.providerType() == 'wms': enable_keyword_wizard = False else: enable_keyword_wizard = True try: if layer: if is_raster_layer(layer): enable_field_mapping_tool = False else: keywords = KeywordIO().read_keywords(layer) keywords_version = keywords.get('keyword_version') if not keywords_version: supported = False else: supported = ( is_keyword_version_supported(keywords_version)) if not supported: enable_field_mapping_tool = False else: layer_purpose = keywords.get('layer_purpose') if not layer_purpose: enable_field_mapping_tool = False else: if layer_purpose == layer_purpose_exposure['key']: layer_subcategory = keywords.get('exposure') elif layer_purpose == layer_purpose_hazard['key']: layer_subcategory = keywords.get('hazard') else: layer_subcategory = None field_groups = get_field_groups( layer_purpose, layer_subcategory) if len(field_groups) == 0: # No field group, disable field mapping tool. enable_field_mapping_tool = False else: enable_field_mapping_tool = True else: enable_field_mapping_tool = False except (KeywordNotFoundError, NoKeywordsFoundError, MetadataReadError): # No keywords, disable field mapping tool. enable_field_mapping_tool = False self.action_keywords_wizard.setEnabled(enable_keyword_wizard) self.action_field_mapping.setEnabled(enable_field_mapping_tool) def shortcut_f7(self): """Executed when user press F7 - will show the shakemap importer.""" self.show_shakemap_importer()
class QWaterPlugin(object): # Store settings in QGIS projects under this key SETTINGS = "QWater" #"ghydraulics""QWater" def __init__(self, iface): # save reference to the QGIS interface self.iface = iface # Create the dialog (after translation) and keep reference self.dlg = QWaterSettingsDialog() #QWater_SettingsDialog() #self.dlg.setWindowModality( Qt.WindowStaysOnTopHint ) #ApplicationModal self.dlg.setWindowFlag(Qt.WindowStaysOnTopHint) self.VazaoClasse = QWater_02Flow() self.common = QWater_00Common() # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) try: pluginMetadata = configparser.ConfigParser() pluginMetadata.read(os.path.join(self.plugin_dir, 'metadata.txt')) self.VERSION = pluginMetadata.get('general', 'version') ''' from qgis.gui import QgsPluginManagerInterface plugInter = iface.pluginManagerInterface() #plugInter.showPluginManager() meta = plugInter.pluginMetadata(self.SETTINGS)#'QWater' self.VERSION = meta['version_installed'] ''' except: self.VERSION = "3.0.0" # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'QWater_{}.qm'.format(locale)) if os.path.exists(locale_path): #se n for pt traduz pra ingles self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) QgsMessageLog.logMessage( 'Qt:{} QWater:{} Lng: {}'.format(qVersion(), self.VERSION, locale), self.SETTINGS, Qgis.Info) def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate(QWaterPlugin.SETTINGS, message) def initGui(self): # create actions defIconPath = ":/plugins/QWater/icons/qwater.svg" self.action = QAction(QIcon(':/plugins/QWater/icons/sizing.svg'), 'Calculate economic diameters', self.iface.mainWindow()) self.action.setWhatsThis( "Calculate economic pipe diameters based on flow data.") self.settingsAction = QAction( QIcon(':/plugins/QWater/icons/00settings.svg'), 'Settings', self.iface.mainWindow()) self.makeModelAction = QAction( QIcon(':/plugins/QWater/icons/makemodel.svg'), 'Make Model', self.iface.mainWindow()) self.fillFieldsAction = QAction( QIcon(':/plugins/QWater/icons/fields_fill.svg'), 'Fill up Fields', self.iface.mainWindow()) self.writeInpAction = QAction( QIcon(':/plugins/QWater/icons/epanet.svg'), 'Write EPANET INP file', self.iface.mainWindow()) self.runEpanetAction = QAction(QIcon(':/plugins/QWater/icons/run.svg'), 'Run EPANET simulation', self.iface.mainWindow()) self.aboutAction = QAction( QIcon(":/plugins/QWater/icons/qwater.svg"), QCoreApplication.translate('GHydraulics', "&About"), self.iface.mainWindow()) self.LoadStylesAction = QAction( QIcon(":/plugins/QWater/icons/style.svg"), "Load default styles", self.iface.mainWindow()) self.GetElevationAction = QAction( QIcon(":/plugins/QWater/icons/getelevation.svg"), "Get Elevation from Raster", self.iface.mainWindow()) self.vazaoAction = QAction(QIcon(':/plugins/QWater/icons/01vazao.svg'), self.tr('Calc Flow'), self.iface.mainWindow()) self.renameAction = QAction( QIcon(':/plugins/QWater/icons/01rename.svg'), 'Renumber Network', self.iface.mainWindow()) self.updateDN_Action = QAction( QIcon(":/plugins/QWater/icons/qwater.svg"), "Update DN field", self.iface.mainWindow()) # Connect actions to triggers self.action.triggered.connect(self.run) self.settingsAction.triggered.connect(self.showSettings) self.makeModelAction.triggered.connect(self.makeModel) self.fillFieldsAction.triggered.connect(self.fillFields) self.writeInpAction.triggered.connect(self.writeInpFile) self.GetElevationAction.triggered.connect(self.GetElevation) self.updateDN_Action.triggered.connect(self.update_DN) self.runEpanetAction.triggered.connect(self.runEpanet) self.aboutAction.triggered.connect(self.about) self.RenameClasse = Rename_Tools(self.iface) self.renameAction.triggered.connect(self.RenameCall) Qwflow = QWater_02Flow().CalcFlow self.vazaoAction.triggered.connect( self.VazaoClasse.CalcFlow ) #self.Vazao #ListNoDemandNodes #QWater_02Flow.CalcFlow self.LoadStylesAction.triggered.connect(self.LoadStyles) #Create toolbar self.toolbar = self.iface.addToolBar('&QWater') self.toolbar.setObjectName('&QWater') # add toolbar buttons self.toolbar.addAction(self.settingsAction) self.toolbar.addAction(self.makeModelAction) self.toolbar.addAction(self.renameAction) self.toolbar.addAction(self.fillFieldsAction) self.toolbar.addAction(self.vazaoAction) self.toolbar.addAction(self.action) self.toolbar.addAction(self.runEpanetAction) #self.iface.addToolBarIcon(self.settingsAction) #Add separator self.toolbar.addSeparator() # Add ToolButton (with Menu) self.menuButton = QToolButton() #QPushButton()#QToolButton self.menuButton.setMenu(QMenu()) self.menuButton.setPopupMode(QToolButton.MenuButtonPopup) # self.menuButton.setAutoRaise(True) m = self.menuButton.menu() m.addAction(self.LoadStylesAction) m.addAction(self.writeInpAction) m.addAction(self.GetElevationAction) m.addAction(self.updateDN_Action) self.menuButton.setDefaultAction(self.GetElevationAction) self.menuButtonAction = self.toolbar.addWidget(self.menuButton) # add menu items self.iface.addPluginToMenu("&QWater", self.settingsAction) self.iface.addPluginToMenu('&QWater', self.makeModelAction) self.iface.addPluginToMenu('&QWater', self.renameAction) self.iface.addPluginToMenu('&QWater', self.fillFieldsAction) self.iface.addPluginToMenu('&QWater', self.vazaoAction) self.iface.addPluginToMenu("&QWater", self.action) self.iface.addPluginToMenu('&QWater', self.runEpanetAction) # tools submenu self.tools_menu = QMenu('&Tools') self.tools_menu.addAction(self.LoadStylesAction) self.tools_menu.addAction(self.writeInpAction) self.tools_menu.addAction(self.GetElevationAction) self.tools_menu.addAction(self.updateDN_Action) # Projects submenu self.project_menu = QMenu('&Projects') self.newProjectAction = QAction(self.iface.actionNewProject().icon(), 'New Project', self.iface.mainWindow()) self.newProjectAction.triggered.connect(self.newProject) self.project_menu.addAction(self.newProjectAction) self.sampleDwCmdAction = QAction( QIcon(':/python/plugins/ghydraulic/icon.xpm'), 'Sample Darcy-Weisbach, cubic meters/day', self.iface.mainWindow()) self.sampleDwCmdAction.triggered.connect(self.openDwCmdSample) self.project_menu.addAction(self.sampleDwCmdAction) self.sampleDwLpsAction = QAction( QIcon(':/python/plugins/ghydraulic/icon.xpm'), 'Sample Darcy-Weisbach, liters/second', self.iface.mainWindow()) self.sampleDwLpsAction.triggered.connect(self.openDwLpsSample) self.project_menu.addAction(self.sampleDwLpsAction) self.sampleHwGpmAction = QAction( QIcon(':/python/plugins/ghydraulic/icon.xpm'), 'Sample Hazen-Williams, gallons/min', self.iface.mainWindow()) self.sampleHwGpmAction.triggered.connect(self.openHwGpmSample) self.project_menu.addAction(self.sampleHwGpmAction) # Back in main menu self.iface.addPluginToMenu("&QWater", self.tools_menu.menuAction()) self.iface.addPluginToMenu("&QWater", self.project_menu.menuAction()) self.iface.addPluginToMenu("&QWater", self.aboutAction) def unload(self): # remove the plugin menu item and icon self.iface.removePluginMenu("&QWater", self.aboutAction) self.iface.removePluginMenu('&QWater', self.runEpanetAction) self.iface.removePluginMenu("&QWater", self.writeInpAction) self.iface.removePluginMenu("&QWater", self.GetElevationAction) self.iface.removePluginMenu("&QWater", self.LoadStylesAction) self.iface.removePluginMenu("&QWater", self.updateDN_Action) self.iface.removePluginMenu('&QWater', self.vazaoAction) self.iface.removePluginMenu('&QWater', self.makeModelAction) self.iface.removePluginMenu('&QWater', self.fillFieldsAction) self.iface.removePluginMenu('&QWater', self.renameAction) self.iface.removePluginMenu("&QWater", self.settingsAction) self.iface.removePluginMenu("&QWater", self.action) self.iface.removePluginMenu("&QWater", self.project_menu.menuAction()) self.iface.removePluginMenu("&QWater", self.tools_menu.menuAction()) self.iface.removeToolBarIcon(self.settingsAction) self.toolbar.removeAction(self.settingsAction) self.toolbar.removeAction(self.LoadStylesAction) self.toolbar.removeAction(self.GetElevationAction) self.toolbar.removeAction(self.updateDN_Action) self.toolbar.removeAction(self.vazaoAction) self.toolbar.removeAction(self.runEpanetAction) self.toolbar.removeAction(self.action) self.toolbar.removeAction(self.renameAction) # remove the toolbar del self.toolbar def RenameCall(self): self.RenameClasse.initGui() def fillFields(self): proj = QgsProject.instance() autolen = proj.readEntry(QWaterPlugin.SETTINGS, 'autolength', "0")[0] undefs = False preencheu = False fillCOLUMNS = { 'JUNCTIONS': { 'ELEVATION': 0 }, 'PIPES': { 'LENGTH': 'CALCULA', 'DIAMETER': 54.6, 'ROUGHNESS': 1, 'MINORLOSS': 0, 'STATUS': 'OPEN' } } for secao in ['PIPES', 'JUNCTIONS']: vLayer = self.common.PegaQWaterLayer(secao) if vLayer != False: feicoes = vLayer.getFeatures() vLayer.startEditing() for feicao in feicoes: camposPadroes = fillCOLUMNS[secao] for (campo, valorPad) in camposPadroes.items(): featVal = feicao[campo] if campo == 'LENGTH' and autolen == '1': ext = feicao.geometry().length() feicao[campo] = ext preencheu = True else: if featVal == NULL or featVal is None: feicao[campo] = valorPad preencheu = True vLayer.updateFeature(feicao) else: undefs = True # self.warning('Fill Fields call not working yet') if not undefs: if preencheu: self.warning('Successfull fill in!', Qgis.Info) else: self.warning('Nothing to fill in!', Qgis.Info) # Calculate economic diameters def run(self): # Check for "LPS" flow units dlg = self.dlg #GHydraulicsSettingsDialog() template = dlg.getTemplate() inp = GHydraulicsInpReader(template) inpunits = inp.getValue('OPTIONS', 'Units').upper() if inpunits != EpanetModel.LPS: self.warning( '"Calculate economic diameters" requires "LPS" flow units instead of "' + inpunits + '". Please change the template INP file.') return maker = GHydraulicsModelMaker(template) self.checkModel(maker) # Let user agree to the change selectedbutton = QMessageBox.question( self.iface.mainWindow(), self.SETTINGS, "This will overwrite all DIAMETER and ROUGHNESS field values using Pipes Table data from settings Dialog. Do you want to continue?", QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) if QMessageBox.Cancel == selectedbutton: return # Execute the action ecodia = GhyEconomicDiameter(GHydraulicsModel.RESULT_FLO, EpanetModel.DIAMETER) maker.beginEditCommand('Calculate economic diameters') try: maker.eachLayer(ecodia.commitEconomicDiametersForLayer, [EpanetModel.PIPES]) except GHydraulicsException as e: self.warning(str(e)) maker.endEditCommand() self.update_DN() # Display the About dialog def about(self): infoString = self.tr( self.SETTINGS + " Plugin " + self.VERSION + "<br />This plugin integrates EPANET with QGIS.<br />Copyright (c) 2017 - 2017 Jorge Almerio<br /><a href=\"https://github.com/jorgealmerio/QWater/blob/master/README.md\">github.com/jorgealmerio/QWater</a>\n" ) QMessageBox.information(self.iface.mainWindow(), "About " + self.SETTINGS, infoString) # Display the settings dialog def showSettings(self): dlg = self.dlg #QWaterSettingsDialog() proj = QgsProject.instance() layers = proj.mapLayers() hasVectorLayers = False msgs = '' QWaterEntries = proj.entryList(QWaterPlugin.SETTINGS, '') GhydraulicsEntries = proj.entryList('ghydraulics', '') # Restore selected layers for secao in EpanetModel.GIS_SECTIONS: #Create QgsMapLayerComboBox filters widget = dlg.findChild(QgsMapLayerComboBox, 'CMB' + secao) if secao in GHydraulicsModel.NODE_SECTIONS: widget.setFilters(QgsMapLayerProxyModel.PointLayer) else: if secao == 'ZONES': widget.setFilters(QgsMapLayerProxyModel.PolygonLayer) else: widget.setFilters(QgsMapLayerProxyModel.LineLayer) widget.setAllowEmptyLayer( True ) #added in QGIS 3.0: Sets whether an optional empty layer ("not set") option is shown in the combo box #Read and restore file Entries if secao in QWaterEntries: lyrName = proj.readEntry(QWaterPlugin.SETTINGS, secao, "")[0] layerEntry = proj.mapLayersByName(lyrName) if layerEntry: widget.setLayer(layerEntry[0]) msgs += '\n{}:{} -> QWater Layer found'.format( secao, lyrName) else: msgs += '\n{}:{} -> QWater Layer NOT found'.format( secao, lyrName) widget.setLayer(None) elif secao in GhydraulicsEntries: #if has Not QWater settings try to get ghydraulics settings pickle_list = str(proj.readEntry('ghydraulics', secao, "")[0]) if '' != pickle_list: # Windows QGIS injects some carriage returns here pickle_list = pickle_list.replace( '\r', '').encode() #use encode convet to byte try: l = loads(pickle_list) valor = l if valor: layerEntry = proj.mapLayersByName(valor[0]) if layerEntry: widget.setLayer(layerEntry[0]) msgs += '\n{}:{} -> GHydraulics Layer found'.format( secao, layerEntry[0].name()) else: widget.setLayer(None) msgs += '\n{}:{} -> GHydraulics Layer NOT found'.format( secao, valor[0]) else: widget.setLayer(None) msgs += '\n{} -> GHydraulics Previous Settings Null'.format( secao) except (KeyError): widget.setLayer(None) # fix_print_with_import print('Error on section:' + secao) continue else: msgs += '\n{} -> Previous Settings NOT found'.format(secao) widget.setLayer(None) #if msgs!='': # print msgs # Restore template inp file template = dlg.getTemplate() dlg.ui.inpFileLineEdit.setText(template) # Restore auto length checkbox dlg.ui.autoLengthCB.setChecked(dlg.getAutoLength()) # Restore backdrop checkbox dlg.ui.writeBackdropCB.setChecked(dlg.getWriteBackdrop()) # Restore data variables dataDefs = QWaterModel.DATA_DEFS for dataDf, defVal in dataDefs.items(): #Get widget widget = dlg.findChild(QLineEdit, 'Txt_' + dataDf) inVar = proj.readEntry(QWaterPlugin.SETTINGS, dataDf, defVal)[0] widget.setText(inVar) #Carrega Tubos tubosMat = proj.readEntry(QWaterPlugin.SETTINGS, "TUBOS_MAT", "0")[0] if tubosMat == '0': #se nao tiver lista de materiais definidas carrega o padrao do modelo tubos = QWaterModel.TUBOS_MAT else: tubos = eval(tubosMat) if not isinstance(tubos[0][0], str): QgsMessageLog.logMessage('Wrong Pipes settings' + tubosMat, self.SETTINGS, Qgis.Warning) #Show Warning Message tubos = QWaterModel.TUBOS_MAT #else: #QgsMessageLog.logMessage('Right Pipes settings'+tubosMat,self.SETTINGS,Qgis.Info) #Show Info Message self.carregaTabMats(tubos) # Exibe o resumo de extensoes de rede se tiver PIPES definido ProjVar = proj.readEntry(QWaterPlugin.SETTINGS, 'PIPES')[0] if ProjVar != '': try: myLayer = proj.mapLayersByName(ProjVar)[0] tot, geo = QWater_00Common().CompRealGeom(myLayer) msgTxt = self.tr( u'<span style=" color:#0000ff;">Geometric Length: {0:.2f} m</span>' ).format(geo) dlg.ui.lbl_extGeo.setText(msgTxt) msgTxt = '' if tot > 0: msgTxt = self.tr( u'<span style=" color:#37c322;">Length Field Sum: {0:.2f} m</span>' ).format(tot) dlg.ui.lbl_extReal.setText(msgTxt) msgTxt = '' except: pass #Call Combobox Zones activated signal to refresh it cmbZones = dlg.ui.CMBZONES cmbZones.activated.emit(cmbZones.currentIndex()) dlg.ui.Txt_POPINI.editingFinished.emit() # show the dialog dlg.show() #for i in range(0, dlg.UNUSED_ITEM+1): # dlg.ui.treeWidget.topLevelItem(i).setExpanded(True) #result = dlg.exec_() result = dlg.exec_() if result: self.accepted() def accepted(self): # Store layers dlg = self.dlg #QWaterSettingsDialog() proj = QgsProject.instance() for secao in EpanetModel.GIS_SECTIONS: #Get QgsMapLayerComboBoxs widget = dlg.findChild(QgsMapLayerComboBox, 'CMB' + secao) curlyr = widget.currentLayer() if not (curlyr is None): lyrName = curlyr.name() else: lyrName = '' #print secao,widget,lyrName proj.writeEntry(self.SETTINGS, secao, lyrName) #proj.writeEntry("ghydraulics",EpanetModel.GIS_SECTIONS[i], dumps(layers)) # Store Inp file templatedir = os.path.join(os.path.dirname(__file__), 'etc') template = str(dlg.ui.inpFileLineEdit.text()).replace( templatedir + os.path.sep, '') proj.writeEntry(self.SETTINGS, "templateinpfile", template) # Store auto length configuration autoLength = dlg.STRING_FALSE if dlg.ui.autoLengthCB.isChecked(): autoLength = dlg.STRING_TRUE proj.writeEntry(self.SETTINGS, dlg.AUTO_LENGTH, autoLength) # Store backdrop configuration writeBackdrop = dlg.STRING_FALSE if dlg.ui.writeBackdropCB.isChecked(): writeBackdrop = dlg.STRING_TRUE proj.writeEntry(self.SETTINGS, dlg.WRITE_BACKDROP, writeBackdrop) # Store data variables dataDefs = QWaterModel.DATA_DEFS for dataDf, defVal in dataDefs.items(): #Get widget widget = dlg.findChild(QLineEdit, 'Txt_' + dataDf) inVar = widget.text() proj.writeEntry(QWaterPlugin.SETTINGS, dataDf, inVar) proj.writeEntry(QWaterPlugin.SETTINGS, "TUBOS_MAT", str(self.tableToArray())) # Display a message once def explainOnce(self, key, title, message): proj = QgsProject.instance() if GHydraulicsModel.STRING_TRUE == str( proj.readEntry(QWaterPlugin.SETTINGS, key)[0]): return True reply = QMessageBox.question(self.iface.mainWindow(), title, message, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if QMessageBox.Yes == reply: proj.writeEntry(QWaterPlugin.SETTINGS, key, GHydraulicsModel.STRING_TRUE) return True return False # Check if all fields are in place def checkModel(self, maker): checker = maker.checker if 0 == checker.getLayerCount( EpanetModel.JUNCTIONS) or 0 == checker.getLayerCount( EpanetModel.PIPES): question = 'Your junction and/or pipe layer configuration is incomplete. Do you want configure the layers now?' reply = QMessageBox.question(self.iface.mainWindow(), GHydraulicsModelChecker.TITLE, question, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.showSettings() if 0 == checker.getLayerCount( EpanetModel.JUNCTIONS) or 0 == checker.getLayerCount( EpanetModel.PIPES): return False self.checkForModifications(checker) missing = checker.checkFields() if 0 < len(missing): fieldlist = [] for name in list(missing.keys()): fieldlist.append('<br/>Layer "' + name + '": ' + ', '.join(missing[name])) message = 'Your model is missing some fields.' + ''.join( fieldlist) + '<br/>Would you like to add them?' reply = QMessageBox.question(self.iface.mainWindow(), GHydraulicsModelChecker.TITLE, message, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if QMessageBox.Yes != reply: return False if not checker.addFields(missing): QMessageBox.critical(self.iface.mainWindow(), GHydraulicsModelChecker.TITLE, 'Not all fields could be added.', QMessageBox.Ok) return False crss = checker.getCrsDictionary() if 1 != len(crss): message = 'Your model uses more than one coordinate reference system. Please use only one.' QMessageBox.critical(self.iface.mainWindow(), GHydraulicsModelChecker.TITLE, message, QMessageBox.Ok, QMessageBox.Ok) return False missing = checker.checkIds() if 0 < len(missing): question = 'There are ' + str( len(missing) ) + ' duplicate ' + GHydraulicsModel.ID_FIELD + ' values. Do want to fix this automatically now?' reply = QMessageBox.question(self.iface.mainWindow(), GHydraulicsModelChecker.TITLE, question, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if QMessageBox.Yes != reply: return False if not maker.enforceUniqueIds(): return False multis = checker.getMultipartCount() if 0 < multis: question = 'There are ' + str( multis ) + ' pipes with multipart geometries possibly causing problems. Do you want to explode them now?' reply = QMessageBox.question(self.iface.mainWindow(), GHydraulicsModelChecker.TITLE, question, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if QMessageBox.Yes == reply: maker.explodeMultipartPipes() # Fill the node1, node2 fields def makeModel(self): if self.common.PegaQWaterLayer('PIPES') == False: return dlg = self.dlg #GHydraulicsSettingsDialog() template = dlg.getTemplate() maker = GHydraulicsModelMaker(template) self.iface.mainWindow().statusBar().showMessage( 'Checking EPANET model') self.checkModel(maker) question = 'Overwrite the fields NODE1 and NODE2 in all line tables?' self.iface.mainWindow().statusBar().showMessage("Making EPANET model") autolength = dlg.getAutoLength() if autolength: question = 'Overwrite the fields NODE1, NODE2 and LENGTH in all line tables?' if self.explainOnce('makeModelExplanation', 'Make EPANET Model', question): vcount = maker.make() if 0 < vcount: reply = QMessageBox.question( self.iface.mainWindow(), GHydraulicsModelChecker.TITLE, 'Your model is missing ' + str(vcount) + ' junctions. Would you like to add them now?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if QMessageBox.Yes == reply: self.iface.mainWindow().statusBar().showMessage( 'Adding missing junctions to EPANET model') maker.addMissingJunctions() lyrJunctions = self.common.PegaQWaterLayer('JUNCTIONS') lyrJunctions.triggerRepaint() #dlg = GHydraulicsSettingsDialog() if autolength: self.iface.mainWindow().statusBar().showMessage( 'Pipe length calculation') maker.calculateLength() maker.cleanup() self.iface.mainWindow().statusBar().showMessage('') # Prevent problems with unsaved data def checkForModifications(self, checker): modified = checker.getModifiedLayers() if 0 < len(modified): message = 'Some of your model layers have not been saved (' + ', '.join( modified) + ').<br/>Do you want to save them now?' reply = QMessageBox.question(self.iface.mainWindow(), GHydraulicsInpWriter.TITLE, message, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if QMessageBox.Yes == reply: checker.commitChanges() # Write out a file in EPANET INP format # returns True on success, False on failure def writeInpFile(self): # Modified layers may not be exprted correctly self.menuButton.setDefaultAction( self.writeInpAction) #Change default toolbutton to writeInpAction checker = GHydraulicsModelChecker() self.checkForModifications(checker) # select a file prjfi = os.path.splitext(QgsProject.instance().fileName())[0] + '.inp' f, __ = QFileDialog.getSaveFileName(self.iface.mainWindow(), GHydraulicsInpWriter.TITLE, prjfi, 'EPANET INP file (*.inp)') if 0 < len(f): dlg = self.dlg #GHydraulicsSettingsDialog() template = dlg.getTemplate() try: writer = GHydraulicsInpWriter(template, self.iface) writeBackdrop = dlg.getWriteBackdrop() if writeBackdrop: backdropfile = writer.getBackdropFromInp(str(f)) question = 'Overwrite ' + os.path.basename( backdropfile) + '?' if os.path.exists(backdropfile) and not self.explainOnce( 'overwriteBackdrop', GHydraulicsInpWriter.TITLE, question): writeBackdrop = False writer.write(f, writeBackdrop) except GHydraulicsException as e: self.warning('Saving an INP file failed: ' + str(e)) return False return True return False # Open CMD sample project def openDwCmdSample(self): self.iface.addProject( os.path.dirname(__file__) + '/samples/d-w/cmd/ghydraulics.qgs') # Open LPS sample project def openDwLpsSample(self): self.iface.addProject( os.path.dirname(__file__) + '/samples/d-w/lps/qwater_lps.qgs') # Open Net1 sample project def openHwGpmSample(self): self.iface.addProject( os.path.dirname(__file__) + '/samples/h-w/gpm/Net1.qgs') # Display message def warning(self, message, nivel=Qgis.Warning): self.iface.messageBar().pushMessage(self.SETTINGS, message, level=nivel, duration=4) # Create a new project, save and configure layers def newProject(self): self.iface.newProject() project = QgsProject.instance() crs = project.crs() #TODO: Solicitar crs ao usuario baseName = project.readPath("./") for name in EpanetModel.GIS_SECTIONS: lname = name.lower() #self.iface.mainWindow() f, __ = QFileDialog.getSaveFileName( caption='Save new ' + lname + ' layer', directory=baseName + '/' + lname + '.shp', filter='ESRI Shapefile (*.shp)') if 0 == len(f): continue baseName = os.path.split(f)[0] fields = QgsFields() for i in range(0, len(EpanetModel.COLUMNS[name])): field = EpanetModel.COLUMNS[name][i] fields.append( QgsField(field, GHydraulicsModel.COLUMN_TYPES[field])) geometrytype = QgsWkbTypes.LineString if EpanetModel.PIPES == name else QgsWkbTypes.Point writer = QgsVectorFileWriter(f, 'System', fields, geometrytype, crs, 'ESRI Shapefile') if writer.hasError() != QgsVectorFileWriter.NoError: self.warning('Error creating shapefile: ' + str(writer.hasError())) continue del writer layer = self.iface.addVectorLayer(f, lname, 'ogr') if not layer.isValid(): self.warning('Failed to load layer!') continue if not crs: crs = layer.crs() layers = [lname] project.writeEntry(self.SETTINGS, name, lname) self.LoadStyles() self.showSettings() def runEpanet(self): lyrPipe = self.common.PegaQWaterLayer('PIPES') if not lyrPipe: return # Get a temporary file t = tempfile.mkstemp(suffix='.inp') os.close(t[0]) dlg = self.dlg #self.dlg #GHydraulicsSettingsDialog() template = dlg.getTemplate() try: writer = GHydraulicsInpWriter(template, self.iface) writer.write(t[1], False) except GHydraulicsException as e: self.warning('Saving an INP file failed :' + str(e)) return try: runner = GHydraulicsModelRunner() output, report, steps = runner.run(t[1]) dlg = GHydraulicsResultDialog(runner.setStep) dlg.ui.textOutput.setText(output) dlg.ui.textReport.setText(report) dlg.ui.comboStep.clear() dlg.ui.comboStep.addItems([str(x) for x in range(1, steps + 1)]) dlg.show() result = dlg.exec_() except GHydraulicsException as e: self.warning('Running a simulation failed :' + str(e)) os.unlink(t[1]) lyrPipe.triggerRepaint() def carregaTabMats(self, tbMats): tableWidget = self.dlg.findChild(QTableWidget, 'tableWidget') tableWidget.setRowCount(0) tableWidget.setColumnCount(0) for i, tbMat in enumerate(tbMats): row = tableWidget.rowCount() tableWidget.insertRow(row) tableWidget.setColumnCount(len(tbMat)) if row < 1: tableWidget.setHorizontalHeaderLabels(tbMat) else: for column, data in enumerate(tbMat): if column == 0: cell_widget = QWidget() lay_out = QHBoxLayout(cell_widget) chk_bx = QCheckBox() if data in [1, '1', 't', 'True']: chk_bx.setCheckState(QtCore.Qt.Checked) else: chk_bx.setCheckState(QtCore.Qt.Unchecked) lay_out.addWidget(chk_bx) lay_out.setAlignment(Qt.AlignCenter) lay_out.setContentsMargins(0, 0, 0, 0) cell_widget.setLayout(lay_out) tableWidget.setCellWidget(row, 0, cell_widget) else: item = QTableWidgetItem("{}".format(data)) #:7.2f item.setTextAlignment(Qt.AlignVCenter | Qt.AlignRight) tableWidget.setItem(row, column, item) #tableWidget.setHorizontalHeaderLabels(['On','DN','Diameter','Roughness','Pressure','Headloss','Reference']) #for i in range(1,4): # tableWidget.resizeColumnToContents(i) tableWidget.removeRow(0) tableWidget.resizeColumnsToContents() def tableToArray(self): table = self.dlg.findChild(QTableWidget, 'tableWidget') result = [self.LeCabecalho(table)] num_rows, num_cols = table.rowCount(), table.columnCount() for row in range(num_rows): rows = [] for col in range(num_cols): if col == 0: cell_widget = table.cellWidget(row, col).findChild( type(QCheckBox())) if cell_widget.isChecked( ): #QTableWidget.item(row,column).checkState()==QtCore.Qt.Checked: rows.append(1) else: rows.append(0) else: item = table.item(row, col) rows.append(item.text()) #if item else '' result.append(rows) return result def LeCabecalho(self, tblWid): header = [] for column in range(tblWid.columnCount()): header.append(tblWid.horizontalHeaderItem(column).text()) return header def LoadStyles(self): self.menuButton.setDefaultAction(self.LoadStylesAction) proj = QgsProject.instance() qStyles = { 'PIPES': 'pipe_headloss.qml', 'JUNCTIONS': 'node_pressure.qml', 'PUMPS': 'node_pump.qml', 'RESERVOIRS': 'node_reserv.qml', 'TANKS': 'node_tank.qml', 'VALVES': 'node_valve.qml' } #{tipo:estilo} rootID = QWaterPlugin.SETTINGS for tipo, estilo in qStyles.items(): ProjVar = proj.readEntry(rootID, tipo)[0] if ProjVar != '': myLayer = proj.mapLayersByName(ProjVar)[0] self.CarregaEstilo(myLayer, estilo) def CarregaEstilo(self, vLayer, Estilo): basepath = os.path.dirname(__file__) #os.path.realpath( FullPath = os.path.join(basepath, 'style/' + Estilo) vLayer.loadNamedStyle(FullPath) vLayer.triggerRepaint() def GetElevation(self): self.menuButton.setDefaultAction( self.GetElevationAction ) #Change default toolbutton to GetElevationAction dlg = QDialog() dlg.setWindowTitle('Select Elevation Raster') #Combobox ml = QgsMapLayerComboBox() ml.setFilters(QgsMapLayerProxyModel.RasterLayer) #ButtonBox bb = QDialogButtonBox() bb.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) #layout layOut = QVBoxLayout() layOut.addWidget(ml) layOut.addWidget(bb) dlg.setLayout(layOut) dlg.setMinimumWidth(300) # Signals answers def ok(): dlg.close() self.RasterSampling(ml.currentLayer()) #curlyr = ml.currentLayer() def cancel(): print('cancelled') dlg.close() #connect to signals bb.accepted.connect(ok) bb.rejected.connect(cancel) dlg.show() def RasterSampling(self, Raster): nodeLyr = self.common.PegaQWaterLayer('JUNCTIONS') if not nodeLyr: iface.messageBar().pushMessage(self.SETTINGS, 'No Junction layer defined!', level=Qgis.Warning, duration=5) return noElevPtos = [] nodeLyr.startEditing() for pto in nodeLyr.getFeatures(): if pto.geometry().isMultipart(): point = pto.geometry().asMultiPoint()[0] else: point = pto.geometry().asPoint() rastSample = Raster.dataProvider().identify( point, QgsRaster.IdentifyFormatValue).results() #previousRastSample = rastSample #value, ok = Raster.dataProvider().sample(point, 1) value = rastSample[1] if value: pto['ELEVATION'] = value #print(pto['DC_ID'],'=',value) nodeLyr.updateFeature(pto) else: noElevPtos.append(pto.id()) if noElevPtos: #print(noElevPtos) nodeLyr.selectByIds(noElevPtos) mapCanvas = iface.mapCanvas() mapCanvas.zoomToSelected(nodeLyr) iface.messageBar().pushMessage( self.SETTINGS, 'Check Selected features with No Elevation data!', level=Qgis.Warning, duration=0) else: iface.messageBar().pushMessage( self.SETTINGS, 'Get elevation from Raster success!', level=Qgis.Info, duration=5) nodeLyr.triggerRepaint() def update_DN(self): self.menuButton.setDefaultAction(self.updateDN_Action) #Check and get Pipes Layer pipeLyr = self.common.PegaQWaterLayer('PIPES') if not pipeLyr: return #Check and create field 'DN' #NPS (Nominal Pipe Size) field_index = pipeLyr.fields().indexFromName('DN') pipeLyr.startEditing() if field_index == -1: #if field does not exist create it DNfld = QgsField("DN", QVariant.Int) pipeLyr.addAttribute(DNfld) pipeLyr.updateFields() field_index = pipeLyr.fields().indexFromName('DN') editSet = QgsEditorWidgetSetup('TextEdit', {}) #{'IsMultiline': 'True'} pipeLyr.setEditorWidgetSetup(field_index, editSet) #Carrega Tubos proj = QgsProject.instance() tubosMat = proj.readEntry(self.SETTINGS, "TUBOS_MAT", "0")[0] if tubosMat == '0': raise GHydraulicsException( 'ERROR: Please, Define Pipes on settings dialog First!') #tubos=QWaterModel.TUBOS_MAT else: tubos = eval(tubosMat) if not isinstance(tubos[0][0], str): raise GHydraulicsException( 'ERROR: Incorrect Pipes definition!. Please, Define Pipes on settings dialog First!' ) cabecalho = tubos[0] diaIdx = cabecalho.index('Diameter') dnIdx = cabecalho.index('DN') indices = [diaIdx, dnIdx] diaXdn = {} for linha in range(1, len(tubos)): diam = eval(tubos[linha][diaIdx]) diaXdn[diam] = eval(tubos[linha][dnIdx]) #diaXdn= [[each_list[i] for i in indices] for each_list in tubos] noDNdef = [] #Update DN field for f in pipeLyr.getFeatures(): diam = f['DIAMETER'] #Check if Diameter exists in Pipes Table Setting if diam not in diaXdn: noDNdef.append(f.id()) else: f['DN'] = diaXdn[diam] pipeLyr.updateFeature(f) pipeLyr.triggerRepaint() if noDNdef: pipeLyr.selectByIds(noDNdef) mapCanvas = iface.mapCanvas() mapCanvas.zoomToSelected(pipeLyr) iface.messageBar().pushMessage( self.SETTINGS, 'Check Selected features with Diameters not in Pipes Table Setting or use Calculate economic Diameters tool!', level=Qgis.Warning, duration=0) else: iface.messageBar().pushMessage(self.SETTINGS, 'DN success updated!', level=Qgis.Info, duration=3)
class QWeather: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'QWeather_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&QWeather') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'QWeather') self.toolbar.setObjectName(u'QWeather') self.key1 = '9Q2gyTzBLVGVSTWdoJnM9Y29uc' self.toolButton = QToolButton() self.toolButton.setMenu(QMenu()) self.toolButton.setPopupMode(QToolButton.MenuButtonPopup) self.toolbar.addWidget(self.toolButton) # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('QWeather', message) def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ # Create the dialog (after translation) and keep reference icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) return action def _generate_signature(self, key, data): key_bytes = bytes(key, 'utf-8') data_bytes = bytes(data, 'utf-8') signature = hmac.new(key_bytes, data_bytes, hashlib.sha1).digest() return b64encode(signature).decode() def get_yahoo_weather( self, location, temp_type, app_id, consumer_key, consumer_secret, url='https://weather-ydn-yql.media.yahoo.com/forecastrss'): # Basic info method = 'GET' concat = '&' query = {'location': location, 'format': 'json', 'u': temp_type} oauth = { 'oauth_consumer_key': consumer_key, 'oauth_nonce': uuid.uuid4().hex, 'oauth_signature_method': 'HMAC-SHA1', 'oauth_timestamp': str(int(time.time())), 'oauth_version': '1.0' } # Prepare signature string (merge all params and SORT them) merged_params = query.copy() merged_params.update(oauth) sorted_params = [ k + '=' + urllib.parse.quote(merged_params[k], safe='') for k in sorted(merged_params.keys()) ] signature_base_str = method + concat + urllib.parse.quote( url, safe='') + concat + urllib.parse.quote( concat.join(sorted_params), safe='') # Generate signature composite_key = urllib.parse.quote(consumer_secret, safe='') + concat oauth_signature = self._generate_signature(composite_key, signature_base_str) # Prepare Authorization header oauth['oauth_signature'] = oauth_signature auth_header = ( 'OAuth ' + ', '.join(['{}="{}"'.format(k, v) for k, v in oauth.items()])) # Send request url = url + '?' + urllib.parse.urlencode(query) request = urllib.request.Request(url) request.add_header('Authorization', auth_header) request.add_header('X-Yahoo-App-Id', app_id) response = urllib.request.urlopen(request).read() return json.loads(response) def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/QWeather/weather.png' self.mainButton = QAction(QIcon(icon_path), "Weather Info", self.iface.mainWindow()) self.mainButton.triggered.connect(self.run) icon_path = ':/plugins/QWeather/icons/reload.png' self.reloadButton = self.add_action( icon_path, text=self.tr(u'Reload QWeather layer'), callback=self.reloadWeather, parent=self.iface.mainWindow()) self.style_temperature_btn = QAction(QIcon(''), "Temperature", self.iface.mainWindow()) self.style_temperature_btn.setText("Temperature") self.style_temperature_btn.triggered.connect(self.style_temperature) self.style_direction_btn = QAction(QIcon(''), "Direction", self.iface.mainWindow()) self.style_direction_btn.setText("Direction") self.style_direction_btn.triggered.connect(self.style_direction) self.style_humidity_btn = QAction(QIcon(''), "Humidity", self.iface.mainWindow()) self.style_humidity_btn.setText("Humidity") self.style_humidity_btn.triggered.connect(self.style_humidity) menu = self.toolButton.menu() menu.addAction(self.mainButton) menu.addAction(self.style_temperature_btn) menu.addSeparator() menu.addAction(self.style_direction_btn) menu.addAction(self.style_humidity_btn) self.toolButton.setDefaultAction(self.mainButton) self.dlg = QWeatherDialog() #self.dlg.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint) self.key2 = 'VtZXJzZWNyZXQmc3Y9MCZ4PTZl' self.dlg.ok.clicked.connect(self.ok) self.dlg.closebutton.clicked.connect(self.close) self.dlg.toolButtonImport.clicked.connect(self.toolButtonImport) self.dlg.checkBox.clicked.connect(self.customBox) self.csvFile = None self.current = 'World Capitals' self.layerTemp = "QWeather" self.reload = False self.dlg.checkBox.setChecked(True) self.reloadButton.setEnabled(False) self.app_id = 'IyELbl3e' self.consumer_key = 'dj0yJmk' + self.key1 + '3' + self.key2 self.consumer_secret = 'e9962f3287764230d163045f737f9ad81428cdcc' def style_humidity(self): try: self.QWeather.styleManager().setCurrentStyle('humidity') except: pass def style_temperature(self): try: self.QWeather.styleManager().setCurrentStyle('temperature') except: pass def style_direction(self): try: self.QWeather.styleManager().setCurrentStyle('direction') except: pass def reloadWeather(self): self.reload = True if self.csvFile is None: self.csvFile = os.path.join(self.plugin_dir, 'World Capitals.csv') self.dlg.imp.setText(self.csvFile) self.ok() def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu(self.tr(u'&QWeather'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def customBox(self): if self.dlg.checkBox.isChecked(): self.dlg.comboBox.setEnabled(False) self.dlg.toolButtonImport.setEnabled(True) self.dlg.imp.setEnabled(True) else: if 'Countries.csv' in self.dlg.imp.text(): self.csvFile = os.path.join(self.plugin_dir, 'World Capitals.csv') self.dlg.imp.setText(self.csvFile) self.dlg.comboBox.setEnabled(True) self.dlg.toolButtonImport.setEnabled(False) self.dlg.imp.setEnabled(False) def run(self): self.dlg.ok.setEnabled(True) self.dlg.closebutton.setEnabled(True) self.dlg.toolButtonImport.setEnabled(True) if self.csvFile is None: self.csvFile = os.path.join(self.plugin_dir, 'World Capitals.csv') self.dlg.imp.setText(self.csvFile) self.dlg.Celsius.setChecked(True) self.dlg.progressBar.setValue(0) self.customBox() self.dlg.comboBox.clear() db_list = [ 'World Capitals', 'Afghanistan', 'Aland', 'Albania', 'Algeria', 'American Samoa', 'Andorra', 'Angola', 'Antarctica', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Aruba', 'Australia', 'Austria', 'Azerbaijan', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bermuda', 'Bhutan', 'Bolivia', 'Bosnia and Herzegovina', 'Botswana', 'Brazil', 'Brunei', 'Bulgaria', 'Burkina Faso', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Cayman Islands', 'Central African Republic', 'Chad', 'Chile', 'China', 'Colombia', 'Comoros', 'Congo (Brazzaville)', 'Congo (Kinshasa)', 'Cook Islands', 'Costa Rica', 'Croatia', 'Cuba', 'Curacao', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'East Timor', 'Ecuador', 'Egypt', 'El Salvador', 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Falkland Islands', 'Faroe Islands', 'Federated States of Micronesia', 'Fiji', 'Finland', 'France', 'French Polynesia', 'Gabon', 'Georgia', 'Germany', 'Ghana', 'Gibraltar', 'Greece', 'Greenland', 'Grenada', 'Guam', 'Guatemala', 'Guinea', 'Guinea Bissau', 'Guyana', 'Haiti', 'Honduras', 'Hong Kong S.A.R.', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran', 'Iraq', 'Ireland', 'Isle of Man', 'Israel', 'Italy', 'Ivory Coast', 'Jamaica', 'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kiribati', 'Kosovo', 'Kuwait', 'Kyrgyzstan', 'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macau S.A.R', 'Macedonia', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', 'Marshall Islands', 'Mauritania', 'Mauritius', 'Mexico', 'Moldova', 'Monaco', 'Mongolia', 'Montenegro', 'Morocco', 'Mozambique', 'Myanmar', 'Namibia', 'Nepal', 'Netherlands', 'New Caledonia', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'North Korea', 'Northern Mariana Islands', 'Norway', 'Oman', 'Pakistan', 'Palau', 'Palestine', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', 'Philippines', 'Poland', 'Portugal', 'Puerto Rico', 'Qatar', 'Romania', 'Russia', 'Rwanda', 'Saint Kitts and Nevis', 'Saint Lucia', 'Saint Vincent and the Grenadines', 'Samoa', 'San Marino', 'Sao Tome and Principe', 'Saudi Arabia', 'Senegal', 'Serbia', 'Seychelles', 'Sierra Leone', 'Singapore', 'Slovakia', 'Slovenia', 'Solomon Islands', 'Somalia', 'Somaliland', 'South Africa', 'South Georgia and the Islands', 'South Korea', 'South Sudan', 'Spain', 'Sri Lanka', 'Sudan', 'Suriname', 'Svalbard and Jan Mayen Islands', 'Swaziland', 'Sweden', 'Switzerland', 'Syria', 'Taiwan', 'Tajikistan', 'Tanzania', 'Thailand', 'The Bahamas', 'The Gambia', 'Togo', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Turks and Caicos Islands', 'Tuvalu', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States Virgin Islands', 'USA', 'Uruguay', 'Uzbekistan', 'Vanuatu', 'Vatican (Holy Sea)', 'Venezuela', 'Vietnam', 'Western Sahara', 'Yemen', 'Zambia', 'Zimbabwe' ] self.dlg.comboBox.addItems(db_list) self.dlg.comboBox.setCurrentText(self.current) if self.dlg.isVisible(): self.dlg.close() self.dlg.show() else: self.dlg.show() def close(self): self.dlg.close() def toolButtonImport(self): self.csvFile = QFileDialog.getOpenFileName( None, "Choose the file", os.path.join(os.path.join(os.path.expanduser('~')), 'Desktop'), "(*.csv)") if self.csvFile[0] == "": self.csvFile = os.path.join(self.plugin_dir, 'World Capitals.csv') self.dlg.imp.setText(self.csvFile) return self.csvFile = self.csvFile[0] self.dlg.imp.setText(self.csvFile) def selectOutp(self): msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Warning) msgBox.setWindowTitle('Warning') msgBox.setText('Please define a csv file with locations.') msgBox.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint) msgBox.exec_() return True def call_import_temps_task(self): self.taskWeather = QgsTask.fromFunction(u'QWeather', self.import_temps_task, on_finished=self.completed, wait_time=4) QgsApplication.taskManager().addTask(self.taskWeather) def import_temps_task(self, task, wait_time): for i, location in enumerate(self.all_cities): try: time.sleep(.1) perc = (i / len(self.all_cities)) * 100 self.taskWeather.setProgress(perc) self.dlg.progressBar.setValue(perc) data = self.get_yahoo_weather( location.replace("'", ""), self.unit, self.app_id, self.consumer_key, self.consumer_secret, url='https://weather-ydn-yql.media.yahoo.com/forecastrss') if data['location'] == {}: continue fields = {} fieldnames = [ 'country', 'city', 'region', 'temperature', 'temperature_unit', 'date', 'direction', 'direction_unit', 'speed', 'speed_unit', 'humidity', 'humidity_unit', 'pressure', 'pressure_unit', 'visibility', 'visibility_unit', 'sunrise', 'sunset', 'icon', 'lat', 'lon' ] for field_init in fieldnames: fields[field_init] = [] try: fields['city'] = data['location']['city'] fields['temperature'] = str(data['current_observation'] ['condition']['temperature']) if not fields['temperature'].isnumeric(): continue fields['country'] = data['location']['country'].replace( 'ô', 'o').replace('´', '') fields['region'] = data['location']['region'].replace( '´', '') fields['direction'] = str( data['current_observation']['wind']['direction']) fields['direction_unit'] = self.unitDirection fields['speed'] = str( data['current_observation']['wind']['speed']) fields['speed_unit'] = self.unitSpeed fields['humidity'] = str( data['current_observation']['atmosphere']['humidity']) fields['humidity_unit'] = self.unitHumidity fields['pressure'] = str( data['current_observation']['atmosphere']['pressure']) fields['pressure_unit'] = self.unitPressure fields['visibility'] = str(data['current_observation'] ['atmosphere']['visibility']) fields['visibility_unit'] = self.unitVisibility fields['sunrise'] = str( data['current_observation']['astronomy']['sunrise']) fields['sunset'] = str( data['current_observation']['astronomy']['sunset']) Lon = data['location']['long'] Lat = data['location']['lat'] fields['lon'] = str(Lon) fields['lat'] = str(Lat) fields['date'] = time.ctime( int(str(data['current_observation']['pubDate']))) try: fields['icon'] = data['current_observation'][ 'condition']['text'] except: fields['icon'] = ' ' except: pass fields['temperature_unit'] = self.unit geo_info = { "type": "Feature", "properties": fields, "geometry": { "coordinates": [Lon, Lat], "type": "Point" } } self.geoinfo.append(geo_info) if self.taskWeather.isCanceled(): self.stopped(self.taskWeather) self.taskWeather.destroyed() return None except: pass return True def stopped(self, task): QgsMessageLog.logMessage( 'Task "{name}" was canceled'.format(name=task.description()), 'QWeather', Qgis.Info) def completed(self, exception, result=None): geojson = { "type": "FeatureCollection", "name": self.layerTemp, "crs": { "type": "name", "properties": { "name": "crs:OGC:1.3:CRS84" } }, "features": self.geoinfo } with open(self.outQWeatherGeoJson, 'w') as geofile: json.dump(geojson, geofile) if len(QgsProject.instance().mapLayersByName(self.layerTemp)) == 0: self.addQWeatherLayer() else: for x in self.iface.mapCanvas().layers(): if x.name() == self.layerTemp: self.QWeather = x QgsProject.instance().removeMapLayer(self.QWeather.id()) self.addQWeatherLayer() try: self.QWeather.selectAll() self.iface.mapCanvas().zoomToSelected() self.QWeather.removeSelection() except: pass ########################################### self.dlg.ok.setEnabled(True) self.dlg.closebutton.setEnabled(True) if not self.dlg.checkBox.isChecked(): self.dlg.toolButtonImport.setEnabled(False) else: self.dlg.toolButtonImport.setEnabled(True) self.reloadButton.setEnabled(True) self.dlg.progressBar.setValue(100) self.dlg.close() def addQWeatherLayer(self): # load qml to current style self.QWeather = self.iface.addVectorLayer(self.outQWeatherGeoJson, self.layerTemp, "ogr") style_manager = self.QWeather.styleManager() # read valid style from layer style = QgsMapLayerStyle() style.readFromLayer(self.QWeather) self.QWeather.loadNamedStyle( os.path.join(self.plugin_dir, "icons", self.style2 + ".qml")) style_manager.renameStyle("default", "direction") style_name = "humidity" # add style with new name style_manager.addStyle(style_name, style) # set new style as current style_manager.setCurrentStyle(style_name) self.QWeather.loadNamedStyle( os.path.join(self.plugin_dir, "icons", "humidity.qml")) style_manager.renameStyle(style_name, style_name) style = QgsMapLayerStyle() style.readFromLayer(self.QWeather) style_name = self.style # add style with new name style_manager.addStyle(style_name, style) # set new style as current style_manager.setCurrentStyle(style_name) self.QWeather.loadNamedStyle( os.path.join(self.plugin_dir, "icons", self.style + ".qml")) style_manager.renameStyle(self.style, "temperature") def ok(self): if not self.reload: if self.dlg.imp.text() == '': if self.selectOutp(): return elif not os.path.isabs(self.dlg.imp.text()): if self.selectOutp(): return else: self.reload = False self.csvFile = self.dlg.imp.text() status = False if not self.dlg.checkBox.isChecked(): self.current = self.dlg.comboBox.currentText() status = self.current.upper() == 'WORLD CAPITALS' if status: self.csvFile = os.path.join(self.plugin_dir, 'World Capitals.csv') if not self.dlg.checkBox.isChecked() and not status: self.csvFile = os.path.join(self.plugin_dir, 'Countries.csv') with open(self.csvFile, 'r') as f_all: self.all_cities = [] for line in f_all: mm = line.rstrip(', ').upper() if self.current.upper() in mm: self.all_cities.append(mm[:-1]) else: try: with open(self.csvFile, 'r') as f: self.all_cities = [line.rstrip('\n').upper() for line in f] except: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Warning) msgBox.setWindowTitle('QWeather') msgBox.setText('Please define a csv file path.') msgBox.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint) msgBox.exec_() return self.outQWeatherGeoJson = os.path.join(self.plugin_dir, 'QWeather.geojson') basename = os.path.basename(self.outQWeatherGeoJson) self.layerTemp = basename[:-8] try: f = open(self.outQWeatherGeoJson, "w") f.close() except: self.selectOutp() self.dlg.ok.setEnabled(False) self.dlg.closebutton.setEnabled(False) self.dlg.toolButtonImport.setEnabled(False) if not self.iface.actionSelectRectangle().isChecked(): self.iface.actionSelectRectangle().trigger() if not self.iface.actionMapTips().isChecked(): self.iface.actionMapTips().trigger() if len(self.all_cities) > 1000: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Warning) msgBox.setWindowTitle('Warning') msgBox.setText('Maximum locations must be below from 1000.') msgBox.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint) msgBox.exec_() ########################################### self.dlg.progressBar.setValue(100) self.dlg.progressBar.setValue(0) self.dlg.ok.setEnabled(True) self.dlg.closebutton.setEnabled(True) self.dlg.toolButtonImport.setEnabled(True) return # do stuff if self.dlg.Celsius.isChecked(): self.unit = 'C' self.unitDirection = "degrees" self.unitSpeed = "km/h" self.unitHumidity = "%" self.unitVisibility = "km" self.unitPressure = "hPa" self.style = 'weather_c' self.style2 = 'direction_c' else: self.unit = 'F' self.unitDirection = "degrees" self.unitSpeed = "mph" self.unitHumidity = "%" self.unitVisibility = "mi" self.unitPressure = "psi" self.style = 'weather_f' self.style2 = 'direction_f' self.geoinfo = [] self.call_import_temps_task()
class UnderMap: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = join( self.plugin_dir, 'i18n', 'UnderMap_{}.qm'.format(locale)) if exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Create the dialog (after translation) and keep reference self.dlg = UnderMapDialog() self.addop = AjouterOperateurDialog() self.addpdf = DialogAddPDF() self.splitpdf = DialogSplitPDF() self.importpoints = DialogImportPoint() self.zoomto = DialogZoomToFeature() # Initialise buttton self.init_button = QToolButton() self.init_button.setMenu(QMenu()) self.init_button.setPopupMode(QToolButton.MenuButtonPopup) # toolBar self.toolbar = self.iface.addToolBar('UnderMap') self.toolbar.setObjectName('UnderMap') # actions self.initialisePDFAction = None self.reportAction = None self.addOperatorAction = None self.initialiseFDPAction = None self.initialiseEmpriseAction = None self.addPDFAction = None self.splitPDFAction = None self.importPointsAction = None self.mergeFeaturesAction = None self.manageBufferAction = None self.saveAsGeoJsonAndTfwAction = None self.controlAction = None self.zoomToAction = None QgsSettings().setValue("qgis/digitizing/reuseLastValues", True) # For enable/disable the addpdf editor icon self.iface.currentLayerChanged.connect(self.layer_changed) # For load layer on qgis self.iface.projectRead.connect(self.load_layer) @staticmethod def tr(message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('UnderMap', message) # noinspection PyPep8Naming def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" # the actions self.initialisePDFAction = QAction( QIcon(join(dirname(__file__), 'resources', 'initialpdf.png')), 'Initialiser PDF', self.iface.mainWindow()) self.addOperatorAction = QAction( QIcon(join(dirname(__file__), 'resources', 'ajouterOperateur.png')), 'Ajouter un exploitant', self.iface.mainWindow()) self.reportAction = QAction( QIcon(join(dirname(__file__), 'resources', 'report.png')), 'Générer le rapport', self.iface.mainWindow()) self.initialiseFDPAction = QAction( QIcon(join(dirname(__file__), 'resources', 'fdp.png')), 'Initialiser un FDP', self.iface.mainWindow()) self.initialiseEmpriseAction = QAction( QIcon(join(dirname(__file__), 'resources', 'icon.png')), 'Initialiser une emprise', self.iface.mainWindow()) self.addPDFAction = QAction( QIcon(join(dirname(__file__), 'resources', 'add_pdf.png')), 'Ajouter pdf', self.iface.mainWindow()) self.splitPDFAction = QAction( QIcon(join(dirname(__file__), 'resources', 'add_pdf.png')), 'Découper un PDF', self.iface.mainWindow()) self.importPointsAction = QAction( QIcon(join(dirname(__file__), 'resources', 'point.png')), 'Importer les points de calage', self.iface.mainWindow()) self.mergeFeaturesAction = QAction( QIcon(join(dirname(__file__), 'resources', '')), 'Fusionner les entités', self.iface.mainWindow()) self.manageBufferAction = QAction( QIcon(join(dirname(__file__), 'resources', '')), 'Génerer les buffers', self.iface.mainWindow()) self.saveAsGeoJsonAndTfwAction = QAction( QIcon(join(dirname(__file__), 'resources', '')), 'Exporter les GeoJSON et les tfw', self.iface.mainWindow()) self.controlAction = QAction( QIcon(join(dirname(__file__), 'resources', 'opacity.png')), 'Contrôler', self.iface.mainWindow()) self.zoomToAction = QAction( QIcon(join(dirname(__file__), 'resources', 'zoom-in.png')), 'Zoom', self.iface.mainWindow()) # actions dialogs self.initialisePDFAction.triggered.connect(self.initialise_PDF) self.addOperatorAction.triggered.connect(self.add_operator) self.initialiseFDPAction.triggered.connect(self.initialise_FDP) self.initialiseEmpriseAction.triggered.connect(self.initialise_emprise) self.reportAction.triggered.connect(self.export_report) self.addPDFAction.triggered.connect(self.add_pdf) self.splitPDFAction.triggered.connect(self.split_pdf) self.importPointsAction.triggered.connect(self.import_points) self.mergeFeaturesAction.triggered.connect(self.merge_features_layer) self.manageBufferAction.triggered.connect(self.manage_buffer) self.saveAsGeoJsonAndTfwAction.triggered.connect(self.save_geojson_tfw) self.controlAction.triggered.connect(self.control) self.zoomToAction.triggered.connect(self.zoom_to_feature) # add actions on menu self.init_button.menu().addAction(self.initialisePDFAction) self.init_button.menu().addAction(self.addOperatorAction) self.init_button.setDefaultAction(self.initialisePDFAction) # add separator # self.initialiseFDPAction.insertSeparator(self.initialisePDFAction) self.init_button.menu().addAction(self.initialiseFDPAction) self.init_button.menu().addAction(self.initialiseEmpriseAction) self.init_button.menu().addAction(self.splitPDFAction) self.init_button.menu().addAction(self.mergeFeaturesAction) self.init_button.menu().addAction(self.manageBufferAction) self.init_button.menu().addAction(self.saveAsGeoJsonAndTfwAction) # add actions and menu in toolbar self.toolbar.addWidget(self.init_button) self.toolbar.addAction(self.reportAction) self.toolbar.addAction(self.addPDFAction) self.toolbar.addAction(self.importPointsAction) self.toolbar.addAction(self.controlAction) self.toolbar.addAction(self.zoomToAction) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" self.iface.mainWindow().removeToolBar(self.toolbar) self.iface.currentLayerChanged.disconnect(self.layer_changed) def layer_changed(self, layer): try: layers = get_layers_in_group("RSX") except AttributeError: return if not hasattr(layer, 'name'): enable_addpdf = False elif layer.name() not in layers: enable_addpdf = False elif not hasattr(layer, 'providerType'): enable_addpdf = False elif layer.providerType() == 'wms': enable_addpdf = False elif not layer.geometryType() == 1: enable_addpdf = False else: enable_addpdf = True self.addPDFAction.setEnabled(enable_addpdf) def run(self): """Run method that performs all the real work""" # show the dialog self.dlg.show() # Run the dialog event loop result = self.dlg.exec_() # See if OK was pressed if result: # Do something useful here - delete the line containing pass and # substitute with your code. pass def add_operator(self): self.addop.exec_() def add_pdf(self): self.addpdf.exec_() def split_pdf(self): self.splitpdf.exec_() def import_points(self): self.importpoints.exec_() def zoom_to_feature(self): self.zoomto.exec_() def manage_buffer(self): project_path = get_project_path() manage_buffer(project_path) self.iface.messageBar().pushInfo('Undermap', "La génération des buffers a bien reussi." ) def control(self): transparency_raster() def initialise_PDF(self): project_path = get_project_path() if project_path == './': QMessageBox.warning(None,"Avertisment","Veulliez ouvrir un projet qgis") return else: dir_selected = QFileDialog.getExistingDirectory(None, "Sélectionner un dossier", project_path, QFileDialog.ShowDirsOnly) if dir_selected == '': self.iface.messageBar().pushWarning('Undermap', "Aucun dossier séléctionné") return else: initialise_pdf(dir_selected) QgsProject.instance().write() def initialise_FDP(self): project_path = get_project_path() if project_path == './': QMessageBox.warning(None, "Avertisment", "Veulliez ouvrir un projet qgis") return else: fileSelected = QFileDialog.getOpenFileName(None, "Sélectionnez un fichier", project_path, "*.dxf") if fileSelected == ('', ''): self.iface.messageBar().pushWarning('Undermap', "Aucun fichier dxf séléctionné") return else: initialise_fdp(fileSelected) def initialise_emprise(self): project_path = get_project_path() if project_path == './': QMessageBox.warning(None, "Avertissement", "Veuillez ouvrir un projet QGIS et l’enregistrer") return else: filter = "DXF(*.dxf);;KML(*.kml);;SHAPE(*.shp)" fileSelected = QFileDialog.getOpenFileName(None, "Sélectionnez un fichier", project_path, filter) if fileSelected == ('', ''): self.iface.messageBar().pushWarning('Undermap', "Aucun fichier séléctionné") return else: initialise_emprise(fileSelected) def export_report(self): project_path = get_project_path() if project_path == './': QMessageBox.warning(None, "Avertisment", "Veuillez ouvrir un projet qgis") return else: if export_xlsx_report(project_path): self.iface.messageBar().pushInfo('Undermap', "La génération du rapport a bien reussi." ) os.startfile(join(project_path, QgsProject.instance().baseName()+'.xlsx')) else: QMessageBox.warning(None, 'Undermap', "QGIS ne peut pas écrire " "le rapport car le fichier" " {} est ouvert " "dans une autre application" .format(join(project_path, QgsProject.instance() .baseName()+'.xlsx'))) def save_geojson_tfw(self): project_path = get_project_path() if project_path == './': QMessageBox.warning(None, "Avertisment", "Veuillez ouvrir un projet qgis") return else: if export_as_geojson(project_path): if export_tfw(project_path): self.iface.messageBar().pushInfo('Undermap', "l'export de GeoJSON et les fichiers" " tfw est bien reussi" ) delete_unused_folder(project_path) def merge_features_layer(self): from matplotlib import pyplot as plt project_path = get_project_path() if project_path == './': QMessageBox.warning(None, "Avertisment", "Veuillez ouvrir un projet qgis") return else: merge_features_connected_layers(project_path) plt.pause(5) overwrite_layers_merged(project_path) delete_unused_folder(project_path) def load_layer(self): project_path = get_project_path() load_unloaded_data(project_path)
class WaysCalc: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', 'WaysCalc_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&WaysCalc') # TODO: We are going to let the user set this up in a future iteration # self.toolbar = self.iface.addToolBar(u'WaysCalc') # self.toolbar.setObjectName(u'WaysCalc') #print "** INITIALIZING WaysCalc" self.pluginIsActive = False self.dockwidget_inters_ways = None self.dockwidget_inters_allways = None # self.settings = None self.IW = None #IntersectionWays self.IAW = None #IntersectionAllWays # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('WaysCalc', message) def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" # выпадающая кнопка для пересекающихся маршрутов self.toolButton = QToolButton() self.toolButton.setMenu(QMenu()) # setPopupMode: DelayedPopup, MenuButtonPopup, InstantPopup self.toolButton.setPopupMode(QToolButton.InstantPopup) self.toolButton.setAutoRaise(True) self.toolButton_action = self.iface.addToolBarWidget(self.toolButton) self.action_intersection = QAction( QIcon(":/plugins/_Ways_calc/icon.png"), u'Поиск пересечений для линейного объекта', self.iface.mainWindow()) self.action_initLayerWays = QAction( u"Выбор слоя для поиска пересечений", self.iface.mainWindow()) self.action_allintersection = QAction( QIcon(":/plugins/_Ways_calc/icon.png"), u'Поиск пересечений всех линейных объектов', self.iface.mainWindow()) self.iface.addPluginToMenu(u"WaysCalc", self.action_intersection) self.iface.addPluginToMenu(u"WaysCalc", self.action_initLayerWays) self.iface.addPluginToMenu(u"WaysCalc", self.action_allintersection) # self.iface.addToolBarIcon(self.action_intersection) m = self.toolButton.menu() m.addAction(self.action_intersection) m.addAction(self.action_initLayerWays) m.addSeparator() m.addAction(self.action_allintersection) self.toolButton.setDefaultAction(self.action_intersection) self.action_intersection.triggered.connect(self.run_intersection) # пересекающиеся маршруты self.action_allintersection.triggered.connect(self.run_allintersection) # пересекающиеся все маршруты self.action_initLayerWays.triggered.connect(self.run_initLayerWays) # выбор слоя сравнения self.pointEmitterIntersection = QgsMapToolEmitPoint(self.iface.mapCanvas()) self.pointEmitterIntersection.setAction(self.action_intersection) self.pointEmitterIntersection.canvasClicked.connect(self.pointEmitterIntersectioncanvasClicked) self.iface.mapCanvas().currentLayerChanged.connect(self.onCurrentLayerChanged) #-------------------------------------------------------------------------- def onClosePlugin(self): """Cleanup necessary items here when plugin dockwidget is closed""" # disconnects try: self.dockwidget_inters_ways.closingPlugin.disconnect(self.onClosePlugin) except: pass try: self.dockwidget_inters_allways.closingPlugin.disconnect(self.onClosePlugin) except: pass self.pluginIsActive = False def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" self.iface.removePluginMenu(u"WaysCalc", self.action_intersection) self.iface.removePluginMenu(u"WaysCalc", self.action_allintersection) self.iface.removePluginMenu(u"WaysCalc", self.action_initLayerWays) self.iface.removeToolBarIcon(self.action_intersection) self.iface.removeToolBarIcon(self.action_intersection) self.iface.removeToolBarIcon(self.toolButton_action) self.action_intersection.triggered.disconnect(self.run_intersection) # пересекающиеся маршруты self.action_allintersection.triggered.disconnect(self.run_allintersection) # пересекающиеся все маршруты self.action_initLayerWays.triggered.disconnect(self.run_initLayerWays) # выбор слоя сравнения self.pointEmitterIntersection.canvasClicked.connect(self.pointEmitterIntersectioncanvasClicked) self.iface.mapCanvas().currentLayerChanged.disconnect(self.onCurrentLayerChanged) if self.IW is not None: self.IW.onUnLoadModule() def init_dock_inters_ways(self, dock): """Run method that loads and starts the plugin""" # if not self.pluginIsActive: self.pluginIsActive = True if self.dockwidget_inters_ways == None: self.dockwidget_inters_ways = WaysCalcDockWidget() self.dockwidget_inters_ways.closingPlugin.connect(self.onClosePlugin) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget_inters_ways) self.dockwidget_inters_ways.hide() self.dockwidget_inters_ways.setWindowTitle(u'WaysCalc: Поиск пересечений для линейного объекта') def init_dock_inters_allways(self, dock): """Run method that loads and starts the plugin""" # if not self.pluginIsActive: self.pluginIsActive = True if self.dockwidget_inters_allways == None: self.dockwidget_inters_allways = WaysCalcDockWidget() self.dockwidget_inters_allways.closingPlugin.connect(self.onClosePlugin) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget_inters_allways) self.dockwidget_inters_allways.hide() self.dockwidget_inters_allways.setWindowTitle(u'WaysCalc: Поиск пересечений всех линейных объектов') def onCurrentLayerChanged(self): self.iface.mapCanvas().unsetMapTool(self.pointEmitterIntersection) #-------------------------------------------------------------------------- # def loadSettings(self): # with open(os.path.join(self.plugin_dir, "settings.json"), "r") as read_file: # self.settings = json.load(read_file) #--------INTERSECTION WAYS------------------------------------------------- def run_initLayerWays(self): if self.IW is None: self.init_IW() self.IW.initLayerWays() def run_intersection(self): self.toolButton.setDefaultAction(self.action_intersection) self.init_dock_inters_ways(self.dockwidget_inters_ways) if self.IW is None: self.init_IW() if self.IW.checkLayers(): self.iface.mapCanvas().setMapTool(self.pointEmitterIntersection) def pointEmitterIntersectioncanvasClicked(self, point, button): self.IW.insersection_take_way(point) def init_IW(self): self.IW = IntersectionWays(self.iface, self.dockwidget_inters_ways) #-------- END INTERSECTION WAYS--------------------------------------------- def init_IAW(self): self.IAW = IntersectionAllWays(self.iface, self.dockwidget_inters_allways) def run_allintersection(self): self.init_dock_inters_allways(self.dockwidget_inters_allways) if self.IAW is None: self.init_IAW() self.IAW.showClickedFeaturesList()
class PdokServicesPlugin(object): def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # docked or dialog, defaults to dialog # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if isinstance(QSettings().value("/pdokservicesplugin/docked"), QVariant): # self.docked = QSettings().value("/pdokservicesplugin/docked", QVariant(False)) # else: # self.docked = QSettings().value("/pdokservicesplugin/docked", False) # # # Create the dialog and keep reference # if "True" == self.docked or "true" == self.docked or True is self.docked: # self.dlg = PdokServicesPluginDockWidget() # self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dlg) # else: # self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) self.currentLayer = None self.SETTINGS_SECTION = '/pdokservicesplugin/' self.pointer = None self.pdokgeocoder = PDOKGeoLocator(self.iface) self.geocoderSourceModel = None def getSettingsValue(self, key, default=''): if QSettings().contains(self.SETTINGS_SECTION + key): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 return str(QSettings().value(key).toString()) else: return str(QSettings().value(key)) else: return default def setSettingsValue(self, key, value): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue(key, QVariant(value)) else: QSettings().setValue(key, value) def initGui(self): # Create action that will start plugin configuration self.runIcon = QIcon( os.path.join(self.plugin_dir, 'icon_add_service.svg')) self.run_action = QAction(self.runIcon, "PDOK Services plugin", self.iface.mainWindow()) self.run_button = QToolButton() self.run_button.setMenu(QMenu()) self.run_button.setPopupMode(QToolButton.MenuButtonPopup) self.run_button.setDefaultAction(self.run_action) self.servicesLoaded = False # connect the action to the run method self.run_action.triggered.connect(self.run) self.setupfq() # Add toolbar button and menu item #self.iface.addToolBarIcon(self.action) self.toolbar = self.iface.addToolBar("PDOK services plugin") self.toolbar.setObjectName("PDOK services plugin") #self.toolbar.addAction(self.run_action) self.toolbar.addWidget(self.run_button) #self.run_button.menu().addSection('Favorieten') self.favourite_1_action = QAction('Favoriet 1', self.iface.mainWindow()) self.favourite_1_action.setIcon(self.runIcon) self.favourite_1_action.triggered.connect( lambda: self.load_favourite(1)) self.set_favourite_action(self.favourite_1_action, 1) self.run_button.menu().addAction(self.favourite_1_action) self.favourite_2_action = QAction('Favoriet 2', self.iface.mainWindow()) self.favourite_2_action.setIcon(self.runIcon) self.favourite_2_action.triggered.connect( lambda: self.load_favourite(2)) self.set_favourite_action(self.favourite_2_action, 2) self.run_button.menu().addAction(self.favourite_2_action) # TODO :-) #self.run_button.menu().addSection('Meest Recent') #self.run_button.menu().addSeparator() self.toolbarSearch = QLineEdit() self.toolbarSearch.setMaximumWidth(200) self.toolbarSearch.setAlignment(Qt.AlignLeft) self.toolbarSearch.setPlaceholderText("PDOK Locatieserver zoek") self.toolbar.addWidget(self.toolbarSearch) self.toolbarSearch.returnPressed.connect(self.searchAddressFromToolbar) # address/point cleanup eraserIcon = QIcon( os.path.join(self.plugin_dir, 'icon_remove_cross.svg')) self.clean_action = QAction(eraserIcon, "Cleanup", self.eraseAddress()) self.toolbar.addAction(self.clean_action) self.clean_action.triggered.connect(self.eraseAddress) self.clean_action.setEnabled(False) self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.run_action) # about self.aboutAction = QAction(QIcon(":/plugins/pdokservicesplugin/icon_help.png"), \ "About", self.iface.mainWindow()) self.aboutAction.setWhatsThis("Pdok Services Plugin About") self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.aboutAction) self.aboutAction.triggered.connect(self.about) self.dlg.ui.btnLoadLayer.clicked.connect(self.loadService) self.dlg.geocoderSearch.returnPressed.connect(self.searchAddress) self.dlg.geocoderSearch.textEdited.connect(self.searchAddress) self.dlg.geocoderSearch.setPlaceholderText( "PDOK Locatieserver zoek, bv postcode of postcode huisnummer") self.dlg.geocoderResultSearch.textChanged.connect( self.filterGeocoderResult) self.dlg.geocoderResultSearch.setPlaceholderText( "een of meer zoekwoorden uit resultaat") #self.iface.mapCanvas().renderStarting.connect(self.extentsChanged) ui = self.dlg.ui cbxs = [ ui.cbx_gem, ui.cbx_wpl, ui.cbx_weg, ui.cbx_pcd, ui.cbx_adr, ui.cbx_pcl, ui.cbx_hmp ] # connect all fq checkboxes with suggest, so upon a change in fq filter we re-search for cbx in cbxs: cbx.stateChanged.connect(self.searchAddress) self.run(True) # for now hiding the pointer as soon as the extent changes #def extentsChanged(self): # self.removePointer() # 2021-02 hiding the check JSON: to much hassle def checkPdokJson(self): myversion = self.getSettingsValue('pdokversion', '1') msgtxt = '' msglvl = 0 # QgsMessageBar.INFO try: response = urllib.request.urlopen( 'http://www.qgis.nl/pdok.version') str_response = response.read().decode('utf-8') pdokversion = json.loads(str_response) if pdokversion > int(myversion): response = urllib.request.urlopen( 'http://www.qgis.nl/pdok.json') str_response = response.read().decode('utf-8') pdokjson = json.loads(str_response) with open(os.path.join(self.plugin_dir, 'pdok.json'), 'w') as outfile: json.dump(pdokjson, outfile) msgtxt = "De laatste versie is opgehaald en zal worden gebruikt " + \ str(pdokversion) + ' (was ' + myversion +')' self.servicesLoaded = False # reset reading of json self.run() self.setSettingsValue('pdokversion', pdokversion) else: msgtxt = "Geen nieuwere versie beschikbaar dan " + str( pdokversion) except Exception as e: #print e msgtxt = "Fout bij ophalen van service info. Netwerk probleem?" msglvl = 2 # QgsMessageBar.CRITICAL # msg if hasattr(self.iface, 'messageBar'): self.iface.messageBar().pushMessage("PDOK services update", msgtxt, level=msglvl, duration=10) else: # 1.8 QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin", msgtxt) def showAndRaise(self): self.dlg.show() self.dlg.raise_() # also remove the pointer self.removePointer() def about(self): infoString = "Written by Richard Duivenvoorde\nEmail - [email protected]\n" infoString += "Company - Zuidt - http://www.zuidt.nl\n" infoString += "Source: https://github.com/rduivenvoorde/pdokservicesplugin" QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin About", infoString) def unload(self): self.removePointer() # Remove the plugin menu item and icon self.iface.removePluginMenu("&Pdok Services Plugin", self.run_action) self.iface.removePluginMenu("&Pdok Services Plugin", self.aboutAction) del self.toolbarSearch del self.run_action del self.aboutAction def showService(self, selectedIndexes): if len(selectedIndexes) == 0: self.currentLayer = None self.dlg.ui.layerInfo.setHtml('') self.dlg.ui.comboSelectProj.clear() return # needed to scroll To the selected row incase of using the keyboard / arrows self.dlg.servicesView.scrollTo( self.dlg.servicesView.selectedIndexes()[0]) # itemType holds the data (== column 1) self.currentLayer = self.dlg.servicesView.selectedIndexes()[1].data( Qt.UserRole) if isinstance(self.currentLayer, QVariant): self.currentLayer = self.currentLayer.toMap() # QGIS 1.8: QVariants currentLayer = {} for key in list(self.currentLayer.keys()): val = self.currentLayer[key] currentLayer[str(key)] = str(val.toString()) self.currentLayer = currentLayer url = self.currentLayer['url'] title = self.currentLayer['title'] style = '' if 'style' in self.currentLayer: style = self.currentLayer['style'] title += f' [{style}]' servicetitle = self.currentLayer['servicetitle'] layername = self.currentLayer['layers'] abstract = self.currentLayer['abstract'] stype = self.currentLayer['type'].upper() minscale = '' if 'minscale' in self.currentLayer and self.currentLayer[ 'minscale'] != None and self.currentLayer['minscale'] != '': minscale = "min. schaal 1:" + self.currentLayer['minscale'] maxscale = '' if 'maxscale' in self.currentLayer and self.currentLayer[ 'maxscale'] != None and self.currentLayer['maxscale'] != '': maxscale = "max. schaal 1:" + self.currentLayer['maxscale'] self.dlg.ui.layerInfo.setText('') self.dlg.ui.btnLoadLayer.setEnabled(True) self.dlg.ui.layerInfo.setHtml( '<h4>%s</h4><h3>%s</h3><lu><li>%s</li><li> </li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li></lu>' % (servicetitle, title, abstract, stype, url, layername, style, minscale, maxscale)) self.dlg.ui.comboSelectProj.clear() if stype == "WMS": try: crs = self.currentLayer['crs'] except KeyError: crs = 'EPSG:28992' crs = crs.split(',') self.dlg.ui.comboSelectProj.addItems(crs) for i in range(len(crs)): if crs[i] == 'EPSG:28992': self.dlg.ui.comboSelectProj.setCurrentIndex(i) if stype == "WMTS": tilematrixsets = self.currentLayer['tilematrixsets'].split(',') self.dlg.ui.comboSelectProj.addItems(tilematrixsets) for i in range(len(tilematrixsets)): if tilematrixsets[i].startswith('EPSG:28992'): self.dlg.ui.comboSelectProj.setCurrentIndex(i) def set_favourite_action(self, action, favourite_number): if QSettings().contains( f'/pdokservicesplugin/favourite_{favourite_number}'): layer = QSettings().value( f'/pdokservicesplugin/favourite_{favourite_number}', None) if layer: action.setToolTip(layer['title'].capitalize()) title = layer['title'].capitalize() if 'style' in layer: style = layer['style'] title += f' [{style}]' if 'type' in layer: stype = layer['type'].upper() title += f' ({stype})' action.setText(title) action.setIcon(self.runIcon) def load_favourite(self, favourite_number): if QSettings().contains( f'/pdokservicesplugin/favourite_{favourite_number}'): layer = QSettings().value( f'/pdokservicesplugin/favourite_{favourite_number}', None) if layer and layer in self.pdok['services']: self.currentLayer = layer self.loadService() return QMessageBox.warning(self.iface.mainWindow(), "Geen Favoriet aanwezig (of verouderd)...", ( \ "Maak een Favoriet aan door in de dialoog met services en lagen\n via het context menu (rechter muisknop) een Favoriet te kiezen..." ), QMessageBox.Ok, QMessageBox.Ok) self.run() def loadService(self): if self.currentLayer == None: return servicetype = self.currentLayer['type'] url = self.currentLayer['url'] # some services have an url with query parameters in it, we have to urlencode those: location, query = urllib.parse.splitquery(url) url = location # RD: 20200820: lijkt of het quoten van de query problemen geeft bij WFS, is/was dit nodig??? #if query != None and query != '': # url +=('?'+urllib.parse.quote_plus(query)) title = self.currentLayer['title'] if 'style' in self.currentLayer: style = self.currentLayer['style'] title += f' [{style}]' else: style = '' # == default for this service layers = self.currentLayer['layers'] # mmm, tricky: we take the first one while we can actually want png/gif or jpeg if servicetype == "wms": imgformat = self.currentLayer['imgformats'].split(',')[0] if self.dlg.ui.comboSelectProj.currentIndex() == -1: crs = 'EPSG:28992' else: crs = self.dlg.ui.comboSelectProj.currentText() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 uri = url self.iface.addRasterLayer( uri, # service uri title, # name for layer (as seen in QGIS) "wms", # dataprovider key [layers], # array of layername(s) for provider (id's) ["" ], # array of stylename(s) NOTE: ignoring styles here!!! imgformat, # image format searchstring crs) # crs code searchstring else: # qgis > 1.8 uri = "crs=" + crs + "&layers=" + layers + "&styles=" + style + "&format=" + imgformat + "&url=" + url self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wmts": if Qgis.QGIS_VERSION_INT < 10900: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( "Sorry, dit type layer: '" + servicetype.upper() + "' \nkan niet worden geladen in deze versie van QGIS.\nMisschien kunt u QGIS 2.0 installeren (die kan het WEL)?\nOf is de laag niet ook beschikbaar als wms of wfs?" ), QMessageBox.Ok, QMessageBox.Ok) return if self.dlg.ui.comboSelectProj.currentIndex() == -1: tilematrixset = 'EPSG:28992' else: tilematrixset = self.dlg.ui.comboSelectProj.currentText() imgformat = self.currentLayer['imgformats'].split(',')[0] # special case for luchtfoto #if layers=="luchtfoto": # # tileMatrixSet=nltilingschema&crs=EPSG:28992&layers=luchtfoto&styles=&format=image/jpeg&url=http://geodata1.nationaalgeoregister.nl/luchtfoto/wmts/1.0.0/WMTSCapabilities.xml # # {u'layers': u'luchtfoto', u'imgformats': u'image/jpeg', u'title': u'PDOK-achtergrond luchtfoto', u'url': u'http://geodata1.nationaalgeoregister.nl/luchtfoto/wms', u'abstract': u'', u'tilematrixsets': u'nltilingschema', u'type': u'wmts'} # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url #else: # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url; #uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; if tilematrixset.startswith('EPSG:'): crs = tilematrixset i = crs.find(':', 5) if i > -1: crs = crs[:i] elif tilematrixset.startswith('OGC:1.0'): crs = 'EPSG:3857' uri = "tileMatrixSet=" + tilematrixset + "&crs=" + crs + "&layers=" + layers + "&styles=default&format=" + imgformat + "&url=" + url #print "############ PDOK URI #################" #print uri self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wfs": location, query = urllib.parse.splitquery(url) #uri = location+"?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME="+layers+"&SRSNAME=EPSG:28992" #uri = location + "?SERVICE=WFS&REQUEST=GetFeature&TYPENAME=" + layers + "&SRSNAME=EPSG:28992" # adding a bbox paramater forces QGIS to NOT cache features but retrieve new features all the time # QGIS will update the BBOX to the right value #uri += "&BBOX=-10000,310000,290000,650000" uri = " pagingEnabled='true' restrictToRequestBBOX='1' srsname='EPSG:28992' typename='" + layers + "' url='" + url + "' version='2.0.0' " self.iface.addVectorLayer(uri, title, "WFS") elif servicetype == "wcs": # cache=AlwaysCache&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs uri = '' # cache=AlwaysCache # cache=PreferNetwork # cache=AlwaysNetwork # cache=AlwaysNetwork&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.1&identifier="+layers+"&url="+url # working for ahn1 ahn2 and ahn3: GEOTIFF_FLOAT32 format = 'GEOTIFF_FLOAT32' # working for ahn25m is only image/tiff if layers == 'ahn25m': format = 'image/tiff' # we handcrated some wcs layers with 2 different image formats: tiff (RGB) and tiff (float32): if 'imgformats' in self.currentLayer: format = self.currentLayer['imgformats'].split(',')[0] uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=" + format + "&identifier=" + layers + "&url=" + url #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format="+format+"&version=1.1.2&identifier=" + layers + "&url=" + url #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.2&identifier=" + layers + "&url=" + url self.iface.addRasterLayer(uri, title, "wcs") else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( "Sorry, dit type layer: '" + servicetype.upper() + "' \nkan niet worden geladen door de plugin of door QGIS.\nIs het niet beschikbaar als wms, wmts of wfs?" ), QMessageBox.Ok, QMessageBox.Ok) return def filterGeocoderResult(self, string): #print "filtering geocoder results: %s" % string self.dlg.geocoderResultView.selectRow(0) self.geocoderProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.geocoderProxyModel.setFilterFixedString(string) def searchAddressFromToolbar(self): self.removePointer() self.geocoderSourceModel.clear() self.geocode() def searchAddress(self): self.removePointer() #print "search geocoder for: %s" % self.dlg.geocoderSearch.text() self.geocoderSourceModel.clear() #self.geocode(self.dlg.geocoderSearch.text()) self.suggest() def eraseAddress(self): """ clean the input and remove the pointer """ self.removePointer() if self.geocoderSourceModel is not None: self.geocoderSourceModel.clear() if self.dlg.geocoderSearch is not None: self.dlg.geocoderSearch.clear() if self.toolbarSearch is not None: self.toolbarSearch.clear() def filterLayers(self, string): # remove selection if one row is selected self.dlg.servicesView.selectRow(0) #self.currentLayer = None self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxyModel.setFilterFixedString(string) #def addSourceRow(self, service, layer): def addSourceRow(self, serviceLayer): # you can attache different "data's" to to an QStandarditem # default one is the visible one: itemType = QStandardItem("%s" % (serviceLayer["type"].upper())) # userrole is a free form one: # only attach the data to the first item # service layer = a dict/object with all props of the layer itemType.setData(serviceLayer, Qt.UserRole) itemType.setToolTip( "%s - %s" % (serviceLayer["type"].upper(), serviceLayer["title"])) # only wms services have styles (sometimes) layername = serviceLayer["title"] if 'style' in serviceLayer: itemLayername = QStandardItem( "%s [%s]" % (serviceLayer["title"], serviceLayer["style"])) layername = "%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) else: itemLayername = QStandardItem("%s" % (serviceLayer["title"])) itemLayername.setToolTip( "%s - %s" % (serviceLayer["type"].upper(), serviceLayer["servicetitle"])) # itemFilter is the item used to search filter in. That is why layername is a combi of layername + filter here itemFilter = QStandardItem( "%s %s %s %s" % (serviceLayer["type"], layername, serviceLayer["servicetitle"], serviceLayer["abstract"])) itemServicetitle = QStandardItem("%s" % (serviceLayer["servicetitle"])) itemServicetitle.setToolTip( "%s - %s" % (serviceLayer["type"].upper(), serviceLayer["title"])) self.sourceModel.appendRow( [itemLayername, itemType, itemServicetitle, itemFilter]) # run method that performs all the real work def run(self, hiddenDialog=False): # enable possible remote pycharm debugging #import pydevd #pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # last viewed/selected tab if QSettings().contains("/pdokservicesplugin/currenttab"): if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 self.dlg.tabs.widget(QSettings().value( "/pdokservicesplugin/currenttab").toInt()[0]) else: self.dlg.tabs.widget( int(QSettings().value("/pdokservicesplugin/currenttab"))) if self.servicesLoaded == False: pdokjson = os.path.join(self.plugin_dir, "pdok.json") with open(pdokjson, 'r', encoding='utf-8') as f: self.pdok = json.load(f) print(f'self.pdok type = {type(self.pdok)}') self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) # filter == search on itemFilter column: self.proxyModel.setFilterKeyColumn(3) self.dlg.servicesView.setModel(self.proxyModel) self.dlg.servicesView.setEditTriggers( QAbstractItemView.NoEditTriggers) self.geocoderProxyModel = QSortFilterProxyModel() self.geocoderSourceModel = QStandardItemModel() self.geocoderProxyModel.setSourceModel(self.geocoderSourceModel) self.geocoderProxyModel.setFilterKeyColumn(0) self.dlg.geocoderResultView.setModel(self.geocoderProxyModel) self.dlg.geocoderResultView.setEditTriggers( QAbstractItemView.NoEditTriggers) #{"services":[ # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"}, # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"} # ]} # for service in self.pdok["services"]: # service[layer] was an array if isinstance(service["layers"], str): self.addSourceRow(service) self.dlg.layerSearch.textChanged.connect(self.filterLayers) self.dlg.layerSearch.setPlaceholderText( "woord uit laagnaam, type of service ") self.dlg.servicesView.selectionModel().selectionChanged.connect( self.showService) self.dlg.servicesView.doubleClicked.connect(self.loadService) self.dlg.servicesView.setContextMenuPolicy(Qt.CustomContextMenu) self.dlg.servicesView.customContextMenuRequested.connect( self.make_favourite) # actually I want to load a service when doubleclicked on header # but as I cannot get this to work, let's disable clicking it then self.dlg.servicesView.verticalHeader().setSectionsClickable(False) self.dlg.servicesView.horizontalHeader().setSectionsClickable( False) #self.dlg.geocoderResultView.doubleClicked.connect(self.zoomToAddress) self.dlg.geocoderResultView.selectionModel( ).selectionChanged.connect(self.zoomToAddress) # hide itemFilter column: self.dlg.servicesView.hideColumn(3) self.servicesLoaded = True self.sourceModel.setHeaderData(2, Qt.Horizontal, "Service") self.sourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.sourceModel.setHeaderData(0, Qt.Horizontal, "Laagnaam [style]") self.sourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) #self.dlg.servicesView.verticalHeader().hide() #self.dlg.servicesView.resizeColumnsToContents() self.dlg.servicesView.setColumnWidth( 0, 300) # set name to 300px (there are some huge layernames) self.dlg.servicesView.horizontalHeader().setStretchLastSection(True) # show the dialog ? if not hiddenDialog: self.dlg.show() # Run the dialog event loop #result = self.dlg.exec_() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue("/pdokservicesplugin/currenttab", QVariant(self.dlg.tabs.currentIndex())) else: QSettings().setValue("/pdokservicesplugin/currenttab", self.dlg.tabs.currentIndex()) self.removePointer() def make_favourite(self, position): menu = QMenu() create_fav1_action = menu.addAction("Maak Deze Laag Favoriet 1") create_fav2_action = menu.addAction("Maak Deze Laag Favoriet 2") action = menu.exec_(self.dlg.servicesView.mapToGlobal(position)) if action == create_fav1_action: QSettings().setValue("/pdokservicesplugin/favourite_1", self.currentLayer) self.set_favourite_action(self.favourite_1_action, 1) elif action == create_fav2_action: QSettings().setValue("/pdokservicesplugin/favourite_2", self.currentLayer) self.set_favourite_action(self.favourite_2_action, 2) def setupfq(self): """ Setup the fq checkboxes in the gui, by looking into the settings for the 'pdokservicesplugin/checkedfqs' key, which contains a list of type strings like ['weg','adres'] """ checked_fqs = self.getSettingsValue('checkedfqs', []) #self.info('setup fq: {}'.format(checked_fqs)) if len(checked_fqs ) > 0: # else there is not saved state... take gui defaults self.dlg.ui.cbx_gem.setChecked('gemeente' in checked_fqs) self.dlg.ui.cbx_wpl.setChecked('woonplaats' in checked_fqs) self.dlg.ui.cbx_weg.setChecked('weg' in checked_fqs) self.dlg.ui.cbx_pcd.setChecked('postcode' in checked_fqs) self.dlg.ui.cbx_adr.setChecked('adres' in checked_fqs) self.dlg.ui.cbx_pcl.setChecked('perceel' in checked_fqs) self.dlg.ui.cbx_hmp.setChecked('hectometerpaal' in checked_fqs) def createfq(self): """ This creates a fq-string (Filter Query, see https://github.com/PDOK/locatieserver/wiki/Zoekvoorbeelden-Locatieserver) Based on the checkboxes in the dialog. Defaults to '' Example: 'fq=+type:adres+type:gemeente' (only gemeente AND addresses) :return: """ fqlist = [] if self.dlg.ui.cbx_gem.isChecked(): fqlist.append('gemeente') if self.dlg.ui.cbx_wpl.isChecked(): fqlist.append('woonplaats') if self.dlg.ui.cbx_weg.isChecked(): fqlist.append('weg') if self.dlg.ui.cbx_pcd.isChecked(): fqlist.append('postcode') if self.dlg.ui.cbx_adr.isChecked(): fqlist.append('adres') if self.dlg.ui.cbx_pcl.isChecked(): fqlist.append('perceel') if self.dlg.ui.cbx_hmp.isChecked(): fqlist.append('hectometerpaal') self.setSettingsValue('checkedfqs', fqlist) #self.info(self.getSettingsValue('checkedfqs', ['leeg?'])) fq = '' if len(fqlist) > 0: fq = '&fq=+type:' + '+type:'.join(fqlist) return fq def suggest(self): self.dlg.ui.lookupinfo.setHtml('') search_text = self.dlg.geocoderSearch.text() if len(search_text) <= 1: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "meer input aub: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) return # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "zoeken: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) results = self.pdokgeocoder.suggest(search_text, self.createfq()) if len(results) == 0: # ignore, as we are suggesting, maybe more characters will reveal something... return for result in results: #print address adrestekst = QStandardItem("%s" % (result["adrestekst"])) adrestekst.setData(result, Qt.UserRole) type = QStandardItem("%s" % (result["type"])) id = QStandardItem("%s" % (result["id"])) score = QStandardItem("%s" % (result["score"])) adrestekst.setData(result, Qt.UserRole) self.geocoderSourceModel.appendRow([adrestekst, type]) self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment( Qt.AlignLeft) self.dlg.geocoderResultView.resizeColumnsToContents() self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection( True) def geocode(self): self.dlg.geocoderSearch.setText(self.toolbarSearch.text()) self.suggest() if self.dlg.geocoderResultView.model().rowCount() > 0: self.dlg.geocoderResultView.selectRow(0) self.zoomToAddress() else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ "Niets gevonden.\nProbeer een andere spelling, of alleen postcode/huisnummer?\n\nSelecteer meer (Locatieserver) 'typen' in de PdokServicesPlugin dialoog.\n\nOf gebruik de 'PDOK geocoder'-tab in de PdokServicesPlugin dialoog." ), QMessageBox.Ok, QMessageBox.Ok) # def geocode(self): # self.dlg.ui.lookupinfo.setHtml('') # search_text = self.toolbarSearch.text() # addresses = self.pdokgeocoder.search(search_text) # if len(addresses) == 0: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "Niets gevonden. Probeer een andere spelling of alleen postcode/huisnummer." # ), QMessageBox.Ok, QMessageBox.Ok) # return # for address in addresses: # #print address # adrestekst = QStandardItem("%s" % (address["adrestekst"])) # adrestekst.setData(address, Qt.UserRole) # straat = QStandardItem("%s" % (address["straat"])) # nummer = QStandardItem("%s" % (address["nummer"])) # postcode = QStandardItem("%s" % (address["postcode"])) # plaats = QStandardItem("%s" % (address["plaats"])) # gemeente = QStandardItem("%s" % (address["gemeente"])) # provincie = QStandardItem("%s" % (address["provincie"])) # self.geocoderSourceModel.appendRow([adrestekst, straat, nummer, postcode, plaats, gemeente, provincie]) # # self.dlg.geocoderResultView.selectRow(0) # self.zoomToAddress() # # self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") # self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Straat") # self.geocoderSourceModel.setHeaderData(2, Qt.Horizontal, "Nr") # self.geocoderSourceModel.setHeaderData(3, Qt.Horizontal, "Postcode") # self.geocoderSourceModel.setHeaderData(4, Qt.Horizontal, "Plaats") # self.geocoderSourceModel.setHeaderData(5, Qt.Horizontal, "Gemeente") # self.geocoderSourceModel.setHeaderData(6, Qt.Horizontal, "Provincie") # # self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(3).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(4).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(5).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(6).setTextAlignment(Qt.AlignLeft) # # self.dlg.geocoderResultView.resizeColumnsToContents() # self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def zoomToAddress(self): # get x,y from data of record self.removePointer() data = self.dlg.geocoderResultView.selectedIndexes()[0].data( Qt.UserRole) if 'centroide_rd' in data: # free OR lookup service geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] else: # no centroid yet, probably only object id, retrieve it via lookup service id = data['id'] data = self.pdokgeocoder.lookup(id) geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] lookup_data = data['data'] lis = '' for key in lookup_data.keys(): lis = lis + '<li>{}: {}</li>'.format(key, lookup_data[key]) self.dlg.ui.lookupinfo.setHtml('<h4>{}</h4><lu>{}</lu>'.format( adrestekst, lis)) # just always transform from 28992 to mapcanvas crs crs = self.iface.mapCanvas().mapSettings().destinationCrs() crs28992 = QgsCoordinateReferenceSystem() crs28992.createFromId(28992) crsTransform = QgsCoordinateTransform(crs28992, crs, QgsProject.instance()) z = 1587 if adrestekst.lower().startswith('adres'): z = 794 elif adrestekst.lower().startswith('perceel'): z = 794 elif adrestekst.lower().startswith('hectometer'): z = 1587 elif adrestekst.lower().startswith('straat'): z = 3175 elif adrestekst.lower().startswith('postcode'): z = 6350 elif adrestekst.lower().startswith('woonplaats'): z = 25398 elif adrestekst.lower().startswith('gemeente'): z = 50797 elif adrestekst.lower().startswith('provincie'): z = 812750 geom.transform(crsTransform) center = geom.asPoint() self.setPointer(center) # zoom to with center is actually setting a point rectangle and then zoom rect = QgsRectangle(center, center) self.iface.mapCanvas().setExtent(rect) self.iface.mapCanvas().zoomScale(z) self.iface.mapCanvas().refresh() def setPointer(self, point): self.removePointer() self.pointer = QgsVertexMarker(self.iface.mapCanvas()) self.pointer.setColor(QColor(255, 255, 0)) self.pointer.setIconSize(10) self.pointer.setPenWidth(5) self.pointer.setCenter(point) self.clean_action.setEnabled(True) def removePointer(self): if self.pointer is not None and self.pointer.scene() is not None: self.iface.mapCanvas().scene().removeItem(self.pointer) self.pointer = None self.clean_action.setEnabled(False) def info(self, msg=""): QgsMessageLog.logMessage('{}'.format(msg), 'PDOK-services Plugin', Qgis.Info)
class QRAVE: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface self.tm = QgsApplication.taskManager() self.qproject = QgsProject.instance() self.pluginIsActive = False self.dockwidget = None self.metawidget = None # Populated on load from a URL self.acknowledgements = None # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'QRAVE_{}.qm'.format(locale)) self.settings = Settings(iface=self.iface) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&Riverscapes Plugin (QRAVE)') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'QRAVE') self.toolbar.setObjectName(u'QRAVE') def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('QRAVE', message) def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" self.qproject.readProject.connect(self.onProjectLoad) self.openAction = QAction( QIcon(':/plugins/qrave_toolbar/RaveAddIn_16px.png'), self.tr(u'Riverscapes Plugin (QRAVE)'), self.iface.mainWindow()) self.openAction.triggered.connect(self.toggle_widget) self.openAction.setStatusTip('Toggle the project viewer') self.openAction.setWhatsThis('Toggle the project viewer') self.openProjectAction = QAction( QIcon(':/plugins/qrave_toolbar/OpenProject.png'), self.tr(u'Open Riverscapes Project'), self.iface.mainWindow()) self.openProjectAction.triggered.connect(self.projectBrowserDlg) self.openProjectAction.setStatusTip('Open QRAVE project') self.openProjectAction.setWhatsThis('Open QRAVE project') self.helpButton = QToolButton() self.helpButton.setToolButtonStyle(Qt.ToolButtonTextOnly) self.helpButton.setMenu(QMenu()) self.helpButton.setPopupMode(QToolButton.MenuButtonPopup) m = self.helpButton.menu() # TODO: get the local help working # self.helpAction = QAction( # QIcon(':/plugins/qrave_toolbar/Help.png'), # self.tr('Help'), # self.iface.mainWindow() # ) # self.helpAction.triggered.connect(partial(showPluginHelp, None, filename=':/plugins/qrave_toolbar/help/build/html/index')) # self.websiteAction = QAction( # QIcon(':/plugins/qrave_toolbar/RaveAddIn_16px.png'), # self.tr('Website'), # self.iface.mainWindow() # ) # self.websiteAction.triggered.connect(lambda: QDesktopServices.openUrl(QUrl("http://rave.riverscapes.xyz"))) self.helpAction = QAction(QIcon(':/plugins/qrave_toolbar/Help.png'), self.tr('Help'), self.iface.mainWindow()) self.helpAction.triggered.connect(lambda: QDesktopServices.openUrl( QUrl("http://rave.riverscapes.xyz"))) self.raveOptionsAction = QAction( QIcon(':/plugins/qrave_toolbar/Options.png'), self.tr('Settings'), self.iface.mainWindow()) self.raveOptionsAction.triggered.connect(self.options_load) self.net_sync_action = QAction( QIcon(':/plugins/qrave_toolbar/refresh.png'), self.tr('Update resources'), self.iface.mainWindow()) self.net_sync_action.triggered.connect( lambda: self.net_sync_load(force=True)) self.find_resources_action = QAction( QIcon(':/plugins/qrave_toolbar/BrowseFolder.png'), self.tr('Find Resources folder'), self.iface.mainWindow()) self.find_resources_action.triggered.connect(self.locateResources) self.about_action = QAction( QIcon(':/plugins/qrave_toolbar/RaveAddIn_16px.png'), self.tr('About QRAVE'), self.iface.mainWindow()) self.about_action.triggered.connect(self.about_load) m.addAction(self.helpAction) # m.addAction(self.websiteAction) m.addAction(self.raveOptionsAction) m.addAction(self.net_sync_action) m.addSeparator() m.addAction(self.find_resources_action) m.addAction(self.about_action) self.helpButton.setDefaultAction(self.helpAction) self.toolbar.addAction(self.openAction) self.toolbar.addAction(self.openProjectAction) self.toolbar.addWidget(self.helpButton) # Do a check to see if the stored version is different than the current version lastVersion = self.settings.getValue('pluginVersion') # This does a lazy netsync (i.e. it will run it if it feels like it) versionChange = lastVersion != __version__ self.net_sync_load(force=versionChange) if versionChange: QgsMessageLog.logMessage( "Version change detected: {} ==> {}".format( lastVersion, __version__), 'QRAVE', level=Qgis.Info) self.settings.setValue('pluginVersion', __version__) def onProjectLoad(self, doc): # If the project has the plugin enabled then restore it. qrave_enabled, type_conversion_ok = self.qproject.readEntry( CONSTANTS['settingsCategory'], 'enabled') if type_conversion_ok and qrave_enabled == '1': self.toggle_widget(forceOn=True) if self.dockwidget is not None: self.dockwidget.reload_tree() def onClosePlugin(self): """Cleanup necessary items here when plugin dockwidget is closed""" if self.metawidget is not None: self.metawidget.hide() if self.dockwidget is not None: self.dockwidget.hide() # disconnects self.dockwidget.closingPlugin.disconnect(self.onClosePlugin) self.qproject.readProject.disconnect(self.onProjectLoad) # remove this statement if dockwidget is to remain # for reuse if plugin is reopened self.dockwidget = None self.pluginIsActive = False def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" if self.metawidget is not None: self.metawidget.hide() if self.dockwidget is not None: self.dockwidget.hide() for action in self.actions: self.iface.removePluginMenu( self.tr(u'&Riverscapes Plugin (QRAVE)'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def toggle_widget(self, forceOn=False): """Toggle the widget open and closed when clicking the toolbar""" if not self.pluginIsActive: self.pluginIsActive = True # dockwidget may not exist if: # first run of plugin # removed on close (see self.onClosePlugin method) if self.dockwidget is None: # Create the dockwidget (after translation) and keep reference self.dockwidget = QRAVEDockWidget() self.metawidget = QRAVEMetaWidget() # Hook metadata changes up to the metawidget self.dockwidget.metaChange.connect(self.metawidget.load) # Run a network sync operation to get the latest stuff. Don't force it. # This is just a quick check self.net_sync_load() # connect to provide cleanup on closing of dockwidget self.dockwidget.closingPlugin.connect(self.onClosePlugin) # show the dockwidget self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dockwidget) self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.metawidget) self.dockwidget.show() else: if self.dockwidget is not None: if self.dockwidget.isHidden(): self.dockwidget.show() elif forceOn is False: self.dockwidget.hide() # The metawidget always starts hidden if self.metawidget is not None: self.metawidget.hide() if self.dockwidget is not None and not self.dockwidget.isHidden(): self.qproject.writeEntry(CONSTANTS['settingsCategory'], 'enabled', True) else: self.qproject.removeEntry(CONSTANTS['settingsCategory'], 'enabled') def net_sync_load(self, force=False): """ Periodically check for new files """ lastDigestSync = self.settings.getValue('lastDigestSync') lastVersion = self.settings.getValue('pluginVersion') currTime = int(time()) # timestamp in seconds plugin_init = self.settings.getValue('initialized') autoUpdate = self.settings.getValue('autoUpdate') self.netsync = NetSync('Sync QRAVE resource files') perform_sync = False # setting the force flag overrides everything else if force: perform_sync = True # Otherwise you only get a sync if 'autoUpdate' is turned on elif autoUpdate: # If this is an old version or the plugin is uninitialized if not plugin_init or lastVersion != __version__ or self.netsync.need_sync: perform_sync = True # If we haven't checked for more than `digestSyncFreqHours` hours elif isinstance(lastDigestSync, int) \ and ((currTime - lastDigestSync) / 3600) > CONSTANTS['digestSyncFreqHours']: perform_sync = True if perform_sync is False: self.netsync = None return # Trigger the dockwidget to repaint after the netsync if self.dockwidget is not None: self.netsync.taskCompleted.connect(self.dockwidget.reload_tree) # FOR DEBUGGING ONLY. NEVER IN PRODUCTION # self.netsync.run() # COMMENT THIS OUT AND USE THE LINE ABOVE FOR SYNCHRONOUS DEBUGGING self.tm.addTask(self.netsync) def projectBrowserDlg(self): """ Browse for a project directory :return: """ last_browse_path = self.settings.getValue('lastBrowsePath') last_dir = os.path.dirname( last_browse_path) if last_browse_path is not None else None dialog_return = QFileDialog.getOpenFileName( self.dockwidget, "Open a Riverscapes project", last_dir, self.tr("Riverscapes Project files (project.rs.xml)")) if dialog_return is not None and dialog_return[ 0] != "" and os.path.isfile(dialog_return[0]): # We set the proect path in the project settings. This way it will be saved with the QgsProject file if self.dockwidget is None or self.dockwidget.isHidden() is True: self.toggle_widget(forceOn=True) self.dockwidget.add_project(dialog_return[0]) def locateResources(self): """This the OS-agnostic "show in Finder" or "show in explorer" equivalent It should open the folder of the item in question Args: fpath (str): [description] """ qurl = QUrl.fromLocalFile(RESOURCES_DIR) QDesktopServices.openUrl(qurl) def options_load(self): """ Open the options/settings dialog """ dialog = OptionsDialog() if self.dockwidget: dialog.dataChange.connect(self.dockwidget.dataChange) dialog.exec_() def about_load(self): """ Open the About dialog """ dialog = AboutDialog() if self.acknowledgements is None: self.acknowledgements = requests.get( 'http://rave.riverscapes.xyz/dotnetack.html').text dialog.acknowledgements.setText(self.acknowledgements) dialog.exec_()