Exemple #1
0
class QgepPlugin(object):
    """
    A plugin for wastewater management
    http://www.github.com/qgep/QGEP
    """
    # The networkAnalyzer will manage the networklayers and pathfinding
    network_analyzer = None

    # Remember not to reopen the dock if there's already one opened
    profile_dock = None

    # Wizard
    wizarddock = None

    # The layer ids the plugin will need
    edgeLayer = None
    nodeLayer = None
    specialStructureLayer = None
    networkElementLayer = None

    profile = None

    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.nodes = None
        self.edges = None

        self.initLogger()
        setup_i18n()

    def tr(self, source_text):
        """
        This does not inherit from QObject but for the translation to work (in particular to have translatable strings
        picked up) we need a tr method.
        :rtype : unicode
        :param source_text: The text to translate
        :return: The translated text
        """
        return QApplication.translate('QgepPlugin', source_text)

    def initLogger(self):
        """
        Initializes the logger
        """
        self.logger = logging.getLogger(__package__)

        settings = QSettings()

        loglevel = settings.value("/QGEP/LogLevel", 'Warning')
        logfile = settings.value("/QGEP/LogFile", None)

        if hasattr(self.logger, 'qgepFileHandler'):
            self.logger.removeHandler(self.logger.qgepFileHandler)
            del self.logger.qgepFileHandler

        current_handlers = [h.__class__.__name__ for h in self.logger.handlers]
        if self.__class__.__name__ not in current_handlers:
            self.logger.addHandler(QgepQgsLogHandler())

        if logfile:
            log_handler = logging.FileHandler(logfile)
            fmt = logging.Formatter(LOGFORMAT)
            log_handler.setFormatter(fmt)
            self.logger.addHandler(log_handler)
            self.logger.fileHandler = log_handler

        if 'Debug' == loglevel:
            self.logger.setLevel(logging.DEBUG)
        elif 'Info' == loglevel:
            self.logger.setLevel(logging.INFO)
        elif 'Warning' == loglevel:
            self.logger.setLevel(logging.WARNING)
        elif 'Error' == loglevel:
            self.logger.setLevel(logging.ERROR)

        fp = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                          "metadata.txt")

        ini_text = QSettings(fp, QSettings.IniFormat)
        verno = ini_text.value("version")

        self.logger.info('QGEP plugin version ' + verno + ' started')

    def initGui(self):
        """
        Called to setup the plugin GUI
        """
        self.network_layer_notifier = QgepLayerNotifier(
            self.iface.mainWindow(), ['vw_network_node', 'vw_network_segment'])
        self.wastewater_networkelement_layer_notifier = QgepLayerNotifier(
            self.iface.mainWindow(), ['vw_wastewater_node', 'vw_qgep_reach'])
        self.toolbarButtons = []

        # Create toolbar button
        self.profileAction = QAction(
            QIcon(
                os.path.join(plugin_root_path(),
                             "icons/wastewater-profile.svg")),
            self.tr("Profile"), self.iface.mainWindow())
        self.profileAction.setWhatsThis(self.tr("Reach trace"))
        self.profileAction.setEnabled(False)
        self.profileAction.setCheckable(True)
        self.profileAction.triggered.connect(self.profileToolClicked)

        self.downstreamAction = QAction(
            QIcon(
                os.path.join(plugin_root_path(),
                             "icons/wastewater-downstream.svg")),
            self.tr("Downstream"), self.iface.mainWindow())
        self.downstreamAction.setWhatsThis(self.tr("Downstream reaches"))
        self.downstreamAction.setEnabled(False)
        self.downstreamAction.setCheckable(True)
        self.downstreamAction.triggered.connect(self.downstreamToolClicked)

        self.upstreamAction = QAction(
            QIcon(
                os.path.join(plugin_root_path(),
                             "icons/wastewater-upstream.svg")),
            self.tr("Upstream"), self.iface.mainWindow())
        self.upstreamAction.setWhatsThis(self.tr("Upstream reaches"))
        self.upstreamAction.setEnabled(False)
        self.upstreamAction.setCheckable(True)
        self.upstreamAction.triggered.connect(self.upstreamToolClicked)

        self.wizardAction = QAction(
            QIcon(os.path.join(plugin_root_path(), "icons/wizard.svg")),
            "Wizard", self.iface.mainWindow())
        self.wizardAction.setWhatsThis(
            self.tr("Create new manholes and reaches"))
        self.wizardAction.setEnabled(False)
        self.wizardAction.setCheckable(True)
        self.wizardAction.triggered.connect(self.wizard)

        self.connectNetworkElementsAction = QAction(
            QIcon(
                os.path.join(plugin_root_path(),
                             "icons/link-wastewater-networkelement.svg")),
            QApplication.translate('qgepplugin',
                                   'Connect wastewater networkelements'),
            self.iface.mainWindow())
        self.connectNetworkElementsAction.setEnabled(False)
        self.connectNetworkElementsAction.setCheckable(True)
        self.connectNetworkElementsAction.triggered.connect(
            self.connectNetworkElements)

        self.refreshNetworkTopologyAction = QAction(
            QIcon(os.path.join(plugin_root_path(),
                               "icons/refresh-network.svg")),
            "Refresh network topology", self.iface.mainWindow())
        self.refreshNetworkTopologyAction.setWhatsThis(
            self.tr("Refresh network topology"))
        self.refreshNetworkTopologyAction.setEnabled(False)
        self.refreshNetworkTopologyAction.setCheckable(False)
        self.refreshNetworkTopologyAction.triggered.connect(
            self.refreshNetworkTopologyActionClicked)

        self.aboutAction = QAction(self.tr('About'), self.iface.mainWindow())
        self.aboutAction.triggered.connect(self.about)

        self.settingsAction = QAction(self.tr('Settings'),
                                      self.iface.mainWindow())
        self.settingsAction.triggered.connect(self.showSettings)

        # Add toolbar button and menu item
        self.toolbar = QToolBar(QApplication.translate('qgepplugin', 'QGEP'))
        self.toolbar.addAction(self.profileAction)
        self.toolbar.addAction(self.upstreamAction)
        self.toolbar.addAction(self.downstreamAction)
        self.toolbar.addAction(self.wizardAction)
        self.toolbar.addAction(self.refreshNetworkTopologyAction)
        self.toolbar.addAction(self.connectNetworkElementsAction)

        self.iface.addPluginToMenu("&QGEP", self.profileAction)
        self.iface.addPluginToMenu("&QGEP", self.settingsAction)
        self.iface.addPluginToMenu("&QGEP", self.aboutAction)

        self.iface.addToolBar(self.toolbar)

        # Local array of buttons to enable / disable based on context
        self.toolbarButtons.append(self.profileAction)
        self.toolbarButtons.append(self.upstreamAction)
        self.toolbarButtons.append(self.downstreamAction)
        self.toolbarButtons.append(self.wizardAction)
        self.toolbarButtons.append(self.refreshNetworkTopologyAction)

        self.network_layer_notifier.layersAvailable.connect(
            self.onLayersAvailable)
        self.network_layer_notifier.layersUnavailable.connect(
            self.onLayersUnavailable)

        # Init the object maintaining the network
        self.network_analyzer = QgepGraphManager()
        self.network_analyzer.message_emitted.connect(
            self.iface.messageBar().pushMessage)
        # Create the map tool for profile selection
        self.profile_tool = QgepProfileMapTool(self.iface, self.profileAction,
                                               self.network_analyzer)
        self.profile_tool.profileChanged.connect(self.onProfileChanged)

        self.upstream_tree_tool = QgepTreeMapTool(self.iface,
                                                  self.upstreamAction,
                                                  self.network_analyzer)
        self.upstream_tree_tool.setDirection("upstream")
        self.upstream_tree_tool.treeChanged.connect(self.onTreeChanged)
        self.downstream_tree_tool = QgepTreeMapTool(self.iface,
                                                    self.downstreamAction,
                                                    self.network_analyzer)
        self.downstream_tree_tool.setDirection("downstream")
        self.downstream_tree_tool.treeChanged.connect(self.onTreeChanged)

        self.maptool_connect_networkelements = QgepMapToolConnectNetworkElements(
            self.iface, self.connectNetworkElementsAction)

        self.wastewater_networkelement_layer_notifier.layersAvailableChanged.connect(
            self.connectNetworkElementsAction.setEnabled)

        self.processing_provider = QgepProcessingProvider()
        QgsApplication.processingRegistry().addProvider(
            self.processing_provider)

        self.network_layer_notifier.layersAdded([])

    def unload(self):
        """
        Called when unloading
        """
        self.toolbar.removeAction(self.profileAction)
        self.toolbar.removeAction(self.upstreamAction)
        self.toolbar.removeAction(self.downstreamAction)
        self.toolbar.removeAction(self.wizardAction)
        self.toolbar.removeAction(self.refreshNetworkTopologyAction)
        self.toolbar.removeAction(self.connectNetworkElementsAction)

        self.toolbar.deleteLater()

        self.iface.removePluginMenu("&QGEP", self.profileAction)
        self.iface.removePluginMenu("&QGEP", self.aboutAction)

        QgsApplication.processingRegistry().removeProvider(
            self.processing_provider)

    def onLayersAvailable(self, layers):
        for b in self.toolbarButtons:
            b.setEnabled(True)

        self.network_analyzer.setReachLayer(layers['vw_network_segment'])
        self.network_analyzer.setNodeLayer(layers['vw_network_node'])

    def onLayersUnavailable(self):
        for b in self.toolbarButtons:
            b.setEnabled(False)

    def profileToolClicked(self):
        """
        Is executed when the profile button is clicked
        """
        self.openDock()
        # Set the profile map tool
        self.profile_tool.setActive()

    def upstreamToolClicked(self):
        """
        Is executed when the user clicks the upstream search tool
        """
        self.openDock()
        self.upstream_tree_tool.setActive()

    def downstreamToolClicked(self):
        """
        Is executed when the user clicks the downstream search tool
        """
        self.openDock()
        self.downstream_tree_tool.setActive()

    def refreshNetworkTopologyActionClicked(self):
        """
        Is executed when the user clicks the refreshNetworkTopologyAction tool
        """
        self.network_analyzer.refresh()

    def wizard(self):
        """
        """
        if not self.wizarddock:
            self.wizarddock = QgepWizard(self.iface.mainWindow(), self.iface)
        self.logger.debug('Opening Wizard')
        self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.wizarddock)
        self.wizarddock.show()

    def connectNetworkElements(self, checked):
        self.iface.mapCanvas().setMapTool(self.maptool_connect_networkelements)

    def openDock(self):
        """
        Opens the dock
        """
        if self.profile_dock is None:
            self.logger.debug('Open dock')
            self.profile_dock = QgepProfileDockWidget(self.iface.mainWindow(),
                                                      self.iface.mapCanvas(),
                                                      self.iface.addDockWidget)
            self.profile_dock.closed.connect(self.onDockClosed)
            self.profile_dock.showIt()

            self.plotWidget = QgepPlotSVGWidget(self.profile_dock,
                                                self.network_analyzer)
            self.plotWidget.specialStructureMouseOver.connect(
                self.highlightProfileElement)
            self.plotWidget.specialStructureMouseOut.connect(
                self.unhighlightProfileElement)
            self.plotWidget.reachMouseOver.connect(
                self.highlightProfileElement)
            self.plotWidget.reachMouseOut.connect(
                self.unhighlightProfileElement)
            self.profile_dock.addPlotWidget(self.plotWidget)
            self.profile_dock.setTree(self.nodes, self.edges)

    def onDockClosed(self):  # used when Dock dialog is closed
        """
        Gets called when the dock is closed
        All the cleanup of the dock has to be done here
        """
        self.profile_dock = None

    def onProfileChanged(self, profile):
        """
        The profile changed: update the plot
        @param profile: The profile to plot
        """
        self.profile = profile.copy()

        if self.plotWidget:
            self.plotWidget.setProfile(profile)

    def onTreeChanged(self, nodes, edges):
        if self.profile_dock:
            self.profile_dock.setTree(nodes, edges)
        self.nodes = nodes
        self.edges = edges

    def highlightProfileElement(self, obj_id):
        if self.profile is not None:
            self.profile.highlight(str(obj_id))

    def unhighlightProfileElement(self):
        if self.profile is not None:
            self.profile.highlight(None)

    def about(self):
        from .gui.dlgabout import DlgAbout

        DlgAbout(self.iface.mainWindow()).exec_()

    def showSettings(self):
        settings_dlg = QgepSettingsDialog(self.iface.mainWindow())
        settings_dlg.exec_()
class CartographyToolsPlugin:
    """QGIS Plugin Implementation."""
    def __init__(self, iface: QgisInterface):
        """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
        """
        super().__init__()
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QgsApplication.locale()
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   '{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        # processing framework
        self.provider = CartographyToolsProvider()

        self.toolbar = None
        self.actions = []
        self.tools = {}

        self.active_tool = None
        self.layout_hooks = LayoutDesignerHooks()

    @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('CartographyTools', message)

    def initProcessing(self):
        """Create the Processing provider"""
        QgsApplication.processingRegistry().addProvider(self.provider)

    def initGui(self):
        """Creates application GUI widgets"""
        self.initProcessing()

        self.toolbar = QToolBar(self.tr('Cartography Tools'))
        self.toolbar.setObjectName('cartographyTools')
        self.iface.addToolBar(self.toolbar)

        self.create_tools()

        self.iface.currentLayerChanged.connect(self.current_layer_changed)
        self.iface.actionToggleEditing().toggled.connect(self.editing_toggled)

        self.layout_hooks.init_gui(self.iface)

    def get_map_tool_action_group(self):
        try:
            return self.iface.mapToolActionGroup()
        except AttributeError:
            return \
            [o for o in self.iface.mainWindow().findChildren(QActionGroup) if self.iface.actionPan() in o.actions()][0]

    def create_tools(self):
        """
        Creates all map tools ands add them to the QGIS interface
        """
        action_single_point_templated_marker = QAction(
            GuiUtils.get_icon('single_point_templated_marker.svg'),
            self.tr('Single Point Templated Marker'))
        action_single_point_templated_marker.setCheckable(True)
        self.tools[SinglePointTemplatedMarkerTool.
                   ID] = SinglePointTemplatedMarkerTool(
                       self.iface.mapCanvas(), self.iface.cadDockWidget(),
                       self.iface, action_single_point_templated_marker)
        self.tools[SinglePointTemplatedMarkerTool.ID].setAction(
            action_single_point_templated_marker)
        action_single_point_templated_marker.triggered.connect(
            partial(self.switch_tool, SinglePointTemplatedMarkerTool.ID))
        action_single_point_templated_marker.setData(
            SinglePointTemplatedMarkerTool.ID)
        self.toolbar.addAction(action_single_point_templated_marker)
        self.actions.append(action_single_point_templated_marker)

        self.get_map_tool_action_group().addAction(
            action_single_point_templated_marker)

        # single point at center of line tool

        action_single_point_at_center_of_line = QAction(
            GuiUtils.get_icon('marker_at_center_of_line.svg'),
            self.tr('Single Point Templated Marker Via Two Points'))
        action_single_point_at_center_of_line.setCheckable(True)
        self.tools[
            TwoPointTemplatedMarkerTool.ID] = TwoPointTemplatedMarkerTool(
                self.iface.mapCanvas(), self.iface.cadDockWidget(), self.iface,
                action_single_point_at_center_of_line)
        self.tools[TwoPointTemplatedMarkerTool.ID].setAction(
            action_single_point_at_center_of_line)
        action_single_point_at_center_of_line.triggered.connect(
            partial(self.switch_tool, TwoPointTemplatedMarkerTool.ID))
        action_single_point_at_center_of_line.setData(
            TwoPointTemplatedMarkerTool.ID)
        self.toolbar.addAction(action_single_point_at_center_of_line)
        self.actions.append(action_single_point_at_center_of_line)

        self.get_map_tool_action_group().addAction(
            action_single_point_at_center_of_line)

        # multi point tool

        action_multi_point_templated_marker = QAction(
            GuiUtils.get_icon('multi_point_templated_marker.svg'),
            self.tr('Multiple Point Templated Marker Along LineString'))
        action_multi_point_templated_marker.setCheckable(True)
        self.tools[
            MultiPointTemplatedMarkerTool.ID] = MultiPointTemplatedMarkerTool(
                self.iface.mapCanvas(), self.iface.cadDockWidget(), self.iface,
                action_multi_point_templated_marker)
        self.tools[MultiPointTemplatedMarkerTool.ID].setAction(
            action_multi_point_templated_marker)
        action_multi_point_templated_marker.triggered.connect(
            partial(self.switch_tool, MultiPointTemplatedMarkerTool.ID))
        action_multi_point_templated_marker.setData(
            MultiPointTemplatedMarkerTool.ID)
        self.toolbar.addAction(action_multi_point_templated_marker)
        self.actions.append(action_multi_point_templated_marker)

        self.get_map_tool_action_group().addAction(
            action_single_point_templated_marker)

        # multi at center of segment point tool

        action_multi_point_center_segment_templated_marker = QAction(
            GuiUtils.get_icon('multi_point_templated_marker_at_center.svg'),
            self.tr('Multiple Point Templated Marker At Center Of Segments'))
        action_multi_point_center_segment_templated_marker.setCheckable(True)
        self.tools[MultiPointSegmentCenterTemplatedMarkerTool.
                   ID] = MultiPointSegmentCenterTemplatedMarkerTool(
                       self.iface.mapCanvas(), self.iface.cadDockWidget(),
                       self.iface,
                       action_multi_point_center_segment_templated_marker)
        self.tools[MultiPointSegmentCenterTemplatedMarkerTool.ID].setAction(
            action_multi_point_center_segment_templated_marker)
        action_multi_point_center_segment_templated_marker.triggered.connect(
            partial(self.switch_tool,
                    MultiPointSegmentCenterTemplatedMarkerTool.ID))
        action_multi_point_center_segment_templated_marker.setData(
            MultiPointSegmentCenterTemplatedMarkerTool.ID)
        self.toolbar.addAction(
            action_multi_point_center_segment_templated_marker)
        self.actions.append(action_multi_point_center_segment_templated_marker)

        self.get_map_tool_action_group().addAction(
            action_single_point_templated_marker)

        self.enable_actions_for_layer(self.iface.activeLayer())

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        QgsApplication.processingRegistry().removeProvider(self.provider)

        if self.toolbar is not None:
            self.toolbar.deleteLater()
        for action in self.actions:
            if action is not None:
                action.deleteLater()

        self.iface.currentLayerChanged.disconnect(self.current_layer_changed)
        self.layout_hooks.unload(self.iface)

    def switch_tool(self, tool_id: str):
        """
        Switches to the tool with the specified tool_id
        """
        tool = self.tools[tool_id]
        if self.iface.mapCanvas().mapTool() == tool:
            return

        self.iface.mapCanvas().setMapTool(tool)
        self.active_tool = tool
        self.active_tool.set_layer(self.iface.activeLayer())

    def current_layer_changed(self, layer: QgsMapLayer):
        """
        Called when the current layer changes
        """
        self.enable_actions_for_layer(layer)

        if self.active_tool:
            self.active_tool.set_layer(layer)

    def editing_toggled(self, enabled: bool):
        """
        Called when editing mode is toggled
        """
        QTimer.singleShot(
            0,
            partial(self.enable_actions_for_layer, self.iface.activeLayer(),
                    enabled))

    def enable_actions_for_layer(self,
                                 layer: QgsMapLayer,
                                 forced_edit_state=None):
        """
        Toggles whether actions should be enabled for the specified layer
        """

        is_editable = forced_edit_state
        if is_editable is None:
            if isinstance(layer, QgsVectorLayer):
                is_editable = layer.isEditable()
            else:
                is_editable = False

        for action in self.actions:
            if sip.isdeleted(action):
                continue

            if self.tools.get(action.data()):
                tool = self.tools[action.data()]
                action.setEnabled(
                    tool.is_compatible_with_layer(layer, is_editable))
                if tool == self.active_tool and not action.isEnabled():
                    self.iface.mapCanvas().unsetMapTool(tool)
                    self.iface.actionPan().trigger()
Exemple #3
0
class VertexComparePlugin(QObject):
    """QGIS Plugin Implementation."""
    def __init__(self, iface: QgisInterface):
        """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
        """
        super().__init__()
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QgsApplication.locale()
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   '{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        self.toolbar = None
        self.layer_combo = None
        self.actions = []
        self.dock = None
        self.vertex_highlighter = VertexHighlighterManager()
        self.selection_handler = SelectionHandler(self)
        self.show_vertices_action = None
        self.show_topology_action = None
        self.show_dock_action = None

    @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('VertexCompare', message)

    def initProcessing(self):
        """Create the Processing provider"""

    def initGui(self):
        """Creates application GUI widgets"""
        self.initProcessing()

        self.dock = VertexDockWidget(self.iface.mapCanvas())
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock)
        self.dock.setUserVisible(False)

        self.toolbar = QToolBar(self.tr('Vertex Compare Toolbar'))
        self.toolbar.setObjectName('vertexCompareToolbar')
        self.iface.addToolBar(self.toolbar)

        self.layer_combo = QgsMapLayerComboBox()
        self.layer_combo.setAllowEmptyLayer(True, self.tr('Disabled'))
        self.layer_combo.setFilters(QgsMapLayerProxyModel.PolygonLayer
                                    | QgsMapLayerProxyModel.LineLayer)
        self.layer_combo.setMinimumWidth(
            QFontMetrics(self.layer_combo.font()).width('x') * 40)
        self.layer_combo.setCurrentIndex(0)
        self.layer_combo.layerChanged.connect(self._set_layer)
        self.toolbar.addWidget(self.layer_combo)

        self.show_vertices_action = QAction(self.tr("Show Vertex Numbers"),
                                            self)
        self.show_vertices_action.setIcon(
            GuiUtils.get_icon('show_vertex_numbers.svg'))
        self.show_vertices_action.setCheckable(True)
        self.show_vertices_action.setEnabled(False)
        self.actions.append(self.show_vertices_action)
        self.toolbar.addAction(self.show_vertices_action)
        self.show_vertices_action.toggled.connect(
            self.vertex_highlighter.set_visible)

        self.show_topology_action = QAction(self.tr("Compare Vertices"), self)
        self.show_topology_action.setIcon(GuiUtils.get_icon('topology.svg'))
        self.show_topology_action.setCheckable(True)
        self.actions.append(self.show_topology_action)
        self.toolbar.addAction(self.show_topology_action)
        self.show_topology_action.toggled.connect(
            self.vertex_highlighter.set_topological)

        self.show_dock_action = QAction(self.tr('Show Vertices'),
                                        parent=self.toolbar)
        self.show_dock_action.setIcon(GuiUtils.get_icon('vertex_table.svg'))
        self.toolbar.addAction(self.show_dock_action)
        self.actions.append(self.show_dock_action)
        self.dock.setToggleVisibilityAction(self.show_dock_action)

        self.selection_handler.selection_changed.connect(
            self._selection_changed)
        self.dock.label_filter_changed.connect(self.vertex_highlighter.redraw)
        self.dock.vertex_symbol_changed.connect(self.vertex_highlighter.redraw)
        self.dock.vertex_text_format_changed.connect(
            self.vertex_highlighter.redraw)
        self.dock.selected_vertex_changed.connect(
            self.vertex_highlighter.set_selected_vertex)

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for a in self.actions:
            a.deleteLater()
        self.actions = []

        if self.toolbar is not None:
            self.toolbar.deleteLater()
            self.toolbar = None
        if self.dock is not None:
            self.dock.deleteLater()
            self.dock = None

    def _set_layer(self, layer: Optional[QgsVectorLayer]):
        """
        Triggered when the selected layer is changed
        """
        self.selection_handler.set_layer(layer)
        self.vertex_highlighter.set_layer(layer)
        self.show_vertices_action.setEnabled(layer is not None)
        if not self.show_vertices_action.isEnabled():
            self.show_vertices_action.setChecked(False)
        else:
            self.show_vertices_action.setChecked(True)

        self.dock.set_selection(
            layer,
            layer.selectedFeatureIds() if layer is not None else [])

    def _selection_changed(self, layer: Optional[QgsVectorLayer],
                           selection: List[int]):
        """
        Triggered when the watched layer's selection is changed
        """
        self.dock.set_selection(layer, selection)