Example #1
0
    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."""
        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 setAsNonRepoLayer(layer):
    removeLayerActions(layer)
    action = QAction("Import to GeoGig...", config.iface.legendInterface())
    action.triggered.connect(partial(addLayer, layer))
    if layer.type() == QgsMapLayer.RasterLayer or layer.storageType() != 'GPKG':
        action.setEnabled(False)
    config.iface.legendInterface().addLegendLayerAction(action, u"GeoGig", u"id2", QgsMapLayer.VectorLayer, False)
    config.iface.legendInterface().addLegendLayerActionForLayer(action, layer)
    _actions[layer.id()] = [action]
    try:
        repoWatcher.layerUpdated.disconnect(updateInfoActions)
    except:
        pass #In case it is a layer that was never a repo layer
 def menu(self):
     menu = QMenu()
     refreshAction = QAction(icon("refresh.svg"), "Refresh", menu)
     refreshAction.triggered.connect(self.refreshContent)
     menu.addAction(refreshAction)
     createBranchAction = QAction(icon("create_branch.png"), "Create branch", menu)
     createBranchAction.triggered.connect(self.createBranch)
     menu.addAction(createBranchAction)
     deleteAction = QAction(QgsApplication.getThemeIcon('/mActionDeleteSelected.svg'), "Delete", menu)
     deleteAction.triggered.connect(self.delete)
     menu.addAction(deleteAction)
     deleteAction.setEnabled(self.parent().childCount() > 1 and self.branch != "master")
     return menu
    def selectOutput(self):
        if isinstance(self.parameter, QgsProcessingParameterFolderDestination):
            self.selectDirectory()
        else:
            popupMenu = QMenu()

            if not self.default_selection:
                if self.parameter.flags() & QgsProcessingParameterDefinition.FlagOptional:
                    actionSkipOutput = QAction(
                        self.tr('Skip Output'), self.btnSelect)
                    actionSkipOutput.triggered.connect(self.skipOutput)
                    popupMenu.addAction(actionSkipOutput)

                if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
                        and self.parameter.supportsNonFileBasedOutput():
                    # use memory layers for temporary layers if supported
                    actionSaveToTemp = QAction(
                        self.tr('Create Temporary Layer'), self.btnSelect)
                else:
                    actionSaveToTemp = QAction(
                        self.tr('Save to a Temporary File'), self.btnSelect)
                actionSaveToTemp.triggered.connect(self.saveToTemporary)
                popupMenu.addAction(actionSaveToTemp)

            actionSaveToFile = QAction(
                QCoreApplication.translate('DestinationSelectionPanel', 'Save to File…'), self.btnSelect)
            actionSaveToFile.triggered.connect(self.selectFile)
            popupMenu.addAction(actionSaveToFile)

            if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
                    and self.parameter.supportsNonFileBasedOutput():
                actionSaveToGpkg = QAction(
                    QCoreApplication.translate('DestinationSelectionPanel', 'Save to GeoPackage…'), self.btnSelect)
                actionSaveToGpkg.triggered.connect(self.saveToGeopackage)
                popupMenu.addAction(actionSaveToGpkg)
                actionSaveToPostGIS = QAction(
                    QCoreApplication.translate('DestinationSelectionPanel', 'Save to PostGIS Table…'), self.btnSelect)
                actionSaveToPostGIS.triggered.connect(self.saveToPostGIS)
                settings = QgsSettings()
                settings.beginGroup('/PostgreSQL/connections/')
                names = settings.childGroups()
                settings.endGroup()
                actionSaveToPostGIS.setEnabled(bool(names))
                popupMenu.addAction(actionSaveToPostGIS)

            actionSetEncoding = QAction(
                QCoreApplication.translate('DestinationSelectionPanel', 'Change File Encoding ({})…').format(self.encoding), self.btnSelect)
            actionSetEncoding.triggered.connect(self.selectEncoding)
            popupMenu.addAction(actionSetEncoding)

            popupMenu.exec_(QCursor.pos())
    def selectOutput(self):
        if isinstance(self.parameter, QgsProcessingParameterFolderDestination):
            self.selectDirectory()
        else:
            popupMenu = QMenu()

            if self.parameter.flags() & QgsProcessingParameterDefinition.FlagOptional:
                actionSkipOutput = QAction(
                    self.tr('Skip output'), self.btnSelect)
                actionSkipOutput.triggered.connect(self.skipOutput)
                popupMenu.addAction(actionSkipOutput)

            if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
                    and self.alg.provider().supportsNonFileBasedOutput():
                # use memory layers for temporary layers if supported
                actionSaveToTemp = QAction(
                    self.tr('Create temporary layer'), self.btnSelect)
            else:
                actionSaveToTemp = QAction(
                    self.tr('Save to a temporary file'), self.btnSelect)
            actionSaveToTemp.triggered.connect(self.saveToTemporary)
            popupMenu.addAction(actionSaveToTemp)

            actionSaveToFile = QAction(
                self.tr('Save to file...'), self.btnSelect)
            actionSaveToFile.triggered.connect(self.selectFile)
            popupMenu.addAction(actionSaveToFile)

            actionShowExpressionsBuilder = QAction(
                self.tr('Use expression...'), self.btnSelect)
            actionShowExpressionsBuilder.triggered.connect(self.showExpressionsBuilder)
            popupMenu.addAction(actionShowExpressionsBuilder)

            if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
                    and self.alg.provider().supportsNonFileBasedOutput():
                actionSaveToSpatialite = QAction(
                    self.tr('Save to SpatiaLite table...'), self.btnSelect)
                actionSaveToSpatialite.triggered.connect(self.saveToSpatialite)
                popupMenu.addAction(actionSaveToSpatialite)
                actionSaveToPostGIS = QAction(
                    self.tr('Save to PostGIS table...'), self.btnSelect)
                actionSaveToPostGIS.triggered.connect(self.saveToPostGIS)
                settings = QgsSettings()
                settings.beginGroup('/PostgreSQL/connections/')
                names = settings.childGroups()
                settings.endGroup()
                actionSaveToPostGIS.setEnabled(bool(names))
                popupMenu.addAction(actionSaveToPostGIS)

            popupMenu.exec_(QCursor.pos())
Example #6
0
    def selectOutput(self):
        if isinstance(self.output, OutputDirectory):
            self.selectDirectory()
        else:
            popupMenu = QMenu()

            if isinstance(self.output, OutputVector) \
                    and self.alg.provider.supportsNonFileBasedOutput():
                # use memory layers for temporary layers if supported
                actionSaveToTemp = QAction(
                    self.tr('Create temporary layer'), self.btnSelect)
            else:
                actionSaveToTemp = QAction(
                    self.tr('Save to a temporary file'), self.btnSelect)
            actionSaveToTemp.triggered.connect(self.saveToTemporary)
            popupMenu.addAction(actionSaveToTemp)

            actionSaveToFile = QAction(
                self.tr('Save to file...'), self.btnSelect)
            actionSaveToFile.triggered.connect(self.selectFile)
            popupMenu.addAction(actionSaveToFile)

            actionShowExpressionsBuilder = QAction(
                self.tr('Use expression...'), self.btnSelect)
            actionShowExpressionsBuilder.triggered.connect(self.showExpressionsBuilder)
            popupMenu.addAction(actionShowExpressionsBuilder)

            if isinstance(self.output, OutputVector) \
                    and self.alg.provider.supportsNonFileBasedOutput():
                actionSaveToSpatialite = QAction(
                    self.tr('Save to Spatialite table...'), self.btnSelect)
                actionSaveToSpatialite.triggered.connect(self.saveToSpatialite)
                popupMenu.addAction(actionSaveToSpatialite)
                actionSaveToPostGIS = QAction(
                    self.tr('Save to PostGIS table...'), self.btnSelect)
                actionSaveToPostGIS.triggered.connect(self.saveToPostGIS)
                settings = QSettings()
                settings.beginGroup('/PostgreSQL/connections/')
                names = settings.childGroups()
                settings.endGroup()
                actionSaveToPostGIS.setEnabled(bool(names))
                popupMenu.addAction(actionSaveToPostGIS)

            popupMenu.exec_(QCursor.pos())
Example #7
0
    def add_action(
            self,
            icon_path,
            text,
            callback,
            enabled_flag=True,
            checkable=False,
            add_to_menu=True,
            add_to_toolbar=True,
            status_tip=None,
            whats_this=None,
            menu=None,
            parent=None):
        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)
        action.setCheckable(checkable)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if menu is not None:
            action.setMenu(menu)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToVectorMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action
Example #8
0
class PythonConsoleWidget(QWidget):

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console"))

        self.settings = QgsSettings()

        self.shell = ShellScintilla(self)
        self.setFocusProxy(self.shell)
        self.shellOut = ShellOutputScintilla(self)
        self.tabEditorWidget = EditorTabWidget(self)

        # ------------ UI -------------------------------

        self.splitterEditor = QSplitter(self)
        self.splitterEditor.setOrientation(Qt.Horizontal)
        self.splitterEditor.setHandleWidth(6)
        self.splitterEditor.setChildrenCollapsible(True)

        self.shellOutWidget = QWidget(self)
        self.shellOutWidget.setLayout(QVBoxLayout())
        self.shellOutWidget.layout().setContentsMargins(0, 0, 0, 0)
        self.shellOutWidget.layout().addWidget(self.shellOut)

        self.splitter = QSplitter(self.splitterEditor)
        self.splitter.setOrientation(Qt.Vertical)
        self.splitter.setHandleWidth(3)
        self.splitter.setChildrenCollapsible(False)
        self.splitter.addWidget(self.shellOutWidget)
        self.splitter.addWidget(self.shell)

        # self.splitterEditor.addWidget(self.tabEditorWidget)

        self.splitterObj = QSplitter(self.splitterEditor)
        self.splitterObj.setHandleWidth(3)
        self.splitterObj.setOrientation(Qt.Horizontal)
        # self.splitterObj.setSizes([0, 0])
        # self.splitterObj.setStretchFactor(0, 1)

        self.widgetEditor = QWidget(self.splitterObj)
        self.widgetFind = QWidget(self)

        self.listClassMethod = QTreeWidget(self.splitterObj)
        self.listClassMethod.setColumnCount(2)
        objInspLabel = QCoreApplication.translate("PythonConsole", "Object Inspector")
        self.listClassMethod.setHeaderLabels([objInspLabel, ''])
        self.listClassMethod.setColumnHidden(1, True)
        self.listClassMethod.setAlternatingRowColors(True)

        # self.splitterEditor.addWidget(self.widgetEditor)
        # self.splitterObj.addWidget(self.listClassMethod)
        # self.splitterObj.addWidget(self.widgetEditor)

        # Hide side editor on start up
        self.splitterObj.hide()
        self.listClassMethod.hide()
        # Hide search widget on start up
        self.widgetFind.hide()

        icon_size = iface.iconSize(dockedToolbar=True) if iface else QSize(16, 16)

        sizes = self.splitter.sizes()
        self.splitter.setSizes(sizes)

        # ----------------Restore Settings------------------------------------

        self.restoreSettingsConsole()

        # ------------------Toolbar Editor-------------------------------------

        # Action for Open File
        openFileBt = QCoreApplication.translate("PythonConsole", "Open Script…")
        self.openFileButton = QAction(self)
        self.openFileButton.setCheckable(False)
        self.openFileButton.setEnabled(True)
        self.openFileButton.setIcon(QgsApplication.getThemeIcon("console/iconOpenConsole.svg"))
        self.openFileButton.setMenuRole(QAction.PreferencesRole)
        self.openFileButton.setIconVisibleInMenu(True)
        self.openFileButton.setToolTip(openFileBt)
        self.openFileButton.setText(openFileBt)

        openExtEditorBt = QCoreApplication.translate("PythonConsole", "Open in External Editor")
        self.openInEditorButton = QAction(self)
        self.openInEditorButton.setCheckable(False)
        self.openInEditorButton.setEnabled(True)
        self.openInEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg"))
        self.openInEditorButton.setMenuRole(QAction.PreferencesRole)
        self.openInEditorButton.setIconVisibleInMenu(True)
        self.openInEditorButton.setToolTip(openExtEditorBt)
        self.openInEditorButton.setText(openExtEditorBt)
        # Action for Save File
        saveFileBt = QCoreApplication.translate("PythonConsole", "Save")
        self.saveFileButton = QAction(self)
        self.saveFileButton.setCheckable(False)
        self.saveFileButton.setEnabled(False)
        self.saveFileButton.setIcon(QgsApplication.getThemeIcon("console/iconSaveConsole.svg"))
        self.saveFileButton.setMenuRole(QAction.PreferencesRole)
        self.saveFileButton.setIconVisibleInMenu(True)
        self.saveFileButton.setToolTip(saveFileBt)
        self.saveFileButton.setText(saveFileBt)
        # Action for Save File As
        saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As…")
        self.saveAsFileButton = QAction(self)
        self.saveAsFileButton.setCheckable(False)
        self.saveAsFileButton.setEnabled(True)
        self.saveAsFileButton.setIcon(QgsApplication.getThemeIcon("console/iconSaveAsConsole.svg"))
        self.saveAsFileButton.setMenuRole(QAction.PreferencesRole)
        self.saveAsFileButton.setIconVisibleInMenu(True)
        self.saveAsFileButton.setToolTip(saveAsFileBt)
        self.saveAsFileButton.setText(saveAsFileBt)
        # Action Cut
        cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut")
        self.cutEditorButton = QAction(self)
        self.cutEditorButton.setCheckable(False)
        self.cutEditorButton.setEnabled(True)
        self.cutEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg"))
        self.cutEditorButton.setMenuRole(QAction.PreferencesRole)
        self.cutEditorButton.setIconVisibleInMenu(True)
        self.cutEditorButton.setToolTip(cutEditorBt)
        self.cutEditorButton.setText(cutEditorBt)
        # Action Copy
        copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy")
        self.copyEditorButton = QAction(self)
        self.copyEditorButton.setCheckable(False)
        self.copyEditorButton.setEnabled(True)
        self.copyEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCopy.svg"))
        self.copyEditorButton.setMenuRole(QAction.PreferencesRole)
        self.copyEditorButton.setIconVisibleInMenu(True)
        self.copyEditorButton.setToolTip(copyEditorBt)
        self.copyEditorButton.setText(copyEditorBt)
        # Action Paste
        pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste")
        self.pasteEditorButton = QAction(self)
        self.pasteEditorButton.setCheckable(False)
        self.pasteEditorButton.setEnabled(True)
        self.pasteEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditPaste.svg"))
        self.pasteEditorButton.setMenuRole(QAction.PreferencesRole)
        self.pasteEditorButton.setIconVisibleInMenu(True)
        self.pasteEditorButton.setToolTip(pasteEditorBt)
        self.pasteEditorButton.setText(pasteEditorBt)
        # Action Run Script (subprocess)
        runScriptEditorBt = QCoreApplication.translate("PythonConsole", "Run Script")
        self.runScriptEditorButton = QAction(self)
        self.runScriptEditorButton.setCheckable(False)
        self.runScriptEditorButton.setEnabled(True)
        self.runScriptEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconRunScriptConsole.svg"))
        self.runScriptEditorButton.setMenuRole(QAction.PreferencesRole)
        self.runScriptEditorButton.setIconVisibleInMenu(True)
        self.runScriptEditorButton.setToolTip(runScriptEditorBt)
        self.runScriptEditorButton.setText(runScriptEditorBt)
        # Action Run Script (subprocess)
        commentEditorBt = QCoreApplication.translate("PythonConsole", "Comment")
        self.commentEditorButton = QAction(self)
        self.commentEditorButton.setCheckable(False)
        self.commentEditorButton.setEnabled(True)
        self.commentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconCommentEditorConsole.svg"))
        self.commentEditorButton.setMenuRole(QAction.PreferencesRole)
        self.commentEditorButton.setIconVisibleInMenu(True)
        self.commentEditorButton.setToolTip(commentEditorBt)
        self.commentEditorButton.setText(commentEditorBt)
        # Action Run Script (subprocess)
        uncommentEditorBt = QCoreApplication.translate("PythonConsole", "Uncomment")
        self.uncommentEditorButton = QAction(self)
        self.uncommentEditorButton.setCheckable(False)
        self.uncommentEditorButton.setEnabled(True)
        self.uncommentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconUncommentEditorConsole.svg"))
        self.uncommentEditorButton.setMenuRole(QAction.PreferencesRole)
        self.uncommentEditorButton.setIconVisibleInMenu(True)
        self.uncommentEditorButton.setToolTip(uncommentEditorBt)
        self.uncommentEditorButton.setText(uncommentEditorBt)
        # Action for Object browser
        objList = QCoreApplication.translate("PythonConsole", "Object Inspector…")
        self.objectListButton = QAction(self)
        self.objectListButton.setCheckable(True)
        self.objectListButton.setEnabled(self.settings.value("pythonConsole/enableObjectInsp",
                                                             False, type=bool))
        self.objectListButton.setIcon(QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg"))
        self.objectListButton.setMenuRole(QAction.PreferencesRole)
        self.objectListButton.setIconVisibleInMenu(True)
        self.objectListButton.setToolTip(objList)
        self.objectListButton.setText(objList)
        # Action for Find text
        findText = QCoreApplication.translate("PythonConsole", "Find Text")
        self.findTextButton = QAction(self)
        self.findTextButton.setCheckable(True)
        self.findTextButton.setEnabled(True)
        self.findTextButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchEditorConsole.svg"))
        self.findTextButton.setMenuRole(QAction.PreferencesRole)
        self.findTextButton.setIconVisibleInMenu(True)
        self.findTextButton.setToolTip(findText)
        self.findTextButton.setText(findText)

        # ----------------Toolbar Console-------------------------------------

        # Action Show Editor
        showEditor = QCoreApplication.translate("PythonConsole", "Show Editor")
        self.showEditorButton = QAction(self)
        self.showEditorButton.setEnabled(True)
        self.showEditorButton.setCheckable(True)
        self.showEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg"))
        self.showEditorButton.setMenuRole(QAction.PreferencesRole)
        self.showEditorButton.setIconVisibleInMenu(True)
        self.showEditorButton.setToolTip(showEditor)
        self.showEditorButton.setText(showEditor)
        # Action for Clear button
        clearBt = QCoreApplication.translate("PythonConsole", "Clear Console")
        self.clearButton = QAction(self)
        self.clearButton.setCheckable(False)
        self.clearButton.setEnabled(True)
        self.clearButton.setIcon(QgsApplication.getThemeIcon("console/iconClearConsole.svg"))
        self.clearButton.setMenuRole(QAction.PreferencesRole)
        self.clearButton.setIconVisibleInMenu(True)
        self.clearButton.setToolTip(clearBt)
        self.clearButton.setText(clearBt)
        # Action for settings
        optionsBt = QCoreApplication.translate("PythonConsole", "Options…")
        self.optionsButton = QAction(self)
        self.optionsButton.setCheckable(False)
        self.optionsButton.setEnabled(True)
        self.optionsButton.setIcon(QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"))
        self.optionsButton.setMenuRole(QAction.PreferencesRole)
        self.optionsButton.setIconVisibleInMenu(True)
        self.optionsButton.setToolTip(optionsBt)
        self.optionsButton.setText(optionsBt)
        # Action for Run script
        runBt = QCoreApplication.translate("PythonConsole", "Run Command")
        self.runButton = QAction(self)
        self.runButton.setCheckable(False)
        self.runButton.setEnabled(True)
        self.runButton.setIcon(QgsApplication.getThemeIcon("console/mIconRunConsole.svg"))
        self.runButton.setMenuRole(QAction.PreferencesRole)
        self.runButton.setIconVisibleInMenu(True)
        self.runButton.setToolTip(runBt)
        self.runButton.setText(runBt)
        # Help action
        helpBt = QCoreApplication.translate("PythonConsole", "Help…")
        self.helpButton = QAction(self)
        self.helpButton.setCheckable(False)
        self.helpButton.setEnabled(True)
        self.helpButton.setIcon(QgsApplication.getThemeIcon("console/iconHelpConsole.svg"))
        self.helpButton.setMenuRole(QAction.PreferencesRole)
        self.helpButton.setIconVisibleInMenu(True)
        self.helpButton.setToolTip(helpBt)
        self.helpButton.setText(helpBt)

        self.toolBar = QToolBar()
        self.toolBar.setEnabled(True)
        self.toolBar.setFocusPolicy(Qt.NoFocus)
        self.toolBar.setContextMenuPolicy(Qt.DefaultContextMenu)
        self.toolBar.setLayoutDirection(Qt.LeftToRight)
        self.toolBar.setIconSize(icon_size)
        self.toolBar.setMovable(False)
        self.toolBar.setFloatable(False)
        self.toolBar.addAction(self.clearButton)
        self.toolBar.addAction(self.runButton)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.showEditorButton)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.optionsButton)
        self.toolBar.addAction(self.helpButton)

        self.toolBarEditor = QToolBar()
        self.toolBarEditor.setEnabled(False)
        self.toolBarEditor.setFocusPolicy(Qt.NoFocus)
        self.toolBarEditor.setContextMenuPolicy(Qt.DefaultContextMenu)
        self.toolBarEditor.setLayoutDirection(Qt.LeftToRight)
        self.toolBarEditor.setIconSize(icon_size)
        self.toolBarEditor.setMovable(False)
        self.toolBarEditor.setFloatable(False)
        self.toolBarEditor.addAction(self.openFileButton)
        self.toolBarEditor.addAction(self.openInEditorButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.saveFileButton)
        self.toolBarEditor.addAction(self.saveAsFileButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.runScriptEditorButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.findTextButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.cutEditorButton)
        self.toolBarEditor.addAction(self.copyEditorButton)
        self.toolBarEditor.addAction(self.pasteEditorButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.commentEditorButton)
        self.toolBarEditor.addAction(self.uncommentEditorButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.objectListButton)

        self.widgetButton = QWidget()
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.widgetButton.sizePolicy().hasHeightForWidth())
        self.widgetButton.setSizePolicy(sizePolicy)

        self.widgetButtonEditor = QWidget(self.widgetEditor)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.widgetButtonEditor.sizePolicy().hasHeightForWidth())
        self.widgetButtonEditor.setSizePolicy(sizePolicy)

        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.shellOut.sizePolicy().hasHeightForWidth())
        self.shellOut.setSizePolicy(sizePolicy)

        self.shellOut.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.shell.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)

        # ------------ Layout -------------------------------

        self.mainLayout = QGridLayout(self)
        self.mainLayout.setMargin(0)
        self.mainLayout.setSpacing(0)
        self.mainLayout.addWidget(self.widgetButton, 0, 0, 1, 1)
        self.mainLayout.addWidget(self.splitterEditor, 0, 1, 1, 1)

        self.shellOutWidget.layout().insertWidget(0, self.toolBar)

        self.layoutEditor = QGridLayout(self.widgetEditor)
        self.layoutEditor.setMargin(0)
        self.layoutEditor.setSpacing(0)
        self.layoutEditor.addWidget(self.toolBarEditor, 0, 1, 1, 1)
        self.layoutEditor.addWidget(self.widgetButtonEditor, 1, 0, 2, 1)
        self.layoutEditor.addWidget(self.tabEditorWidget, 1, 1, 1, 1)
        self.layoutEditor.addWidget(self.widgetFind, 2, 1, 1, 1)

        #  Layout for the find widget
        self.layoutFind = QGridLayout(self.widgetFind)
        self.layoutFind.setContentsMargins(0, 0, 0, 0)
        self.lineEditFind = QgsFilterLineEdit()
        placeHolderTxt = QCoreApplication.translate("PythonConsole", "Enter text to find…")

        self.lineEditFind.setPlaceholderText(placeHolderTxt)
        self.toolBarFindText = QToolBar()
        self.toolBarFindText.setIconSize(icon_size)
        self.findNextButton = QAction(self)
        self.findNextButton.setEnabled(False)
        toolTipfindNext = QCoreApplication.translate("PythonConsole", "Find Next")
        self.findNextButton.setToolTip(toolTipfindNext)
        self.findNextButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchNextEditorConsole.svg"))
        self.findPrevButton = QAction(self)
        self.findPrevButton.setEnabled(False)
        toolTipfindPrev = QCoreApplication.translate("PythonConsole", "Find Previous")
        self.findPrevButton.setToolTip(toolTipfindPrev)
        self.findPrevButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchPrevEditorConsole.svg"))
        self.caseSensitive = QCheckBox()
        caseSensTr = QCoreApplication.translate("PythonConsole", "Case Sensitive")
        self.caseSensitive.setText(caseSensTr)
        self.wholeWord = QCheckBox()
        wholeWordTr = QCoreApplication.translate("PythonConsole", "Whole Word")
        self.wholeWord.setText(wholeWordTr)
        self.wrapAround = QCheckBox()
        self.wrapAround.setChecked(True)
        wrapAroundTr = QCoreApplication.translate("PythonConsole", "Wrap Around")
        self.wrapAround.setText(wrapAroundTr)

        self.toolBarFindText.addWidget(self.lineEditFind)
        self.toolBarFindText.addAction(self.findPrevButton)
        self.toolBarFindText.addAction(self.findNextButton)
        self.toolBarFindText.addWidget(self.caseSensitive)
        self.toolBarFindText.addWidget(self.wholeWord)
        self.toolBarFindText.addWidget(self.wrapAround)

        self.layoutFind.addWidget(self.toolBarFindText, 0, 1, 1, 1)

        # ------------ Add first Tab in Editor -------------------------------

        # self.tabEditorWidget.newTabEditor(tabName='first', filename=None)

        # ------------ Signal -------------------------------

        self.findTextButton.triggered.connect(self._toggleFind)
        self.objectListButton.toggled.connect(self.toggleObjectListWidget)
        self.commentEditorButton.triggered.connect(self.commentCode)
        self.uncommentEditorButton.triggered.connect(self.uncommentCode)
        self.runScriptEditorButton.triggered.connect(self.runScriptEditor)
        self.cutEditorButton.triggered.connect(self.cutEditor)
        self.copyEditorButton.triggered.connect(self.copyEditor)
        self.pasteEditorButton.triggered.connect(self.pasteEditor)
        self.showEditorButton.toggled.connect(self.toggleEditor)
        self.clearButton.triggered.connect(self.shellOut.clearConsole)
        self.optionsButton.triggered.connect(self.openSettings)
        self.runButton.triggered.connect(self.shell.entered)
        self.openFileButton.triggered.connect(self.openScriptFile)
        self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor)
        self.saveFileButton.triggered.connect(self.saveScriptFile)
        self.saveAsFileButton.triggered.connect(self.saveAsScriptFile)
        self.helpButton.triggered.connect(self.openHelp)
        self.listClassMethod.itemClicked.connect(self.onClickGoToLine)
        self.lineEditFind.returnPressed.connect(self._findNext)
        self.findNextButton.triggered.connect(self._findNext)
        self.findPrevButton.triggered.connect(self._findPrev)
        self.lineEditFind.textChanged.connect(self._textFindChanged)

        self.findScut = QShortcut(QKeySequence.Find, self.widgetEditor)
        self.findScut.setContext(Qt.WidgetWithChildrenShortcut)
        self.findScut.activated.connect(self._openFind)

        self.findNextScut = QShortcut(QKeySequence.FindNext, self.widgetEditor)
        self.findNextScut.setContext(Qt.WidgetWithChildrenShortcut)
        self.findNextScut.activated.connect(self._findNext)

        self.findPreviousScut = QShortcut(QKeySequence.FindPrevious, self.widgetEditor)
        self.findPreviousScut.setContext(Qt.WidgetWithChildrenShortcut)
        self.findPreviousScut.activated.connect(self._findPrev)

        # Escape on editor hides the find bar
        self.findScut = QShortcut(Qt.Key_Escape, self.widgetEditor)
        self.findScut.setContext(Qt.WidgetWithChildrenShortcut)
        self.findScut.activated.connect(self._closeFind)

    def _toggleFind(self):
        self.tabEditorWidget.currentWidget().newEditor.toggleFindWidget()

    def _openFind(self):
        self.tabEditorWidget.currentWidget().newEditor.openFindWidget()

    def _closeFind(self):
        self.tabEditorWidget.currentWidget().newEditor.closeFindWidget()

    def _findNext(self):
        self.tabEditorWidget.currentWidget().newEditor.findText(True)

    def _findPrev(self):
        self.tabEditorWidget.currentWidget().newEditor.findText(False)

    def _textFindChanged(self):
        if self.lineEditFind.text():
            self.findNextButton.setEnabled(True)
            self.findPrevButton.setEnabled(True)
            self.tabEditorWidget.currentWidget().newEditor.findText(True, showMessage=False, findFirst=True)
        else:
            self.lineEditFind.setStyleSheet('')
            self.findNextButton.setEnabled(False)
            self.findPrevButton.setEnabled(False)

    def onClickGoToLine(self, item, column):
        tabEditor = self.tabEditorWidget.currentWidget().newEditor
        if item.text(1) == 'syntaxError':
            check = tabEditor.syntaxCheck(fromContextMenu=False)
            if check and not tabEditor.isReadOnly():
                self.tabEditorWidget.currentWidget().save()
            return
        linenr = int(item.text(1))
        itemName = str(item.text(0))
        charPos = itemName.find(' ')
        if charPos != -1:
            objName = itemName[0:charPos]
        else:
            objName = itemName
        tabEditor.goToLine(objName, linenr)

    def toggleEditor(self, checked):
        self.splitterObj.show() if checked else self.splitterObj.hide()
        if not self.tabEditorWidget:
            self.tabEditorWidget.enableToolBarEditor(checked)
            self.tabEditorWidget.restoreTabsOrAddNew()

    def toggleObjectListWidget(self, checked):
        self.listClassMethod.show() if checked else self.listClassMethod.hide()

    def pasteEditor(self):
        self.tabEditorWidget.currentWidget().newEditor.paste()

    def cutEditor(self):
        self.tabEditorWidget.currentWidget().newEditor.cut()

    def copyEditor(self):
        self.tabEditorWidget.currentWidget().newEditor.copy()

    def runScriptEditor(self):
        self.tabEditorWidget.currentWidget().newEditor.runScriptCode()

    def commentCode(self):
        self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(True)

    def uncommentCode(self):
        self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(False)

    def openScriptFileExtEditor(self):
        tabWidget = self.tabEditorWidget.currentWidget()
        path = tabWidget.path
        import subprocess
        try:
            subprocess.Popen([os.environ['EDITOR'], path])
        except KeyError:
            QDesktopServices.openUrl(QUrl.fromLocalFile(path))

    def openScriptFile(self):
        lastDirPath = self.settings.value("pythonConsole/lastDirPath", QDir.homePath())
        openFileTr = QCoreApplication.translate("PythonConsole", "Open File")
        fileList, selected_filter = QFileDialog.getOpenFileNames(
            self, openFileTr, lastDirPath, "Script file (*.py)")
        if fileList:
            for pyFile in fileList:
                for i in range(self.tabEditorWidget.count()):
                    tabWidget = self.tabEditorWidget.widget(i)
                    if tabWidget.path == pyFile:
                        self.tabEditorWidget.setCurrentWidget(tabWidget)
                        break
                else:
                    tabName = QFileInfo(pyFile).fileName()
                    self.tabEditorWidget.newTabEditor(tabName, pyFile)

                    lastDirPath = QFileInfo(pyFile).path()
                    self.settings.setValue("pythonConsole/lastDirPath", pyFile)
                    self.updateTabListScript(pyFile, action='append')

    def saveScriptFile(self):
        tabWidget = self.tabEditorWidget.currentWidget()
        try:
            tabWidget.save()
        except (IOError, OSError) as error:
            msgText = QCoreApplication.translate('PythonConsole',
                                                 'The file <b>{0}</b> could not be saved. Error: {1}').format(tabWidget.path,
                                                                                                              error.strerror)
            self.callWidgetMessageBarEditor(msgText, 2, False)

    def saveAsScriptFile(self, index=None):
        tabWidget = self.tabEditorWidget.currentWidget()
        if not index:
            index = self.tabEditorWidget.currentIndex()
        if not tabWidget.path:
            fileName = self.tabEditorWidget.tabText(index) + '.py'
            folder = self.settings.value("pythonConsole/lastDirPath", QDir.homePath())
            pathFileName = os.path.join(folder, fileName)
            fileNone = True
        else:
            pathFileName = tabWidget.path
            fileNone = False
        saveAsFileTr = QCoreApplication.translate("PythonConsole", "Save File As")
        filename, filter = QFileDialog.getSaveFileName(self,
                                                       saveAsFileTr,
                                                       pathFileName, "Script file (*.py)")
        if filename:
            try:
                tabWidget.save(filename)
            except (IOError, OSError) as error:
                msgText = QCoreApplication.translate('PythonConsole',
                                                     'The file <b>{0}</b> could not be saved. Error: {1}').format(tabWidget.path,
                                                                                                                  error.strerror)
                self.callWidgetMessageBarEditor(msgText, 2, False)
                if fileNone:
                    tabWidget.path = None
                else:
                    tabWidget.path = pathFileName
                return

            if not fileNone:
                self.updateTabListScript(pathFileName, action='remove')

    def openHelp(self):
        QgsHelp.openHelp("plugins/python_console.html")

    def openSettings(self):
        if optionsDialog(self).exec_():
            self.shell.refreshSettingsShell()
            self.shellOut.refreshSettingsOutput()
            self.tabEditorWidget.refreshSettingsEditor()

    def callWidgetMessageBar(self, text):
        self.shellOut.widgetMessageBar(iface, text)

    def callWidgetMessageBarEditor(self, text, level, timed):
        self.tabEditorWidget.widgetMessageBar(iface, text, level, timed)

    def updateTabListScript(self, script, action=None):
        if action == 'remove':
            self.tabListScript.remove(script)
        elif action == 'append':
            if not self.tabListScript:
                self.tabListScript = []
            if script not in self.tabListScript:
                self.tabListScript.append(script)
        else:
            self.tabListScript = []
        self.settings.setValue("pythonConsole/tabScripts",
                               self.tabListScript)

    def saveSettingsConsole(self):
        self.settings.setValue("pythonConsole/splitterConsole", self.splitter.saveState())
        self.settings.setValue("pythonConsole/splitterObj", self.splitterObj.saveState())
        self.settings.setValue("pythonConsole/splitterEditor", self.splitterEditor.saveState())

        self.shell.writeHistoryFile(True)

    def restoreSettingsConsole(self):
        storedTabScripts = self.settings.value("pythonConsole/tabScripts", [])
        self.tabListScript = storedTabScripts
        self.splitter.restoreState(self.settings.value("pythonConsole/splitterConsole", QByteArray()))
        self.splitterEditor.restoreState(self.settings.value("pythonConsole/splitterEditor", QByteArray()))
        self.splitterObj.restoreState(self.settings.value("pythonConsole/splitterObj", QByteArray()))
Example #9
0
    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
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)

        action.triggered.connect(callback)
        #action.triggered.connect(testrun)
        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:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)
            #self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action
Example #10
0
class ProcessingPlugin(QObject):

    def __init__(self, iface):
        super().__init__()
        self.iface = iface
        self.options_factory = None
        self.drop_handler = None
        self.item_provider = None
        self.locator_filter = None
        self.edit_features_locator_filter = None
        self.initialized = False
        self.initProcessing()

    def initProcessing(self):
        if not self.initialized:
            self.initialized = True
            Processing.initialize()

    def initGui(self):
        # port old log, ONCE ONLY!
        settings = QgsSettings()
        if not settings.value("/Processing/hasPortedOldLog", False, bool):
            processing_history_provider = QgsGui.historyProviderRegistry().providerById('processing')
            if processing_history_provider:
                processing_history_provider.portOldLog()
                settings.setValue("/Processing/hasPortedOldLog", True)

        self.options_factory = ProcessingOptionsFactory()
        self.options_factory.setTitle(self.tr('Processing'))
        iface.registerOptionsWidgetFactory(self.options_factory)
        self.drop_handler = ProcessingDropHandler()
        iface.registerCustomDropHandler(self.drop_handler)
        self.item_provider = ProcessingDataItemProvider()
        QgsApplication.dataItemProviderRegistry().addProvider(self.item_provider)
        self.locator_filter = AlgorithmLocatorFilter()
        iface.registerLocatorFilter(self.locator_filter)
        # Invalidate the locator filter for in-place when active layer changes
        iface.currentLayerChanged.connect(lambda _: self.iface.invalidateLocatorResults())
        self.edit_features_locator_filter = InPlaceAlgorithmLocatorFilter()
        iface.registerLocatorFilter(self.edit_features_locator_filter)

        self.toolbox = ProcessingToolbox()
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.toolbox)
        self.toolbox.hide()
        self.toolbox.visibilityChanged.connect(self.toolboxVisibilityChanged)

        self.toolbox.executeWithGui.connect(self.executeAlgorithm)

        self.resultsDock = ResultsDock()
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.resultsDock)
        self.resultsDock.hide()

        self.menu = QMenu(self.iface.mainWindow().menuBar())
        self.menu.setObjectName('processing')
        self.menu.setTitle(self.tr('Pro&cessing'))

        self.toolboxAction = QAction(self.tr('&Toolbox'), self.iface.mainWindow())
        self.toolboxAction.setCheckable(True)
        self.toolboxAction.setObjectName('toolboxAction')
        self.toolboxAction.setIcon(
            QgsApplication.getThemeIcon("/processingAlgorithm.svg"))
        self.iface.registerMainWindowAction(self.toolboxAction,
                                            QKeySequence('Ctrl+Alt+T').toString(QKeySequence.NativeText))
        self.toolboxAction.toggled.connect(self.openToolbox)
        self.iface.attributesToolBar().insertAction(self.iface.actionOpenStatisticalSummary(), self.toolboxAction)
        self.menu.addAction(self.toolboxAction)

        self.modelerAction = QAction(
            QgsApplication.getThemeIcon("/processingModel.svg"),
            QCoreApplication.translate('ProcessingPlugin', '&Graphical Modeler…'), self.iface.mainWindow())
        self.modelerAction.setObjectName('modelerAction')
        self.modelerAction.triggered.connect(self.openModeler)
        self.iface.registerMainWindowAction(self.modelerAction,
                                            QKeySequence('Ctrl+Alt+G').toString(QKeySequence.NativeText))
        self.menu.addAction(self.modelerAction)

        self.historyAction = QAction(
            QgsApplication.getThemeIcon("/mIconHistory.svg"),
            QCoreApplication.translate('ProcessingPlugin', '&History…'), self.iface.mainWindow())
        self.historyAction.setObjectName('historyAction')
        self.historyAction.triggered.connect(self.openHistory)
        self.iface.registerMainWindowAction(self.historyAction,
                                            QKeySequence('Ctrl+Alt+H').toString(QKeySequence.NativeText))
        self.menu.addAction(self.historyAction)
        self.toolbox.processingToolbar.addAction(self.historyAction)

        self.resultsAction = QAction(
            QgsApplication.getThemeIcon("/processingResult.svg"),
            self.tr('&Results Viewer'), self.iface.mainWindow())
        self.resultsAction.setObjectName('resultsViewer')
        self.resultsAction.setCheckable(True)
        self.iface.registerMainWindowAction(self.resultsAction,
                                            QKeySequence('Ctrl+Alt+R').toString(QKeySequence.NativeText))

        self.menu.addAction(self.resultsAction)
        self.toolbox.processingToolbar.addAction(self.resultsAction)
        self.resultsDock.visibilityChanged.connect(self.resultsAction.setChecked)
        self.resultsAction.toggled.connect(self.resultsDock.setUserVisible)

        self.toolbox.processingToolbar.addSeparator()

        self.editInPlaceAction = QAction(
            QgsApplication.getThemeIcon("/mActionProcessSelected.svg"),
            self.tr('Edit Features In-Place'), self.iface.mainWindow())
        self.editInPlaceAction.setObjectName('editInPlaceFeatures')
        self.editInPlaceAction.setCheckable(True)
        self.editInPlaceAction.toggled.connect(self.editSelected)
        self.menu.addAction(self.editInPlaceAction)
        self.toolbox.processingToolbar.addAction(self.editInPlaceAction)

        self.toolbox.processingToolbar.addSeparator()

        self.optionsAction = QAction(
            QgsApplication.getThemeIcon("/mActionOptions.svg"),
            self.tr('Options'), self.iface.mainWindow())
        self.optionsAction.setObjectName('optionsAction')
        self.optionsAction.triggered.connect(self.openProcessingOptions)
        self.toolbox.processingToolbar.addAction(self.optionsAction)

        menuBar = self.iface.mainWindow().menuBar()
        menuBar.insertMenu(
            self.iface.firstRightStandardMenu().menuAction(), self.menu)

        self.menu.addSeparator()

        initializeMenus()
        createMenus()
        createButtons()

        # In-place editing button state sync
        self.iface.currentLayerChanged.connect(self.sync_in_place_button_state)
        self.iface.mapCanvas().selectionChanged.connect(self.sync_in_place_button_state)
        self.iface.actionToggleEditing().triggered.connect(partial(self.sync_in_place_button_state, None))
        self.sync_in_place_button_state()

        # Sync project models
        self.projectModelsMenu = None
        self.projectMenuAction = None
        self.projectMenuSeparator = None

        self.projectProvider = QgsApplication.instance().processingRegistry().providerById("project")
        self.projectProvider.algorithmsLoaded.connect(self.updateProjectModelMenu)

    def updateProjectModelMenu(self):
        """Add projects models to menu"""

        if self.projectMenuAction is None:
            self.projectModelsMenu = QMenu(self.tr("Models"))
            self.projectMenuAction = self.iface.projectMenu().insertMenu(self.iface.projectMenu().children()[-1], self.projectModelsMenu)
            self.projectMenuAction.setParent(self.projectModelsMenu)
            self.iface.projectMenu().insertSeparator(self.projectMenuAction)

        self.projectModelsMenu.clear()

        for model in self.projectProvider.algorithms():
            modelSubMenu = self.projectModelsMenu.addMenu(model.name())
            modelSubMenu.setParent(self.projectModelsMenu)
            action = QAction(self.tr("Execute…"), modelSubMenu)
            action.triggered.connect(partial(self.executeAlgorithm, model.id(), self.projectModelsMenu, self.toolbox.in_place_mode))
            modelSubMenu.addAction(action)
            if model.flags() & QgsProcessingAlgorithm.FlagSupportsBatch:
                action = QAction(self.tr("Execute as Batch Process…"), modelSubMenu)
                modelSubMenu.addAction(action)
                action.triggered.connect(partial(self.executeAlgorithm, model.id(), self.projectModelsMenu, self.toolbox.in_place_mode, True))

    @pyqtSlot(str, QWidget, bool, bool)
    def executeAlgorithm(self, alg_id, parent, in_place=False, as_batch=False):
        """Executes a project model with GUI interaction if needed.

        :param alg_id: algorithm id
        :type alg_id: string
        :param parent: parent widget
        :type parent: QWidget
        :param in_place: in place flag, defaults to False
        :type in_place: bool, optional
        :param as_batch: execute as batch flag, defaults to False
        :type as_batch: bool, optional
        """

        config = {}
        if in_place:
            config['IN_PLACE'] = True

        alg = QgsApplication.instance().processingRegistry().createAlgorithmById(alg_id, config)

        if alg is not None:

            ok, message = alg.canExecute()
            if not ok:
                dlg = MessageDialog()
                dlg.setTitle(self.tr('Error executing algorithm'))
                dlg.setMessage(
                    self.tr('<h3>This algorithm cannot '
                            'be run :-( </h3>\n{0}').format(message))
                dlg.exec_()
                return

            if as_batch:
                dlg = BatchAlgorithmDialog(alg, iface.mainWindow())
                dlg.show()
                dlg.exec_()
            else:
                in_place_input_parameter_name = 'INPUT'
                if hasattr(alg, 'inputParameterName'):
                    in_place_input_parameter_name = alg.inputParameterName()

                if in_place and not [d for d in alg.parameterDefinitions() if d.name() not in (in_place_input_parameter_name, 'OUTPUT')]:
                    parameters = {}
                    feedback = MessageBarProgress(algname=alg.displayName())
                    ok, results = execute_in_place(alg, parameters, feedback=feedback)
                    if ok:
                        iface.messageBar().pushSuccess('', self.tr('{algname} completed. %n feature(s) processed.', n=results['__count']).format(algname=alg.displayName()))
                    feedback.close()
                    # MessageBarProgress handles errors
                    return

                if alg.countVisibleParameters() > 0:
                    dlg = alg.createCustomParametersWidget(parent)

                    if not dlg:
                        dlg = AlgorithmDialog(alg, in_place, iface.mainWindow())
                    canvas = iface.mapCanvas()
                    prevMapTool = canvas.mapTool()
                    dlg.show()
                    dlg.exec_()
                    if canvas.mapTool() != prevMapTool:
                        try:
                            canvas.mapTool().reset()
                        except Exception:
                            pass
                        canvas.setMapTool(prevMapTool)
                else:
                    feedback = MessageBarProgress(algname=alg.displayName())
                    context = dataobjects.createContext(feedback)
                    parameters = {}
                    ret, results = execute(alg, parameters, context, feedback)
                    handleAlgorithmResults(alg, context, feedback)
                    feedback.close()

    def sync_in_place_button_state(self, layer=None):
        """Synchronise the button state with layer state"""

        if layer is None:
            layer = self.iface.activeLayer()

        old_enabled_state = self.editInPlaceAction.isEnabled()

        new_enabled_state = layer is not None and layer.type() == QgsMapLayerType.VectorLayer
        self.editInPlaceAction.setEnabled(new_enabled_state)

        if new_enabled_state != old_enabled_state:
            self.toolbox.set_in_place_edit_mode(new_enabled_state and self.editInPlaceAction.isChecked())

    def openProcessingOptions(self):
        self.iface.showOptionsDialog(self.iface.mainWindow(), currentPage='processingOptions')

    def unload(self):
        self.toolbox.setVisible(False)
        self.iface.removeDockWidget(self.toolbox)
        self.iface.attributesToolBar().removeAction(self.toolboxAction)

        self.resultsDock.setVisible(False)
        self.iface.removeDockWidget(self.resultsDock)

        self.toolbox.deleteLater()
        self.menu.deleteLater()

        # also delete temporary help files
        folder = tempHelpFolder()
        if QDir(folder).exists():
            shutil.rmtree(folder, True)

        self.iface.unregisterMainWindowAction(self.toolboxAction)
        self.iface.unregisterMainWindowAction(self.modelerAction)
        self.iface.unregisterMainWindowAction(self.historyAction)
        self.iface.unregisterMainWindowAction(self.resultsAction)

        self.iface.unregisterOptionsWidgetFactory(self.options_factory)
        self.iface.deregisterLocatorFilter(self.locator_filter)
        self.iface.deregisterLocatorFilter(self.edit_features_locator_filter)
        self.iface.unregisterCustomDropHandler(self.drop_handler)
        QgsApplication.dataItemProviderRegistry().removeProvider(self.item_provider)

        removeButtons()
        removeMenus()

        if self.projectMenuAction is not None:
            self.iface.projectMenu().removeAction(self.projectMenuAction)
            self.projectMenuAction = None
        if self.projectMenuSeparator is not None:
            self.iface.projectMenu().removeAction(self.projectMenuSeparator)
            self.projectMenuSeparator = None

        Processing.deinitialize()

    def openToolbox(self, show):
        self.toolbox.setUserVisible(show)

    def toolboxVisibilityChanged(self, visible):
        self.toolboxAction.setChecked(visible)

    def openModeler(self):
        dlg = ModelerDialog.create()
        dlg.update_model.connect(self.updateModel)
        dlg.show()

    def updateModel(self):
        model_provider = QgsApplication.processingRegistry().providerById('model')
        model_provider.refreshAlgorithms()

    def openResults(self):
        if self.resultsDock.isVisible():
            self.resultsDock.hide()
        else:
            self.resultsDock.show()

    def openHistory(self):
        dlg = HistoryDialog()
        dlg.exec_()

    def tr(self, message, disambiguation=None, n=-1):
        return QCoreApplication.translate('ProcessingPlugin', message, disambiguation=disambiguation, n=n)

    def editSelected(self, enabled):
        self.toolbox.set_in_place_edit_mode(enabled)
Example #11
0
class ShapeTools(object):
    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.settingsDialog = None
        self.xyLineDialog = None
        self.geodesicDensifyDialog = None
        self.azDigitizerTool = None
        self.lineDigitizerTool = None
        self.previousLayer = None
        self.toolbar = self.iface.addToolBar('Shape Tools Toolbar')
        self.toolbar.setObjectName('ShapeToolsToolbar')
        self.provider = ShapeToolsProvider()
        # Initialize the plugin path directory
        self.plugin_dir = os.path.dirname(__file__)

        # initialize locale
        try:
            locale = QSettings().value("locale/userLocale", "en",
                                       type=str)[0:2]
        except Exception:
            locale = "en"
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'shapeTools_{}.qm'.format(locale))
        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

    def initGui(self):
        self.azDigitizerTool = AzDigitizerTool(self.iface)
        self.lineDigitizerTool = LineDigitizerTool(self.iface)

        # Initialize the create shape menu items
        menu = QMenu()
        # Initialize Create Arc Wedge tool
        icon = QIcon(self.plugin_dir + '/images/arc.png')
        self.createArcAction = menu.addAction(icon, tr('Create arc wedge'),
                                              self.createArc)
        self.createArcAction.setObjectName('stCreateArcWedge')
        icon = QIcon(self.plugin_dir + '/images/donut.png')
        self.createDonutAction = menu.addAction(icon, tr('Create donut'),
                                                self.createDonut)
        self.createDonutAction.setObjectName('stCreateDonut')
        icon = QIcon(self.plugin_dir + '/images/ellipse.png')
        self.createEllipseAction = menu.addAction(icon, tr('Create ellipse'),
                                                  self.createEllipse)
        self.createEllipseAction.setObjectName('stCreateEllipse')
        icon = QIcon(self.plugin_dir + '/images/rose.png')
        self.createEllipseRoseAction = menu.addAction(
            icon, tr('Create ellipse rose'), self.createEllipseRose)
        self.createEllipseRoseAction.setObjectName('stCreateEllipseRose')
        icon = QIcon(self.plugin_dir + '/images/epicycloid.png')
        self.createEpicycloidAction = menu.addAction(icon,
                                                     tr('Create epicycloid'),
                                                     self.createEpicycloid)
        self.createEpicycloidAction.setObjectName('stCreateEpicycloid')
        icon = QIcon(self.plugin_dir + '/images/heart.png')
        self.createHeartAction = menu.addAction(icon, tr('Create heart'),
                                                self.createHeart)
        self.createHeartAction.setObjectName('stCreateHeart')
        icon = QIcon(self.plugin_dir + '/images/hypocycloid.png')
        self.createHypocycloidAction = menu.addAction(icon,
                                                      tr('Create hypocycloid'),
                                                      self.createHypocycloid)
        self.createHypocycloidAction.setObjectName('stCreateHypocycloid')
        icon = QIcon(self.plugin_dir + '/images/line.png')
        self.createLOBAction = menu.addAction(icon,
                                              tr('Create line of bearing'),
                                              self.createLOB)
        self.createLOBAction.setObjectName('stCreateLineOfBearing')
        icon = QIcon(self.plugin_dir + '/images/pie.png')
        self.createPieAction = menu.addAction(icon, tr('Create pie wedge'),
                                              self.createPie)
        self.createPieAction.setObjectName('stCreatePie')
        icon = QIcon(self.plugin_dir + '/images/polyfoil.png')
        self.createPolyfoilAction = menu.addAction(icon, tr('Create polyfoil'),
                                                   self.createPolyfoil)
        self.createPolyfoilAction.setObjectName('stCreatePolyfoil')
        icon = QIcon(self.plugin_dir + '/images/polygon.png')
        self.createPolygonAction = menu.addAction(icon, tr('Create polygon'),
                                                  self.createPolygon)
        self.createPolygonAction.setObjectName('stCreatePolygon')
        icon = QIcon(self.plugin_dir + '/images/radialLines.png')
        self.createPolygonAction = menu.addAction(icon,
                                                  tr('Create radial lines'),
                                                  self.createRadialLines)
        self.createPolygonAction.setObjectName('stCreateRadialLines')
        icon = QIcon(self.plugin_dir + '/images/star.png')
        self.createStarAction = menu.addAction(icon, tr('Create star'),
                                               self.createStar)
        self.createStarAction.setObjectName('stCreateStar')
        # Add the shape creation tools to the menu
        icon = QIcon(self.plugin_dir + '/images/shapes.png')
        self.createShapesAction = QAction(icon, tr('Create shapes'),
                                          self.iface.mainWindow())
        self.createShapesAction.setMenu(menu)
        self.iface.addPluginToVectorMenu('Shape Tools',
                                         self.createShapesAction)
        # Add the shape creation tools to the toolbar
        self.createShapeButton = QToolButton()
        self.createShapeButton.setMenu(menu)
        self.createShapeButton.setDefaultAction(self.createDonutAction)
        self.createShapeButton.setPopupMode(QToolButton.MenuButtonPopup)
        self.createShapeButton.triggered.connect(self.createShapeTriggered)
        self.createShapeToolbar = self.toolbar.addWidget(
            self.createShapeButton)

        # Initialize the XY to Line menu item
        icon = QIcon(self.plugin_dir + '/images/xyline.svg')
        self.xyLineAction = QAction(icon, tr('XY to line'),
                                    self.iface.mainWindow())
        self.xyLineAction.setObjectName('stXYtoLine')
        self.xyLineAction.triggered.connect(self.xyLineTool)
        self.iface.addPluginToVectorMenu('Shape Tools', self.xyLineAction)
        self.toolbar.addAction(self.xyLineAction)

        # Initialize the Geodesic Densifier menu item
        icon = QIcon(self.plugin_dir + '/images/geodesicDensifier.svg')
        self.geodesicDensifyAction = QAction(icon,
                                             tr('Geodesic shape densifier'),
                                             self.iface.mainWindow())
        self.geodesicDensifyAction.setObjectName('stGeodesicDensifier')
        self.geodesicDensifyAction.triggered.connect(self.geodesicDensifyTool)
        self.iface.addPluginToVectorMenu('Shape Tools',
                                         self.geodesicDensifyAction)
        self.toolbar.addAction(self.geodesicDensifyAction)

        # Initialize the Geodesic decimation menu items
        menu = QMenu()
        icon = QIcon(self.plugin_dir + '/images/geodesicLineDecimate.svg')
        self.lineDecimateAction = menu.addAction(icon,
                                                 tr('Geodesic line decimate'),
                                                 self.lineDecimateTool)
        self.lineDecimateAction.setObjectName('stGeodesicLineDecimate')
        icon = QIcon(self.plugin_dir + '/images/geodesicPointDecimate.svg')
        self.pointDecimateAction = menu.addAction(
            icon, tr('Geodesic point decimate'), self.pointDecimateTool)
        self.pointDecimateAction.setObjectName('stGeodesicPointDecimate')
        # Add the decimation tools to the menu
        icon = QIcon(self.plugin_dir + '/images/geodesicLineDecimate.svg')
        self.simplifyGeomAction = QAction(
            icon, tr('Geodesic geometry simplification'),
            self.iface.mainWindow())
        self.simplifyGeomAction.setMenu(menu)
        self.iface.addPluginToVectorMenu('Shape Tools',
                                         self.simplifyGeomAction)
        # Add the shape creation tools to the toolbar
        self.simplifyButton = QToolButton()
        self.simplifyButton.setMenu(menu)
        self.simplifyButton.setDefaultAction(self.lineDecimateAction)
        self.simplifyButton.setPopupMode(QToolButton.MenuButtonPopup)
        self.simplifyButton.triggered.connect(self.simplifyTriggered)
        self.simplifyToolbar = self.toolbar.addWidget(self.simplifyButton)

        # Initialize the Geodesic line break menu item
        icon = QIcon(self.plugin_dir + '/images/idlbreak.svg')
        self.geodesicLineBreakAction = QAction(
            icon, tr('Geodesic line break at -180,180'),
            self.iface.mainWindow())
        self.geodesicLineBreakAction.setObjectName('stGeodesicLineBreak')
        self.geodesicLineBreakAction.triggered.connect(
            self.geodesicLineBreakTool)
        self.iface.addPluginToVectorMenu('Shape Tools',
                                         self.geodesicLineBreakAction)
        self.toolbar.addAction(self.geodesicLineBreakAction)

        # Initialize Geodesic Measure Tool
        self.geodesicMeasureTool = GeodesicMeasureTool(self.iface,
                                                       self.iface.mainWindow())
        icon = QIcon(self.plugin_dir + '/images/measure.svg')
        self.measureAction = QAction(icon, tr('Geodesic measure tool'),
                                     self.iface.mainWindow())
        self.measureAction.setObjectName('stGeodesicMeasureTool')
        self.measureAction.triggered.connect(self.measureTool)
        self.measureAction.setCheckable(True)
        self.iface.addPluginToVectorMenu('Shape Tools', self.measureAction)
        self.toolbar.addAction(self.measureAction)

        # Initialize Geodesic Measurement layer
        icon = QIcon(self.plugin_dir + '/images/measureLine.svg')
        self.measureLayerAction = QAction(icon,
                                          tr('Geodesic measurement layer'),
                                          self.iface.mainWindow())
        self.measureLayerAction.setObjectName('stGeodesicLineBreak')
        self.measureLayerAction.triggered.connect(self.measureLayerTool)
        self.iface.addPluginToVectorMenu('Shape Tools',
                                         self.measureLayerAction)
        self.toolbar.addAction(self.measureLayerAction)

        menu = QMenu()
        # Initialize Geodesic transformation tool
        icon = QIcon(self.plugin_dir + '/images/transformShape.svg')
        self.transformationsAction = menu.addAction(
            icon, tr('Geodesic transformations'), self.transformTool)
        self.transformationsAction.setObjectName('stGeodesicTransformations')

        icon = QIcon(self.plugin_dir + '/images/flip.svg')
        self.flipRotateAction = menu.addAction(icon,
                                               tr('Geodesic flip and rotate'),
                                               self.flipRotateTool)
        self.flipRotateAction.setObjectName('stGeodesicFlipRotate')

        icon = QIcon(self.plugin_dir + '/images/flipHorizontal.svg')
        self.flipHorizontalAction = menu.addAction(icon, tr('Flip horizontal'),
                                                   self.flipHorizontalTool)
        self.flipHorizontalAction.setObjectName('stGeodesicFlipHorizontal')
        self.flipHorizontalAction.setEnabled(False)
        icon = QIcon(self.plugin_dir + '/images/flipVertical.svg')
        self.flipVerticalAction = menu.addAction(icon, tr('Flip vertical'),
                                                 self.flipVerticalTool)
        self.flipVerticalAction.setObjectName('stGeodesicFlipVertical')
        self.flipVerticalAction.setEnabled(False)
        icon = QIcon(self.plugin_dir + '/images/rotate180.svg')
        self.rotate180Action = menu.addAction(icon, tr('Rotate 180\xb0'),
                                              self.rotate180Tool)
        self.rotate180Action.setObjectName('stGeodesicRotate180')
        self.rotate180Action.setEnabled(False)
        icon = QIcon(self.plugin_dir + '/images/rotatecw.svg')
        self.rotate90CWAction = menu.addAction(icon, tr('Rotate 90\xb0 CW'),
                                               self.rotate90CWTool)
        self.rotate90CWAction.setObjectName('stGeodesicRotate90CW')
        self.rotate90CWAction.setEnabled(False)
        icon = QIcon(self.plugin_dir + '/images/rotateccw.svg')
        self.rotate90CCWAction = menu.addAction(icon, tr('Rotate 90\xb0 CCW'),
                                                self.rotate90CCWTool)
        self.rotate90CCWAction.setObjectName('stGeodesicRotate90CCW')
        self.rotate90CCWAction.setEnabled(False)
        self.transformsAction = QAction(icon, tr('Geodesic transforms'),
                                        self.iface.mainWindow())
        self.transformsAction.setMenu(menu)
        self.iface.addPluginToVectorMenu('Shape Tools', self.transformsAction)

        self.transformationButton = QToolButton()
        self.transformationButton.setMenu(menu)
        self.transformationButton.setDefaultAction(self.transformationsAction)
        self.transformationButton.setPopupMode(QToolButton.MenuButtonPopup)
        self.transformationButton.triggered.connect(self.toolButtonTriggered)
        self.tranformToolbar = self.toolbar.addWidget(
            self.transformationButton)

        # Initialize the Azimuth Distance Digitize function
        icon = QIcon(self.plugin_dir + '/images/dazdigitize.svg')
        self.digitizeAction = QAction(icon, tr('Azimuth distance digitizer'),
                                      self.iface.mainWindow())
        self.digitizeAction.setObjectName('stAzDistanceDigitizer')
        self.digitizeAction.triggered.connect(self.setShowAzDigitizerTool)
        self.digitizeAction.setCheckable(True)
        self.digitizeAction.setEnabled(False)
        self.iface.addPluginToVectorMenu(u'Shape Tools', self.digitizeAction)
        self.toolbar.addAction(self.digitizeAction)

        # Initialize the multi point azimuth Digitize function
        icon = QIcon(self.plugin_dir + '/images/linedigitize.svg')
        self.lineDigitizeAction = QAction(
            icon, tr('Azimuth distance sequence digitizer'),
            self.iface.mainWindow())
        self.lineDigitizeAction.setObjectName('stLineDigitizer')
        self.lineDigitizeAction.triggered.connect(self.setShowLineDigitizeTool)
        self.lineDigitizeAction.setCheckable(True)
        self.lineDigitizeAction.setEnabled(False)
        self.iface.addPluginToVectorMenu(u'Shape Tools',
                                         self.lineDigitizeAction)
        self.toolbar.addAction(self.lineDigitizeAction)

        # Settings
        icon = QIcon(':/images/themes/default/mActionOptions.svg')
        self.settingsAction = QAction(icon, tr('Settings'),
                                      self.iface.mainWindow())
        self.settingsAction.setObjectName('shapeToolsSettings')
        self.settingsAction.triggered.connect(self.settings)
        self.iface.addPluginToVectorMenu('Shape Tools', self.settingsAction)

        # Help
        icon = QIcon(self.plugin_dir + '/images/help.svg')
        self.helpAction = QAction(icon, tr('Shape Tools help'),
                                  self.iface.mainWindow())
        self.helpAction.setObjectName('shapeToolsHelp')
        self.helpAction.triggered.connect(self.help)
        self.iface.addPluginToVectorMenu('Shape Tools', self.helpAction)

        self.iface.currentLayerChanged.connect(self.currentLayerChanged)
        self.canvas.mapToolSet.connect(self.unsetTool)
        self.enableTools()

        # Add the processing provider
        QgsApplication.processingRegistry().addProvider(self.provider)

    def unsetTool(self, tool):
        try:
            if not isinstance(tool, GeodesicMeasureTool):
                self.measureAction.setChecked(False)
                self.geodesicMeasureTool.endInteractiveLine()
            if not isinstance(tool, AzDigitizerTool):
                self.digitizeAction.setChecked(False)
            if not isinstance(tool, LineDigitizerTool):
                self.lineDigitizeAction.setChecked(False)
        except Exception:
            pass

    def unload(self):
        self.canvas.unsetMapTool(self.azDigitizerTool)
        self.canvas.unsetMapTool(self.lineDigitizerTool)

        # remove from menu
        self.iface.removePluginVectorMenu('Shape Tools',
                                          self.createShapesAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.xyLineAction)
        self.iface.removePluginVectorMenu('Shape Tools',
                                          self.geodesicDensifyAction)
        self.iface.removePluginVectorMenu('Shape Tools',
                                          self.simplifyGeomAction)
        self.iface.removePluginVectorMenu('Shape Tools',
                                          self.geodesicLineBreakAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.measureAction)
        self.iface.removePluginVectorMenu('Shape Tools',
                                          self.measureLayerAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.transformsAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.digitizeAction)
        self.iface.removePluginVectorMenu('Shape Tools',
                                          self.lineDigitizeAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.settingsAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.helpAction)
        # Remove from toolbar
        self.iface.removeToolBarIcon(self.createShapeToolbar)
        self.iface.removeToolBarIcon(self.xyLineAction)
        self.iface.removeToolBarIcon(self.geodesicDensifyAction)
        self.iface.removeToolBarIcon(self.simplifyToolbar)
        self.iface.removeToolBarIcon(self.geodesicLineBreakAction)
        self.iface.removeToolBarIcon(self.measureAction)
        self.iface.removeToolBarIcon(self.measureLayerAction)
        self.iface.removeToolBarIcon(self.transformationsAction)
        self.iface.removeToolBarIcon(self.digitizeAction)
        self.iface.removeToolBarIcon(self.lineDigitizeAction)
        self.iface.removeToolBarIcon(self.tranformToolbar)
        self.azDigitizerTool = None
        self.lineDigitizerTool = None
        # remove the toolbar
        del self.toolbar

        QgsApplication.processingRegistry().removeProvider(self.provider)

    def toolButtonTriggered(self, action):
        self.transformationButton.setDefaultAction(action)

    def createShapeTriggered(self, action):
        self.createShapeButton.setDefaultAction(action)

    def simplifyTriggered(self, action):
        self.simplifyButton.setDefaultAction(action)

    def setShowAzDigitizerTool(self):
        self.digitizeAction.setChecked(True)
        self.canvas.setMapTool(self.azDigitizerTool)

    def setShowLineDigitizeTool(self):
        self.lineDigitizeAction.setChecked(True)
        self.canvas.setMapTool(self.lineDigitizerTool)

    def xyLineTool(self):
        processing.execAlgorithmDialog('shapetools:xy2line', {})

    def geodesicDensifyTool(self):
        processing.execAlgorithmDialog('shapetools:geodesicdensifier', {})

    def pointDecimateTool(self):
        processing.execAlgorithmDialog('shapetools:geodesicpointdecimate', {})

    def lineDecimateTool(self):
        processing.execAlgorithmDialog('shapetools:geodesiclinedecimate', {})

    def geodesicLineBreakTool(self):
        processing.execAlgorithmDialog('shapetools:linebreak', {})

    def measureTool(self):
        self.measureAction.setChecked(True)
        self.canvas.setMapTool(self.geodesicMeasureTool)

    def createArc(sefl):
        processing.execAlgorithmDialog('shapetools:createarc', {})

    def createDonut(sefl):
        processing.execAlgorithmDialog('shapetools:createdonut', {})

    def createEllipse(sefl):
        processing.execAlgorithmDialog('shapetools:createellipse', {})

    def createEllipseRose(sefl):
        processing.execAlgorithmDialog('shapetools:createrose', {})

    def createEpicycloid(sefl):
        processing.execAlgorithmDialog('shapetools:createepicycloid', {})

    def createHeart(sefl):
        processing.execAlgorithmDialog('shapetools:createheart', {})

    def createHypocycloid(sefl):
        processing.execAlgorithmDialog('shapetools:createhypocycloid', {})

    def createLOB(sefl):
        processing.execAlgorithmDialog('shapetools:createlob', {})

    def createPie(sefl):
        processing.execAlgorithmDialog('shapetools:createpie', {})

    def createPolyfoil(sefl):
        processing.execAlgorithmDialog('shapetools:createpolyfoil', {})

    def createPolygon(sefl):
        processing.execAlgorithmDialog('shapetools:createpolygon', {})

    def createRadialLines(sefl):
        processing.execAlgorithmDialog('shapetools:createradiallines', {})

    def createStar(sefl):
        processing.execAlgorithmDialog('shapetools:createstar', {})

    def measureLayerTool(self):
        processing.execAlgorithmDialog('shapetools:measurelayer', {})

    def transformTool(self):
        processing.execAlgorithmDialog('shapetools:geodesictransformations',
                                       {})

    def flipRotateTool(self):
        processing.execAlgorithmDialog('shapetools:geodesicflip', {})

    def flipHorizontalTool(self):
        layer = self.iface.activeLayer()
        flipLayer(self.iface, layer, 0)

    def flipVerticalTool(self):
        layer = self.iface.activeLayer()
        flipLayer(self.iface, layer, 1)

    def rotate180Tool(self):
        layer = self.iface.activeLayer()
        flipLayer(self.iface, layer, 2)

    def rotate90CWTool(self):
        layer = self.iface.activeLayer()
        flipLayer(self.iface, layer, 3)

    def rotate90CCWTool(self):
        layer = self.iface.activeLayer()
        flipLayer(self.iface, layer, 4)

    def settings(self):
        if self.settingsDialog is None:
            self.settingsDialog = SettingsWidget(self.iface,
                                                 self.iface.mainWindow())
        self.settingsDialog.show()

    def help(self):
        '''Display a help page'''
        url = QUrl.fromLocalFile(self.plugin_dir + '/index.html').toString()
        webbrowser.open(url, new=2)

    def currentLayerChanged(self):
        layer = self.iface.activeLayer()
        if self.previousLayer is not None:
            try:
                self.previousLayer.editingStarted.disconnect(
                    self.layerEditingChanged)
            except Exception:
                pass
            try:
                self.previousLayer.editingStopped.disconnect(
                    self.layerEditingChanged)
            except Exception:
                pass
        self.previousLayer = None
        if layer is not None:
            if isinstance(layer, QgsVectorLayer):
                layer.editingStarted.connect(self.layerEditingChanged)
                layer.editingStopped.connect(self.layerEditingChanged)
                self.previousLayer = layer
        self.enableTools()

    def layerEditingChanged(self):
        self.enableTools()

    def enableTools(self):
        self.digitizeAction.setEnabled(False)
        self.lineDigitizeAction.setEnabled(False)
        self.flipHorizontalAction.setEnabled(False)
        self.flipVerticalAction.setEnabled(False)
        self.rotate180Action.setEnabled(False)
        self.rotate90CWAction.setEnabled(False)
        self.rotate90CCWAction.setEnabled(False)
        layer = self.iface.activeLayer()

        if not layer or not layer.isValid() or (
                layer.type() !=
                QgsMapLayer.VectorLayer) or not layer.isEditable():
            return
        wkbtype = layer.wkbType()
        geomtype = QgsWkbTypes.geometryType(wkbtype)
        self.lineDigitizeAction.setEnabled(True)
        if geomtype == QgsWkbTypes.PointGeometry or geomtype == QgsWkbTypes.LineGeometry:
            self.digitizeAction.setEnabled(True)
        if geomtype == QgsWkbTypes.LineGeometry or geomtype == QgsWkbTypes.PolygonGeometry:
            self.flipHorizontalAction.setEnabled(True)
            self.flipVerticalAction.setEnabled(True)
            self.rotate180Action.setEnabled(True)
            self.rotate90CWAction.setEnabled(True)
            self.rotate90CCWAction.setEnabled(True)
Example #12
0
class GeToolsPlugin:
    def __init__(self, iface):
        self.iface = iface

        locale = QgsApplication.locale()
        qmPath = '{}/i18n/getools_{}.qm'.format(pluginPath, locale)

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

    def initGui(self):
        self.actionPostionToGe = QAction(self.tr('Coords to the Google Earth'), self.iface.mainWindow())
        self.actionPostionToGe.setIcon(QIcon(os.path.join(pluginPath, 'icons', 'ge-click.svg')))
        self.actionPostionToGe.setObjectName('gePosition')
        self.actionPostionToGe.setCheckable(True)

        self.actionFeaturesToGe = QAction(self.tr('Feature(s) to the Google Earth'), self.iface.mainWindow())
        self.actionFeaturesToGe.setIcon(QIcon(os.path.join(pluginPath, 'icons', 'ge-selected.svg')))
        self.actionFeaturesToGe.setObjectName('geFeatures')
        self.actionFeaturesToGe.setCheckable(True)

        self.actionVectorToGe = QAction(self.tr('Vector layer to the Google Earth'), self.iface.mainWindow())
        self.actionVectorToGe.setIcon(QIcon(os.path.join(pluginPath, 'icons', 'ge-export-vector.svg')))
        self.actionVectorToGe.setObjectName('geVector')

        self.actionRasterToGe = QAction(self.tr('Raster layer to the Google Earth'), self.iface.mainWindow())
        self.actionRasterToGe.setIcon(QIcon(os.path.join(pluginPath, 'icons', 'ge-export-raster.svg')))
        self.actionRasterToGe.setObjectName('geRaster')

        self.actionSettings = QAction(self.tr('Settings…'), self.iface.mainWindow())
        self.actionSettings.setIcon(QgsApplication.getThemeIcon('/mActionOptions.svg'))
        self.actionSettings.setObjectName('geSettings')

        self.actionAbout = QAction(self.tr('About GETools…'), self.iface.mainWindow())
        self.actionAbout.setIcon(QgsApplication.getThemeIcon('/mActionHelpContents.svg'))
        self.actionAbout.setObjectName('geAbout')

        self.iface.addPluginToMenu(self.tr('GETools'), self.actionPostionToGe)
        self.iface.addPluginToMenu(self.tr('GETools'), self.actionFeaturesToGe)
        self.iface.addPluginToMenu(self.tr('GETools'), self.actionVectorToGe)
        self.iface.addPluginToMenu(self.tr('GETools'), self.actionRasterToGe)
        self.iface.addPluginToMenu(self.tr('GETools'), self.actionSettings)
        self.iface.addPluginToMenu(self.tr('GETools'), self.actionAbout)

        self.iface.addVectorToolBarIcon(self.actionPostionToGe)
        self.iface.addVectorToolBarIcon(self.actionFeaturesToGe)
        self.iface.addVectorToolBarIcon(self.actionVectorToGe)
        self.iface.addRasterToolBarIcon(self.actionRasterToGe)

        self.actionPostionToGe.triggered.connect(self.selectPosition)
        self.actionFeaturesToGe.triggered.connect(self.selectFeatures)
        self.actionVectorToGe.triggered.connect(self.exportLayer)
        self.actionRasterToGe.triggered.connect(self.exportLayer)
        self.actionSettings.triggered.connect(self.settings)
        self.actionAbout.triggered.connect(self.about)

        self.toolClick = MapToolClick(self.iface.mapCanvas())
        self.toolClick.setAction(self.actionPostionToGe)
        self.toolClick.canvasClicked.connect(self.exportPosition)

        self.toolSelect = MapToolSelect(self.iface.mapCanvas())
        self.toolSelect.setAction(self.actionFeaturesToGe)
        self.toolSelect.featuresSelected.connect(self.exportFeatures)

        self.iface.currentLayerChanged.connect(self.toggleButtons)

        self.taskManager = QgsApplication.taskManager()

    def unload(self):
        self.iface.removeVectorToolBarIcon(self.actionPostionToGe)
        self.iface.removeVectorToolBarIcon(self.actionFeaturesToGe)
        self.iface.removeVectorToolBarIcon(self.actionVectorToGe)
        self.iface.removeRasterToolBarIcon(self.actionRasterToGe)

        self.iface.removePluginMenu(self.tr('GETools'), self.actionPostionToGe)
        self.iface.removePluginMenu(self.tr('GETools'), self.actionFeaturesToGe)
        self.iface.removePluginMenu(self.tr('GETools'), self.actionVectorToGe)
        self.iface.removePluginMenu(self.tr('GETools'), self.actionRasterToGe)
        self.iface.removePluginMenu(self.tr('GETools'), self.actionSettings)
        self.iface.removePluginMenu(self.tr('GETools'), self.actionAbout)

        if self.iface.mapCanvas().mapTool() == self.toolClick:
            self.iface.mapCanvas().unsetMapTool(self.toolClick)
        if self.iface.mapCanvas().mapTool() == self.toolSelect:
            self.iface.mapCanvas().unsetMapTool(self.toolSelect)

        del self.toolClick
        del self.toolSelect

        utils.removeTempFiles()

    def settings(self):
        dlg = OptionsDialog(self.iface.mainWindow())
        dlg.exec_()

    def about(self):
        dlg = AboutDialog()
        dlg.exec_()

    def tr(self, text):
        return QCoreApplication.translate('GeTools', text)

    def selectPosition(self):
        self.iface.mapCanvas().setMapTool(self.toolClick)

    def selectFeatures(self):
        self.iface.mapCanvas().setMapTool(self.toolSelect)

    def toggleButtons(self, layer):
        if layer and layer.type() == QgsMapLayer.VectorLayer:
            self.actionFeaturesToGe.setEnabled(True)
            self.actionVectorToGe.setEnabled(True)
            self.actionRasterToGe.setEnabled(False)
        elif layer and layer.type() == QgsMapLayer.RasterLayer:
            self.actionFeaturesToGe.setEnabled(False)
            self.actionVectorToGe.setEnabled(False)
            self.actionRasterToGe.setEnabled(True)
        else:
            self.actionFeaturesToGe.setEnabled(False)
            self.actionVectorToGe.setEnabled(False)
            self.actionRasterToGe.setEnabled(False)

    def exportPosition(self, point, button):
        task = KmlWriterTask(point)
        task.exportComplete.connect(self.completed)
        task.errorOccurred.connect(self.errored)

        self.taskManager.addTask(task)

    def exportFeatures(self):
        task = KmlWriterTask(self.iface.activeLayer(), True)
        task.exportComplete.connect(self.completed)
        task.errorOccurred.connect(self.errored)

        self.taskManager.addTask(task)

    def exportLayer(self):
        task = KmlWriterTask(self.iface.activeLayer())
        task.exportComplete.connect(self.completed)
        task.errorOccurred.connect(self.errored)

        self.taskManager.addTask(task)

    def completed(self, fileName):
        uri = QUrl.fromLocalFile(fileName)
        self.iface.messageBar().pushSuccess(self.tr('GETools'), self.tr('Successfully exported to <a href="{}">{}</a>').format(uri.toString(), fileName))
        QDesktopServices.openUrl(uri)

    def errored(self, error):
        self.iface.messageBar().pushWarning(self.tr('GETools'), error)
Example #13
0
class DatabaseExport(object):

    def __init__(self, iface):
        self.iface = iface
        self.dialog = None
        self.cancel = False
        self.act = QAction('Database Export', iface.mainWindow())
        self.act.triggered.connect(self.execDialog)

    def initGui(self, menu=None):
        if menu is not None:
            menu.addAction(self.act)
        else:
            self.iface.addToolBarIcon(self.act)

    def unload(self, menu=None):
        if menu is None:
            menu.removeAction(self.act)
        else:
            self.iface.removeToolBarIcon(self.act)

    def execDialog(self):
        """
        """
        self.dialog = PluginDialog(self.iface, self.iface.mainWindow())
        self.dialog.accepted.connect(self.execTool)
        self.dialog.rejected.connect(self.quitDialog)
        self.dialog.setModal(False)
        self.act.setEnabled(False)
        self.dialog.show()

    def scheduleAbort(self):
        self.cancel = True

    def quitDialog(self):
        self.dialog = None
        self.act.setEnabled(True)
        self.cancel = False

    def execTool(self):
        # check if export folder exists
        folder = self.dialog.out_folder.text()
        if not os.path.exists(folder):
            self.quitDialog()
            return

        proj_name = self.dialog.project_box.currentText()
        if proj_name == '':
            self.quitDialog()
            return

        host = self.dialog.db_host.text()
        port = self.dialog.db_port.value()
        name = self.dialog.db_name.text()
        user = self.dialog.db_user.text()
        pwrd = self.dialog.db_pass.text()

        # try to set up a database connection
        try:
            conn_str = "dbname='{}' user='******' host='{}' port={} password='******'".format(name, user, host, port, pwrd)
            conn = psycopg2.connect(conn_str)
        except psycopg2.DatabaseError:
            self.quitDialog()
            return

        cur = conn.cursor()


#Start here the export function
        if self.dialog.profiles_box.isChecked():
            self.export_river_profiles(proj_name, cur, folder, self.dialog.db_name.text())
        if self.dialog.floodplain_box.isChecked():
            self.export_floodplains(proj_name, cur, folder, self.dialog.db_name.text())
        if self.dialog.materials_box.isChecked():
            self.export_materials(proj_name, cur, folder, self.dialog.db_name.text())

        QMessageBox.information(self.iface.mainWindow(), "Database Export", "Export finished successfully.")
        self.quitDialog()


#Export function  (Why static?)
#Export river profiles
    @staticmethod
    def export_river_profiles(proj_name, cursor, folder, db_name):

        cursor.execute('SELECT "model_id", "name" FROM {}.hyd_river_general_prm'.format(proj_name))

        for r in cursor.fetchall():
            filename = os.path.join(folder, 'river_{:d}.txt'.format(r[0]))
            with open(filename, 'w') as rvr_file:

                cursor.execute("""
                    SELECT "profile_glob_id", "profile_id", "name", "riverstation", "delta_x",
                           "connection_type", "profile_type", "init_condition", "left_overflow",
                           "left_poleni", "right_overflow", "right_poleni"
                    FROM {}.hyd_river_profile_prm
                    WHERE river_id = {}
                    ORDER BY "riverstation" DESC
                """.format(proj_name, r[0]))
                rvr_file.write('########################################################################\n')
                rvr_file.write('# This file was automatically generated by ProMaIDes Database '
                                   'Export-QGIS-Plugin Version {version_1} \n'.format(version_1=VERSION))
                # date and time output
                now = datetime.now()
                dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
                rvr_file.write('# Generated at {dt_string_1} '.format(dt_string_1=dt_string))
                rvr_file.write('from project {filename_1} from database {db} \n'.format(filename_1=proj_name,
                                                                                       db = db_name))
                rvr_file.write('# Comments are marked with #\n')
                rvr_file.write('########################################################################\n')
                rvr_file.write("""
TITLE = "{}"
VARIABLES = "X", "Y", "Z", "MathType", "Distance", "Ident"
DATASETAUXDATA  NumProf = "{:d}"
                """.format(r[1], cursor.rowcount))

                for p in cursor.fetchall():

                    cursor.execute("""
                        SELECT "point_x", "point_y", "point_z", "material_id", "distance", "bank_channel_id"
                        FROM {}.hyd_river_profile_points_prm
                        WHERE profile_id = {}
                        ORDER BY "distance" ASC
                    """.format(proj_name, p[0]))

                    num_points = cursor.rowcount
                    points = cursor.fetchall()

                    bc_point = False
                    bc_point_stat = True
                    bc_point_val = 0
                    bc_lat = False
                    bc_lat_stat = True
                    bc_lat_val = 0
                    bc_waterl = False
                    bc_waterl_stat = True
                    bc_waterl_val = 0

                    cursor.execute("""
                        SELECT "boundary_type", "stationary", "value"
                        FROM {}.hyd_river_prof_bound_condition_prm
                        WHERE profile_glob_id = {}
                    """.format(proj_name, p[0]))

                    for bc in cursor:
                        if bc[0] == "point":
                            bc_point = True
                            bc_point_stat = bc[1]
                            bc_point_val = bc[2]
                        elif bc[0] == "lateral":
                            bc_lat = True
                            bc_lat_stat = bc[1]
                            bc_lat_val = bc[2]
                        elif bc[0] == "waterlevel":
                            bc_waterl = True
                            bc_waterl_stat = bc[1]
                            bc_waterl_val = bc[2]

                    rvr_file.write("""
ZONE T="{}" I={:d}
AUXDATA ProfLDist = "{}"
AUXDATA DeltaXtable = "{}"
AUXDATA ConnectionType = "{}"
AUXDATA ProfType = "{}"
AUXDATA InitCondition = "{}"
AUXDATA BoundaryPointCondition = "{}"
AUXDATA BoundaryPointStationary = "{}"
AUXDATA BoundaryPointValue = "{}"
AUXDATA BoundaryLateralCondition = "{}"
AUXDATA BoundaryLateralStationary = "{}"
AUXDATA BoundaryLateralValue = "{}"
AUXDATA BoundaryWaterlevelCondition = "{}"
AUXDATA BoundaryWaterlevelStationary = "{}"
AUXDATA BoundaryWaterlevelValue = "{}"
AUXDATA OverflowCouplingLeft = "{}"
AUXDATA PoleniFacLeft = "{}"
AUXDATA OverCouplingRight = "{}"
AUXDATA PoleniFacRight = "{}"
                        \n""".format(p[2], num_points, p[3], p[4], p[5], p[6], p[7],
                               str(bc_point).lower(), str(bc_point_stat).lower(), int(bc_point_val) if not bc_point_stat else bc_point_val,
                               str(bc_lat).lower(), str(bc_lat_stat).lower(), int(bc_lat_val) if not bc_lat_stat else bc_lat_val,
                               str(bc_waterl).lower(), str(bc_waterl_stat).lower(), int(bc_waterl_val) if not bc_waterl_stat else bc_waterl_val,
                               str(p[8]).lower(), p[9], str(p[10]).lower(), p[11]))

                    for point in points:
                        rvr_file.write('\t'.join(map(str, point)) + '\n')
#Export floodplain data
    @staticmethod
    def export_floodplains(proj_name, cursor, folder , db_name):

        cursor.execute("""
            SELECT "model_id", "nx", "ny", "low_left_x", "low_left_y", "elemwidth_x",
                   "elemwidth_y", "noinfo_value", "angle"
            FROM {}.hyd_floodplain_general_prm
            """.format(proj_name))

        for fpl in cursor.fetchall():
            filename = os.path.join(folder, 'floodplain_{:d}.txt'.format(fpl[0]))
            with open(filename, 'w') as fpl_file:
                fpl_file.write('########################################################################\n')
                fpl_file.write('# This file was automatically generated by ProMaIDes Database '
                                   'Export-QGIS-Plugin Version {version_1} \n'.format(version_1=VERSION))
                # date and time output
                now = datetime.now()
                dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
                fpl_file.write('# Generated at {dt_string_1} '.format(dt_string_1=dt_string))
                fpl_file.write('from project {filename_1} from database {db} \n'.format(filename_1=proj_name,
                                                                                       db = db_name))
                fpl_file.write('# Comments are marked with #\n')

                fpl_file.write("""#           
# The following metadata can be copied to a Promaides .ilm
# file to use this raster with Promaides.
#!GENERAL = <SET>
#    $NX = {}
#    $NY = {}
#    $LOWLEFTX = {}
#    $LOWLEFTY = {}
#    $ELEMWIDTH_X = {}
#    $ELEMWIdTH_Y = {}
#    $NOINFOVALUE = {}
#    $ANGLE = {}
#<SET>
                
!BEGIN\n""".format(fpl[1], fpl[2], fpl[3], fpl[4], fpl[5], fpl[6], fpl[7], fpl[8]))
                fpl_file.write('########################################################################\n')

                cursor.execute("""
                    SELECT "elem_glob_id", "elem_id", "geodetic_height", "material_id", "init_condition"
                    FROM {}.hyd_floodplain_element_prm
                    WHERE floodplain_id = {}
                    ORDER BY "elem_id" ASC
                """.format(proj_name, fpl[0]))
                elements = cursor.fetchall()

                cursor.execute("""
                    SELECT "elem_glob_id", "boundary_type", "stationary", "value"
                    FROM {}.hyd_floodplain_elem_bound_cond_prm
                """.format(proj_name))
                bcs = cursor.fetchall()

                bcs = {bc[0]: bc[1:] for bc in bcs}
                for element in elements:
                    element_id = element[0]
                    if element_id in bcs:
                        bc_type, bc_stat, bc_val = bcs[element_id]
                        bc_val = int(bc_val) if not bc_stat else bc_val
                        bc_stat = str(bc_stat).lower()
                        fpl_file.write('\t'.join(map(str, element[1:])) + '\ttrue\t{}\t{}\t{}\n'.format(bc_stat, bc_val, bc_type))
                    else:
                        fpl_file.write('\t'.join(map(str, element[1:])) + '\tfalse\ttrue\t0.0\n')
                fpl_file.write('!END\n')

# Export material id data
    @staticmethod
    def export_materials(proj_name, cursor, folder, db_name):

        cursor.execute("""
            SELECT "material_id", "value", "type"
            FROM {}.hyd_material_param_prm
            ORDER BY "material_id" ASC
        """.format(proj_name))

        num_mats = cursor.rowcount

        filename = os.path.join(folder, 'materials.txt')
        with open(filename, 'w') as mat_file:
            mat_file.write('########################################################################\n')
            mat_file.write('# This file was automatically generated by ProMaIDes Database '
                           'Export-QGIS-Plugin Version {version_1} \n'.format(version_1=VERSION))
            # date and time output
            now = datetime.now()
            dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
            mat_file.write('# Generated at {dt_string_1} '.format(dt_string_1=dt_string))
            mat_file.write('from project {filename_1} from database {db} \n'.format(filename_1=proj_name,
                                                                                       db = db_name))
            mat_file.write('# Comments are marked with #\n')
            mat_file.write('########################################################################\n')

            mat_file.write('{}\n'.format(num_mats))
            for record in cursor:
                mat_file.write('\t'.join(map(str, record)) + '\n')
Example #14
0
class Plugin(QGISPluginBase):
    """docstring for Plugin"""
    def __init__(self, iface):
        super(Plugin, self).__init__()
        self.iface = iface

        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.i18nPath,
                                   'plugin_{}.qm'.format(locale))
        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

    def tr(self, message):
        return QApplication.translate('Plugin', message)

    def initGui(self):
        self.paste_geometry_action = QAction(self.tr('Paste Geometry'),
                                             self.iface.mainWindow())
        self.paste_geometry_action.setIcon(
            QIcon(os.path.join(self.dir, 'icon.svg')))
        self.paste_geometry_action.setShortcut(QKeySequence('Ctrl+Shift+G'))
        self.paste_geometry_action.setToolTip(self.tr('Paste Geometry'))
        self.paste_geometry_action.setStatusTip(self.description)
        self.paste_geometry_action.setEnabled(False)
        self.paste_geometry_action.triggered.connect(self.pasteGeometry)

        self.iface.editMenu().insertAction(
            self.iface.actionDeleteSelected(),
            self.paste_geometry_action,
        )
        self.iface.digitizeToolBar().insertAction(
            self.iface.actionDeleteSelected(),
            self.paste_geometry_action,
        )

        self.iface.currentLayerChanged.connect(self._changeCurrentLayerHandle)
        self._changeCurrentLayerHandle(self.iface.activeLayer())

    def unload(self):
        self.iface.editMenu().removeAction(self.paste_geometry_action)
        self.iface.digitizeToolBar().removeAction(self.paste_geometry_action)
        self.iface.currentLayerChanged.disconnect(
            self._changeCurrentLayerHandle)

    def pushMessage(self, title, message, level=QGis23MessageBarLevel.Info):
        self.iface.messageBar().pushMessage(title, message, level)

    def pushLog(self, msg, level=QGis23MessageLogLevel.Info):
        QgsMessageLog.logMessage(msg, self.name, level)

    def pasteGeometry(self):
        geoms = self._tryGetFeaturesGeomsFromClipBoard()
        if len(geoms) > 1:
            self.pushMessage(
                self.tr('Paste geometry'),
                self.tr('Fail to paste. Multiple features in the clipboard.'),
                QGis23MessageBarLevel.Critical)
            return
        if len(geoms) == 0:
            self.pushMessage(
                self.tr('Paste geometry'),
                self.tr('Nothing to paste. No features in the clipboard.'),
                QGis23MessageBarLevel.Critical)
            return

        geom = geoms[0]

        layer = self.iface.activeLayer()
        selected_features = layer.selectedFeatures()

        if len(selected_features) > 1:
            self.pushMessage(
                self.tr('Paste geometry'),
                # 'Multiple features selected. Need only one.',
                self.tr(
                    'Multiple features are selected. There should be only one.'
                ),
                QGis23MessageBarLevel.Critical)
            return

        if len(selected_features) == 0:
            self.pushMessage(
                self.tr('Paste geometry'),
                self.tr('Nowhere to paste. No target feature selected.'),
                QGis23MessageBarLevel.Critical)
            return

        feature = selected_features[0]

        if feature.geometry().type() != geom.type():
            self.pushMessage(
                self.tr('Paste geometry'),
                self.tr('Incompatible geometries. Trying to paste %s to %s') %
                (getGeomtryName(
                    geom.type()), getGeomtryName(feature.geometry().type())),
                QGis23MessageBarLevel.Critical)
            return

        result = layer.changeGeometry(feature.id(), geom)

        if not result:
            self.pushMessage(
                self.tr('Paste geometry'),
                self.tr('Something is wrong. Can\'t change geometry.'),
                QGis23MessageBarLevel.Critical)
            return

        # This is hack. It is not mandatory instruction.
        # But without new features not repaint.
        # May be I made something wrong above
        self.iface.mapCanvas().refresh()

    def _tryGetFeaturesGeomsFromClipBoard(self):
        cb = QApplication.clipboard()
        clipboard_text = cb.text()
        if sys.version_info[0] == 2:
            clipboard_text = clipboard_text.encode('utf-8')

        reader = csv.DictReader(StringIO(clipboard_text), delimiter='\t')

        geoms = []
        for row in reader:
            wkt_geom = row.get('wkt_geom')
            geom = QgsGeometry.fromWkt(wkt_geom)

            if not geom:
                self.pushLog('Can\'t create geometry from wkt: %s' % wkt_geom)
                continue

            geoms.append(geom)
        return geoms

    def _changeCurrentLayerHandle(self, layer):
        if layer and isinstance(layer, QgsVectorLayer):
            layer.selectionChanged.connect(self._checkPasteAvalability)
            layer.editingStarted.connect(self._checkPasteAvalability)
            layer.editingStopped.connect(self._checkPasteAvalability)
            self._checkPasteAvalability()

    def _checkPasteAvalability(self):
        layer = self.iface.activeLayer()
        is_available = False
        if layer and isinstance(layer, QgsVectorLayer) and layer.isEditable():

            if len(layer.selectedFeatures()) == 1:
                is_available = True
            else:
                msg = self.tr("Select a target feature!")
                is_available = False
        else:
            msg = self.tr("Start editing a vector layer!")
            is_available = False

        if is_available:
            if len(self._tryGetFeaturesGeomsFromClipBoard()) == 0:
                msg = self.tr(
                    "Copy feature with the geometry you need to paste first!")
                is_available = False

        self.paste_geometry_action.setEnabled(is_available)
        if is_available:
            self.paste_geometry_action.setToolTip(self.tr('Paste Geometry'))
        else:
            self.paste_geometry_action.setToolTip(
                "%s. %s" % (self.tr('Paste Geometry'), msg))
Example #15
0
class XYZHubConnector(object):
    """base plugin"""
    def __init__(self, iface):
        """init"""
        import sys
        print(sys.version)
        self.iface = iface
        self.web_menu = "&XYZ Hub Connector"
        self.init_modules()
        self.obj = self

    def initGui(self):
        """startup"""

        parent = self.iface.mainWindow()

        ######## action, button

        icon = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/xyz.png"))
        icon_bbox = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/bbox.svg"))
        self.action_connect = QAction(icon, "XYZ Hub Connection", parent)
        self.action_connect.setWhatsThis(
            QCoreApplication.translate(PLUGIN_NAME, "WhatsThis message"))
        self.action_connect.setStatusTip(
            QCoreApplication.translate(PLUGIN_NAME, "status tip message"))

        self.action_sync_edit = QAction(
            QIcon("%s/%s" % (config.PLUGIN_DIR, "images/sync.svg")),
            "Push changes to XYZ Hub", parent)

        self.action_clear_cache = QAction(
            QIcon("%s/%s" % (config.PLUGIN_DIR, "images/delete.svg")),
            "Clear cache", parent)

        # self.action_sync_edit.setVisible(False) # disable magic sync
        self.edit_buffer.config_ui(self.enable_sync_btn)

        self.cb_layer_selected(self.iface.activeLayer())

        ######## CONNECT action, button

        self.action_connect.triggered.connect(self.open_connection_dialog)
        self.action_sync_edit.triggered.connect(self.open_sync_edit_dialog)
        self.action_clear_cache.triggered.connect(self.open_clear_cache_dialog)

        ######## Add the toolbar + button
        self.toolbar = self.iface.addToolBar(PLUGIN_NAME)
        self.toolbar.setObjectName("XYZ Hub Connector")

        self.actions_menu = [
            self.action_connect, self.action_sync_edit, self.action_clear_cache
        ]

        for a in [self.action_connect, self.action_sync_edit]:
            self.toolbar.addAction(a)

        for a in self.actions_menu:
            self.iface.addPluginToWebMenu(self.web_menu, a)

        # # uncomment to use menu button
        # tool_btn = QToolButton(self.toolbar)
        # tool_btn.setDefaultAction(self.action_connect)
        # tool_btn.setPopupMode(tool_btn.MenuButtonPopup)
        # self.xyz_widget_action = self.toolbar.addWidget(tool_btn) # uncomment to use menu button

        # self.toolbar.addAction(self.action_connect)

        self.action_help = None

        progress = QProgressBar()
        progress.setMinimum(0)
        progress.setMaximum(0)
        progress.reset()
        progress.hide()
        # progress = self.iface.statusBarIface().children()[2] # will be hidden by qgis
        self.iface.statusBarIface().addPermanentWidget(progress)
        self.pb = progress

    def init_modules(self):
        if LOG_TO_FILE:
            QgsApplication.messageLog().messageReceived.connect(
                cb_log_qgis)  #, Qt.QueuedConnection

        # util.init_module()

        # parent = self.iface.mainWindow()
        parent = QgsProject.instance()

        self.secret = Secret(config.USER_PLUGIN_DIR + "/secret.ini")
        ######## Init xyz modules
        self.map_basemap_meta = basemap.load_default_xml()
        self.auth_manager = AuthManager(config.USER_PLUGIN_DIR + "/auth.ini")

        self.token_model = GroupTokenModel(parent)

        self.network = NetManager(parent)

        self.con_man = LoaderManager()
        self.con_man.config(self.network)
        self.edit_buffer = EditBuffer()
        ######## data flow
        # self.conn_info = SpaceConnectionInfo()

        ######## token
        self.token_model.load_ini(config.USER_PLUGIN_DIR + "/token.ini")

        ######## CALLBACK

        self.con_man.ld_pool.signal.progress.connect(
            self.cb_progress_busy)  #, Qt.QueuedConnection
        self.con_man.ld_pool.signal.finished.connect(self.cb_progress_done)

        QgsProject.instance().layersWillBeRemoved["QStringList"].connect(
            self.edit_buffer.remove_layers)
        # QgsProject.instance().layersWillBeRemoved["QStringList"].connect( self.layer_man.remove_layers)

        # QgsProject.instance().layersAdded.connect( self.edit_buffer.config_connection)

        self.iface.currentLayerChanged.connect(
            self.cb_layer_selected)  # UNCOMMENT

        self.iface.mapCanvas().extentsChanged.connect(self.reload_tile,
                                                      Qt.QueuedConnection)

        QgsProject.instance().readProject.connect(self.import_project)
        self.import_project()

    def unload_modules(self):
        # self.con_man.disconnect_ux( self.iface)
        QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect(
            self.edit_buffer.remove_layers)
        # QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect( self.layer_man.remove_layers)

        # QgsProject.instance().layersAdded.disconnect( self.edit_buffer.config_connection)
        self.edit_buffer.unload_connection()

        self.iface.currentLayerChanged.disconnect(
            self.cb_layer_selected)  # UNCOMMENT

        self.iface.mapCanvas().extentsChanged.disconnect(self.reload_tile)

        # utils.disconnect_silent(self.iface.currentLayerChanged)

        self.secret.deactivate()

        if LOG_TO_FILE:
            QgsApplication.messageLog().messageReceived.disconnect(cb_log_qgis)
        # close_file_logger()
        pass

    def unload(self):
        """teardown"""
        self.unload_modules()
        # remove the plugin menu item and icon
        self.iface.removePluginWebMenu(self.web_menu, self.action_help)

        self.toolbar.clear(
        )  # remove action from custom toolbar (toolbar still exist)
        self.toolbar.deleteLater()

        for a in self.actions_menu:
            self.iface.removePluginWebMenu(self.web_menu, a)
        # remove progress
        self.iface.statusBarIface().removeWidget(self.pb)

    ###############
    # Callback
    ###############
    def cb_layer_selected(self, qlayer):
        flag_xyz = True if qlayer is not None and is_xyz_supported_layer(
            qlayer) else False
        if flag_xyz:
            self.edit_buffer.config_connection([qlayer])
            self.edit_buffer.enable_ui(qlayer.id())
        else:
            msg = "No XYZHub Layer selected"
            self.enable_sync_btn(False, msg)
        # disable magic sync
        # self.action_sync_edit.setEnabled(flag_xyz)
    def enable_sync_btn(self, flag, msg=""):
        msg = msg or ("No changes detected since last push"
                      if not flag else "Push changes")
        self.action_sync_edit.setToolTip(msg)
        self.action_sync_edit.setEnabled(flag)

    ###############
    # Callback of action (main function)
    ###############
    def cb_success_msg(self, title, msg="", dt=5):
        self.iface.messageBar().pushMessage(config.TAG_PLUGIN,
                                            ": ".join([title,
                                                       msg]), Qgis.Success, dt)

    def make_cb_success(self, title, msg="", dt=5):
        def _cb_success_msg():
            self.cb_success_msg(title, msg, dt=dt)

        return _cb_success_msg

    def make_cb_success_args(self, title, msg="", dt=5):
        def _cb_success_msg(args):
            a, kw = parse_qt_args(args)
            txt = ". ".join(map(str, a))
            self.cb_success_msg(title, txt, dt=dt)

        return _cb_success_msg

    def cb_handle_error_msg(self, e):
        err = parse_exception_obj(e)
        if isinstance(err, ChainInterrupt):
            e0, idx = err.args[0:2]
        else:
            e0 = err
        if isinstance(e0,
                      (net_handler.NetworkError, net_handler.NetworkTimeout)):
            ok = self.show_net_err(e0)
            if ok: return
        elif isinstance(e0, EmptyXYZSpaceError):
            ret = exec_warning_dialog("XYZ Hub",
                                      "Requested query returns no features")
            return
        self.show_err_msgbar(err)

    def show_net_err(self, err):
        reply_tag, status, reason, body, err_str, url = err.args[:6]
        if reply_tag in ["count", "statistics"]:  # too many error
            # msg = "Network Error: %s: %s. %s"%(status, reason, err_str)
            return 1

        detail = "\n".join(["Request:", url, "", "Response:", body])
        msg = ("%s: %s\n" % (status, reason) +
               "There was a problem connecting to the server")
        if status in [401, 403]:
            msg += (
                "\n\n" + "Please input valid token with correct permissions." +
                "\n" + "Token is generated via " +
                "<a href='https://xyz.api.here.com/token-ui/'>https://xyz.api.here.com/token-ui/</a>"
            )
        ret = exec_warning_dialog("Network Error", msg, detail)
        return 1

    def show_err_msgbar(self, err):
        self.iface.messageBar().pushMessage("Error", repr(err), Qgis.Warning,
                                            3)
        msg = format_traceback(err)
        QgsMessageLog.logMessage(msg, config.TAG_PLUGIN, Qgis.Warning)

    def cb_progress_busy(self, n_active):
        if n_active > 1: return
        self.flag_pb_show = True
        self.cb_progress_refresh()

    def cb_progress_done(self):
        self.flag_pb_show = False
        self.cb_progress_refresh()

    def cb_progress_refresh(self):
        if not hasattr(self, "flag_pb_show"): return

        pb = self.pb
        if self.flag_pb_show:
            pb.show()
            # print_qgis("show",pb)
        else:
            pb.hide()
            # print_qgis("hide")

    ###############
    # Action (main function)
    ###############

    # UNUSED
    def refresh_canvas(self):
        # self.iface.activeLayer().triggerRepaint()
        self.iface.mapCanvas().refresh()

    def previous_canvas_extent(self):
        self.iface.mapCanvas().zoomToPreviousExtent()

    #

    def new_main_dialog(self):
        parent = self.iface.mainWindow()
        dialog = MainDialog(parent)

        dialog.config(self.token_model)
        dialog.config_secret(self.secret)
        auth = self.auth_manager.get_auth()
        dialog.config_basemap(self.map_basemap_meta, auth)

        con = self.con_man.make_con("create")
        con.signal.finished.connect(
            dialog.btn_use.clicked.emit)  # can be optimized !!
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("list")
        con.signal.results.connect(make_fun_args(dialog.cb_display_spaces))
        con.signal.error.connect(self.cb_handle_error_msg)
        con.signal.error.connect(lambda e: dialog.cb_enable_token_ui())
        con.signal.finished.connect(dialog.cb_enable_token_ui)

        con = self.con_man.make_con("edit")
        con.signal.finished.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("delete")
        con.signal.results.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("stat")
        con.signal.results.connect(make_fun_args(
            dialog.cb_display_space_count))
        con.signal.error.connect(self.cb_handle_error_msg)

        ############ clear cache btn
        dialog.signal_clear_cache.connect(self.open_clear_cache_dialog)

        ############ add map tile btn
        dialog.signal_add_basemap.connect(self.add_basemap_layer)

        ############ btn: new, edit, delete space
        dialog.signal_new_space.connect(self.start_new_space)
        dialog.signal_edit_space.connect(self.start_edit_space)
        dialog.signal_del_space.connect(self.start_delete_space)

        ############ Use Token btn
        dialog.signal_use_token.connect(lambda a: self.con_man.finish_fast())
        dialog.signal_use_token.connect(self.start_use_token)

        ############ get count
        dialog.signal_space_count.connect(
            self.start_count_feat,
            Qt.QueuedConnection)  # queued -> non-blocking ui

        ############ connect btn
        dialog.signal_space_connect.connect(self.start_load_layer)
        dialog.signal_space_tile.connect(self.start_load_tile)

        ############ upload btn
        dialog.signal_upload_space.connect(self.start_upload_space)

        return dialog

    def start_new_space(self, args):
        con = self.con_man.get_con("create")
        con.start_args(args)

    def start_edit_space(self, args):
        con = self.con_man.get_con("edit")
        con.start_args(args)

    def start_delete_space(self, args):
        con = self.con_man.get_con("delete")
        con.start_args(args)

    def start_use_token(self, args):
        con = self.con_man.get_con("list")
        con.start_args(args)

    def start_count_feat(self, args):
        con = self.con_man.get_con("stat")
        con.start_args(args)

    def start_upload_space(self, args):
        con_upload = UploadLayerController(self.network, n_parallel=2)
        self.con_man.add_background(con_upload)
        # con_upload.signal.finished.connect( self.make_cb_success("Uploading finish") )
        con_upload.signal.results.connect(
            self.make_cb_success_args("Uploading finish"))
        con_upload.signal.error.connect(self.cb_handle_error_msg)

        con = InitUploadLayerController(self.network)
        self.con_man.add_background(con)

        con.signal.results.connect(con_upload.start_args)
        con.signal.error.connect(self.cb_handle_error_msg)

        con.start_args(args)

    def start_load_layer(self, args):
        # create new con
        # config
        # run

        ############ connect btn
        con_load = LoadLayerController(self.network, n_parallel=1)
        self.con_man.add_background(con_load)
        # con_load.signal.finished.connect( self.make_cb_success("Loading finish") )
        con_load.signal.results.connect(
            self.make_cb_success_args("Loading finish"))
        # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_load.signal.error.connect(self.cb_handle_error_msg)

        con_load.start_args(args)

        # con.signal.results.connect( self.layer_man.add_args) # IMPORTANT
    def start_load_tile(self, args):
        canvas = self.iface.mapCanvas()
        rect = bbox_utils.extend_to_rect(bbox_utils.get_bounding_box(canvas))
        level = tile_utils.get_zoom_for_current_map_scale(canvas)
        # rect = (-180,-90,180,90)
        # level = 0
        a, kw = parse_qt_args(args)

        kw["tile_schema"] = "here"
        kw["tile_ids"] = tile_utils.bboxToListColRow(*rect, level)
        # kw["limit"] = 100

        ############ connect btn
        con_load = TileLayerLoader(self.network, n_parallel=1)
        self.con_man.add_layer(con_load)
        # con_load.signal.finished.connect( self.make_cb_success("Tiles loaded") )
        con_load.signal.results.connect(
            self.make_cb_success_args("Tiles loaded", dt=2))
        # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_load.signal.error.connect(self.cb_handle_error_msg)

        con_load.start_args(make_qt_args(*a, **kw))

    def reload_tile(self):
        canvas = self.iface.mapCanvas()
        rect = bbox_utils.extend_to_rect(bbox_utils.get_bounding_box(canvas))
        level = tile_utils.get_zoom_for_current_map_scale(canvas)
        kw = dict()
        kw["tile_schema"] = "here"
        kw["tile_ids"] = tile_utils.bboxToListColRow(*rect, level)
        # kw["limit"] = 100

        unique_con = set()
        lst_con = list()
        for qnode in [
                vl
                for vl in QgsProject.instance().layerTreeRoot().checkedLayers(
                ) if is_xyz_supported_layer(vl)
        ] + [
                g for g in QgsProject.instance().layerTreeRoot().findGroups()
                if len(g.children()) == 0 and g.isVisible()
                and is_xyz_supported_node(g)
        ]:
            xlayer_id = qnode.customProperty("xyz-hub-id")
            con = self.con_man.get_from_xyz_layer(xlayer_id)
            if con is None: continue
            if con in unique_con: continue
            lst_con.append(con)
            unique_con.add(con)
        # print_qgis(lst_con)
        # print_qgis(self.con_man._layer_ptr)

        for con in lst_con:
            print_qgis(con.status)
            print_qgis("loading tile", level, rect)
            con.restart(**kw)

    def add_basemap_layer(self, args):
        a, kw = parse_qt_args(args)
        meta, app_id, app_code = a
        self.auth_manager.save(app_id, app_code)
        basemap.add_basemap_layer(meta, app_id, app_code)

    ###############
    # import project function
    ###############

    def import_project(self):
        self.init_tile_loader()

    def init_tile_loader(self):
        cnt = 0
        for qnode in [
                g for g in QgsProject.instance().layerTreeRoot().findGroups()
                if is_xyz_supported_node(g)
        ]:
            try:
                layer = XYZLayer.load_from_qnode(qnode)
                con_load = TileLayerLoader(self.network,
                                           n_parallel=1,
                                           layer=layer)
                ptr = self.con_man.add_layer(con_load)
                # con_load.signal.finished.connect( self.make_cb_success("Tiles loaded") )
                con_load.signal.results.connect(
                    self.make_cb_success_args("Tiles loaded", dt=2))
                # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
                con_load.signal.error.connect(self.cb_handle_error_msg)

                cnt += 1
            except:
                pass

        # print_qgis(self.con_man._layer_ptr)
        self.cb_success_msg("Import XYZ Layer",
                            "%s XYZ Layer imported" % cnt,
                            dt=2)

    ###############
    # Open dialog
    ###############
    def open_clear_cache_dialog(self):
        dialog = ConfirmDialog(
            "Delete cache will make loaded layer unusable !!")
        ret = dialog.exec_()
        if ret != dialog.Ok: return

        utils.clear_cache()

    def open_connection_dialog(self):
        dialog = self.new_main_dialog()

        vlayer = self.iface.activeLayer()
        dialog.set_layer(vlayer)

        dialog.exec_()
        self.con_man.finish_fast()
        # self.startTime = time.time()
    def open_sync_edit_dialog(self):
        vlayer = self.iface.activeLayer()
        layer_buffer = self.edit_buffer.get_layer_buffer(vlayer.id())

        lst_added_feat, removed_ids = layer_buffer.get_sync_feat()
        conn_info = layer_buffer.get_conn_info()

        # print_qgis("lst_added_feat: ",lst_added_feat)
        # print_qgis("removed_feat: ", removed_ids)

        con = EditSyncController(self.network)
        self.con_man.add_background(con)
        con.signal.finished.connect(layer_buffer.sync_complete)
        con.signal.results.connect(
            self.make_cb_success_args("Sync edit finish"))
        con.signal.error.connect(self.cb_handle_error_msg)

        con.start(conn_info, layer_buffer, lst_added_feat, removed_ids)
Example #16
0
class Comptages(QObject):

    def __init__(self, iface):
        QObject.__init__(self)

        self.iface = iface
        self.settings = Settings()
        self.settings_dialog = SettingsDialog()
        self.layers = Layers()
        self.chart_dock = ChartDock(self.iface, self.layers)
        self.iface.addDockWidget(Qt.BottomDockWidgetArea, self.chart_dock)
        self.filter_start_date = None
        self.filter_end_date = None
        self.filter_installation = None
        self.filter_sensor = None
        self.tm = QgsApplication.taskManager()

    def initGui(self):
        self.connect_db_action = QAction(
            QIcon(':/plugins/Comptages/images/power.png'),
            'Connection DB',
            self.iface.mainWindow()
        )

        self.create_new_action = QAction(
            QIcon(':/plugins/Comptages/images/measure.png'),
            'Créer un nouveau comptage',
            None
        )

        self.select_edit_action = QAction(
            QIcon(':/plugins/Comptages/images/select_edit.png'),
            'Modifier comptage',
            None
        )

        self.import_files_action = QAction(
            QIcon(':/plugins/Comptages/images/import.png'),
            'Importation',
            None
        )

        self.validate_imported_files = QAction(
            QIcon(':/plugins/Comptages/images/validate.png'),
            'Validation',
            None
        )

        self.filter_action = QAction(
            QIcon(':/plugins/Comptages/images/filter.png'),
            'Filtrer',
            None
        )

        self.import_ics_action = QAction(
            QIcon(':/plugins/Comptages/images/calendar.png'),
            'Importer fichier ics',
            None
        )

        self.settings_action = QAction(
            QIcon(':/plugins/Comptages/images/settings.png'),
            'Réglages',
            None
        )

        self.connect_db_action.triggered.connect(
            self.do_connect_db_action)

        self.create_new_action.triggered.connect(
            self.do_create_new_action)

        self.select_edit_action.triggered.connect(
            self.do_select_edit_action)

        self.import_files_action.triggered.connect(
            self.do_import_files_action)

        self.validate_imported_files.triggered.connect(
            self.do_validate_imported_files_action)

        self.filter_action.triggered.connect(
            self.do_filter_action)

        self.import_ics_action.triggered.connect(
            self.do_import_ics_action)

        self.settings_action.triggered.connect(
            self.do_settings_action)

        self.create_new_action.setEnabled(False)
        self.select_edit_action.setEnabled(False)
        self.import_files_action.setEnabled(False)
        self.validate_imported_files.setEnabled(False)
        self.filter_action.setEnabled(False)
        self.import_ics_action.setEnabled(False)

        self.iface.addPluginToMenu('Comptages', self.connect_db_action)
        self.iface.addPluginToMenu('Comptages', self.create_new_action)
        self.iface.addPluginToMenu('Comptages', self.select_edit_action)
        self.iface.addPluginToMenu('Comptages', self.import_files_action)
        self.iface.addPluginToMenu('Comptages', self.validate_imported_files)
        self.iface.addPluginToMenu('Comptages', self.filter_action)
        self.iface.addPluginToMenu('Comptages', self.import_ics_action)
        self.iface.addPluginToMenu('Comptages', self.settings_action)

        self.toolbar = self.iface.addToolBar('Comptages')
        self.toolbar.setObjectName('Comptages')
        self.toolbar.setToolTip('Comptages toolbar')

        self.toolbar.addAction(self.connect_db_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.create_new_action)
        self.toolbar.addAction(self.select_edit_action)
        self.toolbar.addAction(self.import_files_action)
        self.toolbar.addAction(self.validate_imported_files)
        self.toolbar.addAction(self.filter_action)
        self.toolbar.addAction(self.import_ics_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.settings_action)

    def unload(self):
        self.iface.removePluginMenu('Comptages', self.connect_db_action)
        self.iface.removePluginMenu('Comptages', self.create_new_action)
        self.iface.removePluginMenu('Comptages', self.select_edit_action)
        self.iface.removePluginMenu('Comptages', self.filter_action)
        self.iface.removePluginMenu('Comptages', self.import_ics_action)
        self.iface.removePluginMenu('Comptages', self.settings_action)

        del self.connect_db_action
        del self.create_new_action
        del self.select_edit_action
        del self.filter_action
        del self.import_ics_action
        del self.settings_action

        del self.toolbar

    def do_connect_db_action(self):
        self.layers.load_layers()
        self.enable_actions_if_needed()

    def do_create_new_action(self):
        if self.tm.countActiveTasks() > 0:
            push_info(("Veuillez patienter jusqu'à ce que l'importation "
                       "soit terminée."))
            return
        self.layers.create_count()

    def do_select_edit_action(self):
        if self.tm.countActiveTasks() > 0:
            push_info(("Veuillez patienter jusqu'à ce que l'importation "
                       "soit terminée."))
            return
        self.layers.edit_count()

    def do_import_files_action(self):
        if self.tm.countActiveTasks() > 0:
            push_info(("Veuillez patienter jusqu'à ce que l'importation "
                       "soit terminée."))
            return
        file_dialog = QFileDialog()
        title = 'Importer'
        path = self.settings.value('data_import_directory')
        files = QFileDialog.getOpenFileNames(
            file_dialog, title, path,
            "Data file (*.A?? *.aV? *.I?? *.V?? *.txt)")[0]

        for file_path in files:
            self.import_file(file_path)

    def import_file(self, file_path, count_id=None):

        # Manage binary files
        with open(file_path, 'rb') as fd:
            file_head = fd.read(24)

        if file_head == b'Golden River Traffic Ltd':  # is a binary file
            formatter = self.layers.get_formatter_name('GoldenRiver')
            file_path_formatted = "{}_for".format(file_path)
            os.system("{} {} {}".format(
                formatter, file_path, file_path_formatted))
            file_path = file_path_formatted

        file_header = DataImporter.parse_file_header(file_path)

        if not count_id:
            count_id = self.layers.guess_count_id(
                file_header['SITE'],
                datetime.strptime(file_header['STARTREC'], "%H:%M %d/%m/%y"),
                datetime.strptime(file_header['STOPREC'], "%H:%M %d/%m/%y"))

        if not count_id:
            QgsMessageLog.logMessage(
                """Impossible de trouver le comptage associé {}:
                section: {} start: {} end: {}""".format(
                    file_path,
                    file_header['SITE'],
                    datetime.strptime(file_header['STARTREC'], "%H:%M %d/%m/%y"),
                    datetime.strptime(file_header['STOPREC'], "%H:%M %d/%m/%y")
                ),
                'Comptages', Qgis.Critical)
            return

        QgsMessageLog.logMessage(
            'Importation {}'.format(os.path.basename(file_path)),
            'Comptages', Qgis.Info)

        file_format = file_header['FORMAT']

        if file_format == 'VBV-1':
            task = DataImporterVbv1(file_path, count_id)
        elif file_format == 'INT-2':
            task = DataImporterInt2(file_path, count_id)
        elif file_format == 'MC':
            task = DataImporterMC(file_path, count_id)
        else:
            push_info('Format {} of {} not supported'.format(
                file_format,
                os.path.basename(file_path)))
            return

        self.tm.allTasksFinished.connect(self.task_finished)
        self.tm.addTask(task)
        return task

    def task_finished(self):
        self.tm.allTasksFinished.disconnect(self.task_finished)
        push_info(('Toutes les tâches sont terminées. Consultez le journal '
                   'pour plus de détails.'))
        self.chart_dock.show_next_quarantined_chart()

    def do_validate_imported_files_action(self):
        if self.tm.countActiveTasks() > 0:
            push_info(("Veuillez patienter jusqu'à ce que l'importation "
                       "soit terminée."))
            return
        self.chart_dock.show_next_quarantined_chart()

    def do_filter_action(self):
        dlg = FilterDialog(self.iface)

        # Set last values in the filter
        if self.filter_start_date:
            dlg.start_date.setDateTime(self.filter_start_date)
        else:
            dlg.start_date.setDateTime(QDateTime())
        if self.filter_end_date:
            dlg.end_date.setDateTime(self.filter_end_date)
        else:
            dlg.end_date.setDateTime(QDateTime())
        if self.filter_installation:
            dlg.installation.setCurrentIndex(self.filter_installation)
        if self.filter_sensor:
            dlg.sensor.setCurrentIndex(self.filter_sensor)

        if dlg.exec_():
            self.filter_start_date = dlg.start_date.dateTime()
            self.filter_end_date = dlg.end_date.dateTime()
            self.filter_installation = dlg.installation.currentIndex()
            self.filter_sensor = dlg.sensor.currentIndex()

            self.layers.apply_filter(
                dlg.start_date.dateTime().toString('yyyy-MM-dd'),
                dlg.end_date.dateTime().toString('yyyy-MM-dd'),
                dlg.installation.currentIndex(),
                dlg.sensor.currentIndex())

    def do_import_ics_action(self):
        IcsImporter(self.layers)

    def do_settings_action(self):
        self.settings_dialog.exec_()

    def do_export_configuration_action(self, count_id):
        config_creator = ConfigCreatorCmd(self.layers, count_id)
        config_creator.set_section_commands()

        installation_name = self.layers.get_installation_name_of_count(
            count_id)

        file_dialog = QFileDialog()
        file_dialog.setDefaultSuffix('*.CMD')
        title = 'Exporter la configuration'
        path = os.path.join(
            self.settings.value('config_export_directory'),
            "{}.CMD".format(installation_name))
        file = QFileDialog.getSaveFileName(
            file_dialog, title, path, "Config file (*.CMD)")[0]

        if not file:
            return

        config_creator.write_file(file)
        push_info('Written config file {}'.format(file))

    def do_import_single_file_action(self, count_id):
        file_dialog = QFileDialog()
        title = 'Importation'
        path = self.settings.value('data_import_directory')
        file_path = QFileDialog.getOpenFileName(
            file_dialog, title, path,
            "Data file (*.A?? *.aV? *.I?? *.V?? *.txt)")[0]

        if not file_path:
            return

        self.import_file(file_path, count_id)

    def do_generate_report_action(self, count_id):
        if self.tm.countActiveTasks() > 0:
            push_info(("Veuillez patienter jusqu'à ce que l'importation "
                       "soit terminée."))
            return

        # Show message if there are no data to process
        contains_data = self.layers.count_contains_data(count_id)
        if not contains_data :
            push_info("Installation {}: Il n'y a pas de données à traiter pour "
                "le comptage {}".format(
                self.layers.get_installation_name_of_count(count_id),count_id))
            return

        file_dialog = QFileDialog()
        title = 'Exporter un rapport'
        path = self.settings.value('report_export_directory')
        file_path = QFileDialog.getExistingDirectory(
            file_dialog, title, path)
        print(file_path)

        if not file_path:
            return

        report_creator = ReportCreator(count_id, file_path, self.layers)
        report_creator.run()
        push_info("Installation {} (count={}): Génération du rapport terminée."
         .format(self.layers.get_installation_name_of_count(count_id),count_id))

    def do_export_plan_action(self, count_id):
        plan_creator = PlanCreator(self.layers)
        file_dialog = QFileDialog()
        file_dialog.setDefaultSuffix('*.PDF')
        title = 'Exporter plan de pose'
        path = os.path.join(
            self.settings.value('config_export_directory'),
            "{}.pdf".format("plan_de_pose"))
        file = QFileDialog.getSaveFileName(
            file_dialog, title, path, "Config file (*.PDF)")[0]

        if not file:
            return

        # Highlight the current sections and installation in the layout
        previous_highlightes_sections = self.layers.highlighted_sections
        self.layers.highlighted_sections = \
            self.layers.get_section_ids_of_count(count_id)
        QgsExpressionContextUtils.setProjectVariable(
            QgsProject.instance(), 'highlighted_installation',
            self.layers.get_installation_name_of_count(count_id))

        plan_creator.export_pdf(count_id, file)

        self.layers.highlighted_sections = previous_highlightes_sections
        QgsExpressionContextUtils.setProjectVariable(
            QgsProject.instance(), 'highlighted_installation',
            '')
        self.layers.layers['section'].triggerRepaint()

    def do_generate_chart_action(self, count_id):
        if self.tm.countActiveTasks() > 0:
            push_info(("Veuillez patienter jusqu'à ce que l'importation "
                       "soit terminée."))
            return
        self.chart_dock.set_attributes(count_id)

    def enable_actions_if_needed(self):
        """Enable actions if the plugin is connected to the db
        otherwise disable them"""
        self.create_new_action.setEnabled(True)
        self.select_edit_action.setEnabled(True)
        self.import_files_action.setEnabled(True)
        self.validate_imported_files.setEnabled(True)
        self.import_ics_action.setEnabled(True)
        self.filter_action.setEnabled(True)

    def is_section_highlighted(self, section_id):
        return self.layers.is_section_highlighted(section_id)

    @qgsfunction(args="auto", group="Comptages")
    def is_highlighted(feature, parent):
        """Used by section layer to apply a style to the sections related to a
        count"""

        # Call the method of the current instance of the plugin
        return plugins['comptages'].is_section_highlighted(
            feature.attribute('id'))

    @qgsfunction(args="auto", group="Comptages")
    def check_dates(feature, parent):
        """Used by count layer to show if a count was during a special
        period"""

        return plugins['comptages'].layers.check_dates(
            feature.attribute('start_process_date'),
            feature.attribute('end_process_date')
        )
class LatLonTools:
    digitizerDialog = None
    convertCoordinateDialog = None
    mapTool = None
    showMapTool = None
    copyExtentTool = None

    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.crossRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry)
        self.crossRb.setColor(Qt.red)
        self.provider = LatLonToolsProvider()
        self.toolbar = self.iface.addToolBar('Lat Lon Tools Toolbar')
        self.toolbar.setObjectName('LatLonToolsToolbar')

    def initGui(self):
        '''Initialize Lot Lon Tools GUI.'''
        # Initialize the Settings Dialog box
        self.settingsDialog = SettingsWidget(self, self.iface,
                                             self.iface.mainWindow())

        # Add Interface for Coordinate Capturing
        icon = QIcon(os.path.dirname(__file__) + "/images/copyicon.svg")
        self.copyAction = QAction(icon, "Copy/Display Coordinate",
                                  self.iface.mainWindow())
        self.copyAction.setObjectName('latLonToolsCopy')
        self.copyAction.triggered.connect(self.startCapture)
        self.copyAction.setCheckable(True)
        self.toolbar.addAction(self.copyAction)
        self.iface.addPluginToMenu("Lat Lon Tools", self.copyAction)

        # Add Interface for External Map
        icon = QIcon(os.path.dirname(__file__) + "/images/mapicon.png")
        self.externMapAction = QAction(icon, "Show in External Map",
                                       self.iface.mainWindow())
        self.externMapAction.setObjectName('latLonToolsExternalMap')
        self.externMapAction.triggered.connect(self.setShowMapTool)
        self.externMapAction.setCheckable(True)
        self.toolbar.addAction(self.externMapAction)
        self.iface.addPluginToMenu("Lat Lon Tools", self.externMapAction)

        # Add Interface for Zoom to Coordinate
        icon = QIcon(os.path.dirname(__file__) + "/images/zoomicon.svg")
        self.zoomToAction = QAction(icon, "Zoom To Coordinate",
                                    self.iface.mainWindow())
        self.zoomToAction.setObjectName('latLonToolsZoom')
        self.zoomToAction.triggered.connect(self.showZoomToDialog)
        self.toolbar.addAction(self.zoomToAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.zoomToAction)

        self.zoomToDialog = ZoomToLatLon(self, self.iface,
                                         self.iface.mainWindow())
        self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.zoomToDialog)
        self.zoomToDialog.hide()

        # Add Interface for Multi point zoom
        icon = QIcon(os.path.dirname(__file__) + '/images/multizoom.svg')
        self.multiZoomToAction = QAction(icon, "Multi-location Zoom",
                                         self.iface.mainWindow())
        self.multiZoomToAction.setObjectName('latLonToolsMultiZoom')
        self.multiZoomToAction.triggered.connect(self.multiZoomTo)
        self.toolbar.addAction(self.multiZoomToAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.multiZoomToAction)

        self.multiZoomDialog = MultiZoomWidget(self, self.settingsDialog,
                                               self.iface.mainWindow())
        self.multiZoomDialog.hide()
        self.multiZoomDialog.setFloating(True)

        menu = QMenu()
        # Add Interface for copying the canvas extent
        icon = QIcon(os.path.dirname(__file__) + "/images/copycanvas.svg")
        self.copyCanvasAction = menu.addAction(icon, 'Copy Canvas Extent',
                                               self.copyCanvas)
        self.copyCanvasAction.setObjectName('latLonToolsCopyCanvasExtent')
        # Add Interface for copying an interactive extent
        icon = QIcon(os.path.dirname(__file__) + "/images/copyextent.svg")
        self.copyExtentAction = menu.addAction(icon,
                                               'Copy Selected Area Extent',
                                               self.copyExtent)
        self.copyExtentAction.setCheckable(True)
        self.copyExtentAction.setObjectName(
            'latLonToolsCopySelectedAreaExtent')
        # Add Interface for copying a layer extent
        icon = QIcon(os.path.dirname(__file__) + "/images/copylayerextent.svg")
        self.copyLayerExtentAction = menu.addAction(icon, 'Copy Layer Extent',
                                                    self.copyLayerExtent)
        self.copyLayerExtentAction.setObjectName('latLonToolsCopyLayerExtent')
        # Add Interface for copying the extent of selected features
        icon = QIcon(
            os.path.dirname(__file__) + "/images/copyselectedlayerextent.svg")
        self.copySelectedFeaturesExtentAction = menu.addAction(
            icon, 'Copy Selected Features Extent',
            self.copySelectedFeaturesExtent)
        self.copySelectedFeaturesExtentAction.setObjectName(
            'latLonToolsCopySelectedFeaturesExtent')

        # Add the copy extent tools to the menu
        icon = QIcon(os.path.dirname(__file__) + '/images/copylayerextent.svg')
        self.copyExtentsAction = QAction(icon, 'Copy Extents to Clipboard',
                                         self.iface.mainWindow())
        self.copyExtentsAction.setMenu(menu)
        self.iface.addPluginToMenu('Lat Lon Tools', self.copyExtentsAction)

        # Add the copy extent tools to the toolbar
        self.copyExtentButton = QToolButton()
        self.copyExtentButton.setMenu(menu)
        self.copyExtentButton.setDefaultAction(self.copyCanvasAction)
        self.copyExtentButton.setPopupMode(QToolButton.MenuButtonPopup)
        self.copyExtentButton.triggered.connect(self.copyExtentTriggered)
        self.copyExtentToolbar = self.toolbar.addWidget(self.copyExtentButton)

        # Create the coordinate converter menu
        icon = QIcon(':/images/themes/default/mIconProjectionEnabled.svg')
        self.convertCoordinatesAction = QAction(icon, "Coordinate Conversion",
                                                self.iface.mainWindow())
        self.convertCoordinatesAction.setObjectName(
            'latLonToolsCoordinateConversion')
        self.convertCoordinatesAction.triggered.connect(
            self.convertCoordinatesTool)
        self.toolbar.addAction(self.convertCoordinatesAction)
        self.iface.addPluginToMenu("Lat Lon Tools",
                                   self.convertCoordinatesAction)

        # Create the conversions menu
        menu = QMenu()
        icon = QIcon(os.path.dirname(__file__) + '/images/field2geom.svg')
        action = menu.addAction(icon, "Fields to point layer", self.field2geom)
        action.setObjectName('latLonToolsField2Geom')
        icon = QIcon(os.path.dirname(__file__) + '/images/geom2field.svg')
        action = menu.addAction(icon, "Point layer to fields", self.geom2Field)
        action.setObjectName('latLonToolsGeom2Field')
        icon = QIcon(os.path.dirname(__file__) + '/images/pluscodes.svg')
        action = menu.addAction(icon, "Plus Codes to point layer",
                                self.PlusCodestoLayer)
        action.setObjectName('latLonToolsPlusCodes2Geom')
        action = menu.addAction(icon, "Point layer to Plus Codes",
                                self.toPlusCodes)
        action.setObjectName('latLonToolsGeom2PlusCodes')
        icon = QIcon(os.path.dirname(__file__) + '/images/mgrs2point.svg')
        action = menu.addAction(icon, "MGRS to point layer", self.MGRStoLayer)
        action.setObjectName('latLonToolsMGRS2Geom')
        icon = QIcon(os.path.dirname(__file__) + '/images/point2mgrs.svg')
        action = menu.addAction(icon, "Point layer to MGRS", self.toMGRS)
        action.setObjectName('latLonToolsGeom2MGRS')
        self.conversionsAction = QAction(icon, "Conversions",
                                         self.iface.mainWindow())
        self.conversionsAction.setMenu(menu)
        self.iface.addPluginToMenu('Lat Lon Tools', self.conversionsAction)

        # Add to Digitize Toolbar
        icon = QIcon(os.path.dirname(__file__) + '/images/latLonDigitize.svg')
        self.digitizeAction = QAction(icon, "Lat Lon Digitize",
                                      self.iface.mainWindow())
        self.digitizeAction.setObjectName('latLonToolsDigitize')
        self.digitizeAction.triggered.connect(self.digitizeClicked)
        self.digitizeAction.setEnabled(False)
        self.toolbar.addAction(self.digitizeAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.digitizeAction)

        # Initialize the Settings Dialog Box
        settingsicon = QIcon(':/images/themes/default/mActionOptions.svg')
        self.settingsAction = QAction(settingsicon, "Settings",
                                      self.iface.mainWindow())
        self.settingsAction.setObjectName('latLonToolsSettings')
        self.settingsAction.setToolTip('Lat Lon Tools Settings')
        self.settingsAction.triggered.connect(self.settings)
        self.toolbar.addAction(self.settingsAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.settingsAction)

        # Help
        icon = QIcon(os.path.dirname(__file__) + '/images/help.svg')
        self.helpAction = QAction(icon, "Help", self.iface.mainWindow())
        self.helpAction.setObjectName('latLonToolsHelp')
        self.helpAction.triggered.connect(self.help)
        self.iface.addPluginToMenu('Lat Lon Tools', self.helpAction)

        self.iface.currentLayerChanged.connect(self.currentLayerChanged)
        self.canvas.mapToolSet.connect(self.resetTools)
        self.enableDigitizeTool()

        # Add the processing provider
        QgsApplication.processingRegistry().addProvider(self.provider)
        InitLatLonFunctions()

    def resetTools(self, newtool, oldtool):
        '''Uncheck the Copy Lat Lon tool'''
        try:
            if self.mapTool and (oldtool is self.mapTool):
                self.copyAction.setChecked(False)
            if self.showMapTool and (oldtool is self.showMapTool):
                self.externMapAction.setChecked(False)
            if newtool is self.mapTool:
                self.copyAction.setChecked(True)
            if newtool is self.showMapTool:
                self.externMapAction.setChecked(True)
        except Exception:
            pass

    def unload(self):
        '''Unload LatLonTools from the QGIS interface'''
        self.zoomToDialog.removeMarker()
        self.multiZoomDialog.removeMarkers()
        if self.mapTool:
            self.canvas.unsetMapTool(self.mapTool)
        if self.showMapTool:
            self.canvas.unsetMapTool(self.showMapTool)
        self.iface.removePluginMenu('Lat Lon Tools', self.copyAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.copyExtentsAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.externMapAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.zoomToAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.multiZoomToAction)
        self.iface.removePluginMenu('Lat Lon Tools',
                                    self.convertCoordinatesAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.conversionsAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.settingsAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.helpAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.digitizeAction)
        self.iface.removeDockWidget(self.zoomToDialog)
        self.iface.removeDockWidget(self.multiZoomDialog)
        # Remove Toolbar Icons
        self.iface.removeToolBarIcon(self.copyAction)
        self.iface.removeToolBarIcon(self.copyExtentToolbar)
        self.iface.removeToolBarIcon(self.zoomToAction)
        self.iface.removeToolBarIcon(self.externMapAction)
        self.iface.removeToolBarIcon(self.multiZoomToAction)
        self.iface.removeToolBarIcon(self.convertCoordinatesAction)
        self.iface.removeToolBarIcon(self.digitizeAction)
        del self.toolbar

        if self.convertCoordinateDialog:
            self.iface.removeDockWidget(self.convertCoordinateDialog)
            self.convertCoordinateDialog = None

        self.zoomToDialog = None
        self.multiZoomDialog = None
        self.settingsDialog = None
        self.showMapTool = None
        self.mapTool = None
        self.digitizerDialog = None
        QgsApplication.processingRegistry().removeProvider(self.provider)
        UnloadLatLonFunctions()

    def startCapture(self):
        '''Set the focus of the copy coordinate tool'''
        if self.mapTool is None:
            from .copyLatLonTool import CopyLatLonTool
            self.mapTool = CopyLatLonTool(self.settingsDialog, self.iface)
        self.canvas.setMapTool(self.mapTool)

    def copyExtentTriggered(self, action):
        self.copyExtentButton.setDefaultAction(action)

    def copyExtent(self):
        if self.copyExtentTool is None:
            from .captureExtent import CaptureExtentTool
            self.copyExtentTool = CaptureExtentTool(self.iface, self)
            self.copyExtentTool.setAction(self.copyExtentAction)
        self.canvas.setMapTool(self.copyExtentTool)

    def copyLayerExtent(self):
        layer = self.iface.activeLayer()
        if not layer or not layer.isValid():
            return
        if isinstance(layer, QgsVectorLayer) and (layer.featureCount() == 0):
            self.iface.messageBar().pushMessage(
                "",
                "This layer has no features - A bounding box cannot be calculated.",
                level=Qgis.Warning,
                duration=4)
            return
        src_crs = layer.crs()
        extent = layer.extent()
        if settings.bBoxCrs == 0:
            dst_crs = epsg4326
        else:
            dst_crs = self.canvas.mapSettings().destinationCrs()

        outStr = getExtentString(extent, src_crs, dst_crs)
        clipboard = QApplication.clipboard()
        clipboard.setText(outStr)
        self.iface.messageBar().pushMessage(
            "",
            "'{}' copied to the clipboard".format(outStr),
            level=Qgis.Info,
            duration=4)

    def copySelectedFeaturesExtent(self):
        layer = self.iface.activeLayer()
        if not layer or not layer.isValid():
            return
        if isinstance(layer, QgsVectorLayer) and (layer.featureCount() == 0):
            self.iface.messageBar().pushMessage(
                "",
                "This layer has no features - A bounding box cannot be calculated.",
                level=Qgis.Warning,
                duration=4)
            return
        if isinstance(layer, QgsVectorLayer):
            extent = layer.boundingBoxOfSelected()
            if extent.isNull():
                self.iface.messageBar().pushMessage(
                    "",
                    "No features were selected.",
                    level=Qgis.Warning,
                    duration=4)
                return
        else:
            extent = layer.extent()
        src_crs = layer.crs()
        if settings.bBoxCrs == 0:
            dst_crs = epsg4326
        else:
            dst_crs = self.canvas.mapSettings().destinationCrs()

        outStr = getExtentString(extent, src_crs, dst_crs)
        clipboard = QApplication.clipboard()
        clipboard.setText(outStr)
        self.iface.messageBar().pushMessage(
            "",
            "'{}' copied to the clipboard".format(outStr),
            level=Qgis.Info,
            duration=4)

    def copyCanvas(self):
        extent = self.iface.mapCanvas().extent()
        canvas_crs = self.canvas.mapSettings().destinationCrs()
        if settings.bBoxCrs == 0:
            dst_crs = epsg4326
        else:
            dst_crs = canvas_crs

        outStr = getExtentString(extent, canvas_crs, dst_crs)
        clipboard = QApplication.clipboard()
        clipboard.setText(outStr)
        self.iface.messageBar().pushMessage(
            "",
            "'{}' copied to the clipboard".format(outStr),
            level=Qgis.Info,
            duration=4)

    def setShowMapTool(self):
        '''Set the focus of the external map tool.'''
        if self.showMapTool is None:
            from .showOnMapTool import ShowOnMapTool
            self.showMapTool = ShowOnMapTool(self.iface)
        self.canvas.setMapTool(self.showMapTool)

    def showZoomToDialog(self):
        '''Show the zoom to docked widget.'''
        self.zoomToDialog.show()

    def convertCoordinatesTool(self):
        '''Display the Convert Coordinate Tool Dialog box.'''
        if self.convertCoordinateDialog is None:
            from .coordinateConverter import CoordinateConverterWidget
            self.convertCoordinateDialog = CoordinateConverterWidget(
                self, self.settingsDialog, self.iface, self.iface.mainWindow())
            self.convertCoordinateDialog.setFloating(True)
            self.iface.addDockWidget(Qt.RightDockWidgetArea,
                                     self.convertCoordinateDialog)
        self.convertCoordinateDialog.show()

    def multiZoomTo(self):
        '''Display the Multi-zoom to dialog box'''
        self.multiZoomDialog.show()

    def field2geom(self):
        '''Convert layer containing a point x & y coordinate to a new point layer'''
        processing.execAlgorithmDialog('latlontools:field2geom', {})

    def geom2Field(self):
        '''Convert layer geometry to a text string'''
        processing.execAlgorithmDialog('latlontools:geom2field', {})

    def toMGRS(self):
        '''Display the to MGRS  dialog box'''
        processing.execAlgorithmDialog('latlontools:point2mgrs', {})

    def MGRStoLayer(self):
        '''Display the to MGRS  dialog box'''
        processing.execAlgorithmDialog('latlontools:mgrs2point', {})

    def toPlusCodes(self):
        processing.execAlgorithmDialog('latlontools:point2pluscodes', {})

    def PlusCodestoLayer(self):
        processing.execAlgorithmDialog('latlontools:pluscodes2point', {})

    def settings(self):
        '''Show the settings dialog box'''
        self.settingsDialog.show()

    def help(self):
        '''Display a help page'''
        import webbrowser
        url = QUrl.fromLocalFile(os.path.dirname(__file__) +
                                 "/index.html").toString()
        webbrowser.open(url, new=2)

    def settingsChanged(self):
        # Settings may have changed so we need to make sure the zoomToDialog window is configured properly
        self.zoomToDialog.configure()
        self.multiZoomDialog.settingsChanged()

    def zoomTo(self, src_crs, lat, lon):
        canvas_crs = self.canvas.mapSettings().destinationCrs()
        transform = QgsCoordinateTransform(src_crs, canvas_crs,
                                           QgsProject.instance())
        x, y = transform.transform(float(lon), float(lat))

        rect = QgsRectangle(x, y, x, y)
        self.canvas.setExtent(rect)

        pt = QgsPointXY(x, y)
        self.highlight(pt)
        self.canvas.refresh()
        return pt

    def highlight(self, point):
        currExt = self.canvas.extent()

        leftPt = QgsPoint(currExt.xMinimum(), point.y())
        rightPt = QgsPoint(currExt.xMaximum(), point.y())

        topPt = QgsPoint(point.x(), currExt.yMaximum())
        bottomPt = QgsPoint(point.x(), currExt.yMinimum())

        horizLine = QgsGeometry.fromPolyline([leftPt, rightPt])
        vertLine = QgsGeometry.fromPolyline([topPt, bottomPt])

        self.crossRb.reset(QgsWkbTypes.LineGeometry)
        self.crossRb.addGeometry(horizLine, None)
        self.crossRb.addGeometry(vertLine, None)

        QTimer.singleShot(700, self.resetRubberbands)

    def resetRubberbands(self):
        self.crossRb.reset()

    def digitizeClicked(self):
        if self.digitizerDialog is None:
            from .digitizer import DigitizerWidget
            self.digitizerDialog = DigitizerWidget(self, self.iface,
                                                   self.iface.mainWindow())
        self.digitizerDialog.show()

    def currentLayerChanged(self):
        layer = self.iface.activeLayer()
        if layer is not None:
            try:
                layer.editingStarted.disconnect(self.layerEditingChanged)
            except Exception:
                pass
            try:
                layer.editingStopped.disconnect(self.layerEditingChanged)
            except Exception:
                pass

            if isinstance(layer, QgsVectorLayer):
                layer.editingStarted.connect(self.layerEditingChanged)
                layer.editingStopped.connect(self.layerEditingChanged)

        self.enableDigitizeTool()

    def layerEditingChanged(self):
        self.enableDigitizeTool()

    def enableDigitizeTool(self):
        self.digitizeAction.setEnabled(False)
        layer = self.iface.activeLayer()

        if layer is not None and isinstance(layer, QgsVectorLayer) and (
                layer.geometryType()
                == QgsWkbTypes.PointGeometry) and layer.isEditable():
            self.digitizeAction.setEnabled(True)
        else:
            if self.digitizerDialog is not None:
                self.digitizerDialog.close()
    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 InaSAFE 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
        """

        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.addPluginToVectorMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action
Example #19
0
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 = QFileInfo(QgsApplication.qgisUserDatabaseFilePath(
        )).path() + "/python/plugins/pdokservicesplugin"
        # initialize locale
        localePath = ""
        if isinstance(QSettings().value("locale/userLocale"), QVariant):
            locale = QSettings().value("locale/userLocale").value()[0:2]
        else:
            locale = QSettings().value("locale/userLocale")[0:2]

        if QFileInfo(self.plugin_dir).exists():
            localePath = self.plugin_dir + "/i18n/pdokservicesplugin_" + locale + ".qm"

        if QFileInfo(localePath).exists():
            self.translator = QTranslator()
            self.translator.load(localePath)
            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)
        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
        runIcon = QIcon(os.path.join(self.plugin_dir, 'icon_add_service.svg'))
        self.run_action = QAction(runIcon, \
            "PDOK Services plugin", self.iface.mainWindow())

        self.servicesLoaded = False
        # connect the action to the run method
        # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway
        # if "True" == self.docked or "true" == self.docked or  True == self.docked:
        #     self.run_action.triggered.connect(self.showAndRaise)
        #     self.dlg.radioDocked.setChecked(True)
        #     # docked the dialog is immidiately visible, so should run NOW
        # else:
        #     self.run_action.triggered.connect(self.run)
        #     self.dlg.radioDocked.setChecked(False)
        #     self.setupfq()
        self.run_action.triggered.connect(self.run)
        #self.dlg.radioDocked.setChecked(False)
        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.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.dlg.radioDocked.toggled.connect(self.set_docked)

        self.dlg.btnCheckPdokJson.clicked.connect(self.checkPdokJson)
        #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()

    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(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 set_docked(self, foo):
    #     self.setSettingsValue('docked', self.dlg.radioDocked.isChecked())
    #     #if Qgis.QGIS_VERSION_INT < 10900:
    #     #    # qgis <= 1.8
    #     #    QSettings().setValue("/pdokservicesplugin/docked", QVariant(self.dlg.radioDocked.isChecked()))
    #     #else:
    #     #    QSettings().setValue("/pdokservicesplugin/docked", self.dlg.radioDocked.isChecked())

    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 = title + ' [' + 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>&nbsp;</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 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
        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 = title + ' [' + 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 + "&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(os.path.dirname(__file__), ".",
                                    "pdok.json")
            f = open(pdokjson, 'r', encoding='utf-8')
            self.pdok = json.load(f)
            f.close()

            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) or 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)
            # 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 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:
            self.iface.mapCanvas().scene().removeItem(self.pointer)
            self.clean_action.setEnabled(False)

    def info(self, msg=""):
        QgsMessageLog.logMessage('{}'.format(msg), 'PDOK-services Plugin',
                                 Qgis.Info)
class captureGPSFeatures(FieldRestrictionTypeUtilsMixin):

    def __init__(self, iface, featuresWithGPSToolbar):

        TOMsMessageLog.logMessage("In captureGPSFeatures::init", level=Qgis.Info)

        FieldRestrictionTypeUtilsMixin.__init__(self, iface)

        # Save reference to the QGIS interface
        self.iface = iface
        self.canvas = self.iface.mapCanvas()

        self.featuresWithGPSToolbar = featuresWithGPSToolbar
        self.gpsMapTool = False
        self.marker = None

        # This will set up the items on the toolbar
        # Create actions

        self.gnssToolGroup = QActionGroup(featuresWithGPSToolbar)
        self.actionCreateRestriction = QAction(QIcon(":/plugins/featureswithgps/resources/mActionAddTrack.svg"),
                               QCoreApplication.translate("MyPlugin", "Create Restriction"),
                               self.iface.mainWindow())
        self.actionCreateRestriction.setCheckable(True)

        self.actionAddGPSLocation = QAction(QIcon(":/plugins/featureswithgps/resources/greendot3.png"),
                               QCoreApplication.translate("MyPlugin", "Add vertex from gnss"),
                               self.iface.mainWindow())
        #self.actionAddGPSLocation.setCheckable(True)

        self.actionRemoveRestriction = QAction(QIcon(":plugins/featureswithgps/resources/mActionDeleteTrack.svg"),
                                        QCoreApplication.translate("MyPlugin", "Remove Restriction"),
                                        self.iface.mainWindow())
        self.actionRemoveRestriction.setCheckable(True)

        self.actionRestrictionDetails = QAction(QIcon(":/plugins/featureswithgps/resources/mActionGetInfo.svg"),
                                         QCoreApplication.translate("MyPlugin", "Get Restriction Details"),
                                         self.iface.mainWindow())
        self.actionRestrictionDetails.setCheckable(True)
        self.gnssToolGroup.addAction(self.actionRestrictionDetails)

        self.actionCreateSign = QAction(QIcon(":/plugins/featureswithgps/resources/mActionSetEndPoint.svg"),
                                                    QCoreApplication.translate("MyPlugin", "Create sign from gnss"),
                                                    self.iface.mainWindow())
        self.actionCreateSign.setCheckable(True)

        self.actionCreateMTR = QAction(QIcon(":/plugins/featureswithgps/resources/UK_traffic_sign_606F.svg"),
                                                    QCoreApplication.translate("MyPlugin", "Create moving traffic restriction"),
                                                    self.iface.mainWindow())
        self.actionCreateMTR.setCheckable(True)

        self.actionMoveFeatureToDifferentLayer = QAction(QIcon(""),
                                         QCoreApplication.translate("MyPlugin", "Move feature to different layer"),
                                         self.iface.mainWindow())
        self.actionMoveFeatureToDifferentLayer.setCheckable(True)
        self.gnssToolGroup.addAction(self.actionMoveFeatureToDifferentLayer)

        # Add actions to the toolbar

        self.featuresWithGPSToolbar.addAction(self.actionCreateRestriction)
        self.featuresWithGPSToolbar.addAction(self.actionAddGPSLocation)
        self.featuresWithGPSToolbar.addAction(self.actionRestrictionDetails)
        #self.featuresWithGPSToolbar.addAction(self.actionRemoveRestriction)
        self.featuresWithGPSToolbar.addAction(self.actionCreateSign)
        #self.featuresWithGPSToolbar.addAction(self.actionCreateMTR)
        self.featuresWithGPSToolbar.addAction(self.actionMoveFeatureToDifferentLayer)

        self.gnssToolGroup.addAction(self.actionCreateRestriction)
        #self.gnssToolGroup.addAction(self.actionAddGPSLocation)
        #self.gnssToolGroup.addAction(self.actionRemoveRestriction)
        self.gnssToolGroup.addAction(self.actionRestrictionDetails)
        #self.gnssToolGroup.addAction(self.actionCreateSign)
        #self.gnssToolGroup.addAction(self.actionCreateMTR)
        self.gnssToolGroup.addAction(self.actionMoveFeatureToDifferentLayer)

        self.gnssToolGroup.setExclusive(True)
        self.gnssToolGroup.triggered.connect(self.onGroupTriggered)

        # Connect action signals to slots

        self.actionCreateRestriction.triggered.connect(self.doCreateRestriction)
        self.actionAddGPSLocation.triggered.connect(self.doAddGPSLocation)
        self.actionRestrictionDetails.triggered.connect(self.doRestrictionDetails)
        #self.actionRemoveRestriction.triggered.connect(self.doRemoveRestriction)
        self.actionCreateSign.triggered.connect(self.doCreateSign)
        #self.actionCreateMTR.triggered.connect(self.doCreateMTR)
        self.actionMoveFeatureToDifferentLayer.triggered.connect(self.doMoveFeatureToDifferentLayer)

        self.actionCreateRestriction.setEnabled(False)
        self.actionAddGPSLocation.setEnabled(False)
        self.actionRestrictionDetails.setEnabled(False)
        #self.actionRemoveRestriction.setEnabled(False)
        self.actionCreateSign.setEnabled(False)
        #self.actionCreateMTR.setEnabled(False)
        self.actionMoveFeatureToDifferentLayer.setEnabled(False)

        self.searchBar = searchBar(self.iface, self.featuresWithGPSToolbar)
        self.searchBar.disableSearchBar()

        self.mapTool = None
        self.currGnssAction = None
        self.gpsConnection = None
        self.createMapToolDict = {}

    def enableFeaturesWithGPSToolbarItems(self):

        TOMsMessageLog.logMessage("In enablefeaturesWithGPSToolbarItems", level=Qgis.Warning)
        self.gpsAvailable = False
        self.closeTOMs = False

        self.tableNames = TOMsLayers(self.iface)
        self.params = gpsParams()

        self.tableNames.TOMsLayersNotFound.connect(self.setCloseTOMsFlag)
        #self.tableNames.gpsLayersNotFound.connect(self.setCloseCaptureGPSFeaturesFlag)
        self.params.TOMsParamsNotFound.connect(self.setCloseCaptureGPSFeaturesFlag)

        self.TOMsConfigFileObject = TOMsConfigFile()
        self.TOMsConfigFileObject.TOMsConfigFileNotFound.connect(self.setCloseTOMsFlag)
        self.TOMsConfigFileObject.initialiseTOMsConfigFile()

        self.tableNames.getLayers(self.TOMsConfigFileObject)

        self.prj = QgsProject().instance()
        self.dest_crs = self.prj.crs()
        TOMsMessageLog.logMessage("In captureGPSFeatures::init project CRS is " + self.dest_crs.description(),
                                 level=Qgis.Warning)
        self.transformation = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:4326"), self.dest_crs,
                                                     self.prj)

        self.params.getParams()

        if self.closeTOMs:
            QMessageBox.information(self.iface.mainWindow(), "ERROR", ("Unable to start editing tool ..."))
            #self.actionProposalsPanel.setChecked(False)
            return   # TODO: allow function to continue without GPS enabled ...

        # Now check to see if the port is set. If not assume that just normal tools

        gpsPort = self.params.setParam("gpsPort")
        TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems: GPS port is: {}".format(gpsPort), level=Qgis.Warning)
        self.gpsConnection = None

        if gpsPort:
            self.gpsAvailable = True

        if self.gpsAvailable == True:
            self.curr_gps_location = None
            self.curr_gps_info = None

            TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems - GPS port is specified ",
                                     level=Qgis.Info)
            self.gps_thread = GPS_Thread(self.dest_crs, gpsPort)
            thread = QThread()
            self.gps_thread.moveToThread(thread)
            self.gps_thread.gpsActivated.connect(self.gpsStarted)
            self.gps_thread.gpsPosition.connect(self.gpsPositionProvided)
            self.gps_thread.gpsDeactivated.connect(functools.partial(self.gpsStopped))
            self.gps_thread.gpsError.connect(self.gpsErrorEncountered)
            #self.gps_thread.progress.connect(progressBar.setValue)
            thread.started.connect(self.gps_thread.startGPS)
            #thread.finished.connect(functools.partial(self.gpsStopped, thread))
            thread.start()
            self.thread = thread

            TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems - attempting connection ",
                                     level=Qgis.Info)

            time.sleep(1.0)

            try:
                self.roamDistance = float(self.params.setParam("roamDistance"))
            except Exception as e:
                TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems:init: roamDistance issue: {}".format(e), level=Qgis.Warning)
                self.roamDistance = 5.0

        self.enableToolbarItems()

        self.createMapToolDict = {}

    def enableToolbarItems(self):
        TOMsMessageLog.logMessage("In enableToolbarItems", level=Qgis.Warning)
        self.actionCreateRestriction.setEnabled(True)
        self.actionRestrictionDetails.setEnabled(True)
        #self.actionRemoveRestriction.setEnabled(True)
        #self.actionCreateSign.setEnabled(True)
        #self.actionCreateMTR.setEnabled(True)
        self.actionMoveFeatureToDifferentLayer.setEnabled(True)

        self.searchBar.enableSearchBar()

        self.currMapTool = None
        self.theCurrentMapTool = None

        self.iface.currentLayerChanged.connect(self.changeCurrLayer2)
        self.canvas.mapToolSet.connect(self.changeMapTool2)
        self.canvas.extentsChanged.connect(self.changeExtents)

        # transaction for move ...
        self.localTransaction = MoveLayerTransaction(self.iface)

    def enableGnssToolbarItem(self):
        if self.gpsConnection:
            self.actionAddGPSLocation.setEnabled(True)
            self.actionCreateSign.setEnabled(True)
            self.lastCentre = QgsPointXY(0,0)

    def disableGnssToolbarItem(self):
        self.actionAddGPSLocation.setEnabled(False)
        self.actionCreateSign.setEnabled(False)

    def disableToolbarItems(self):

        self.actionCreateRestriction.setEnabled(False)
        self.actionRestrictionDetails.setEnabled(False)
        self.actionRemoveRestriction.setEnabled(False)
        self.actionCreateSign.setEnabled(False)
        #self.actionCreateMTR.setEnabled(False)
        self.actionMoveFeatureToDifferentLayer.setEnabled(False)

        self.searchBar.disableSearchBar()

        """if self.gpsConnection:
            self.actionAddGPSLocation.setEnabled(False)"""

    def setCloseTOMsFlag(self):
        self.closeTOMs = True
        QMessageBox.information(self.iface.mainWindow(), "ERROR", ("Now closing TOMs ..."))

    def disableFeaturesWithGPSToolbarItems(self):

        TOMsMessageLog.logMessage("In disablefeaturesWithGPSToolbarItems", level=Qgis.Warning)
        if self.gpsConnection and not self.closeTOMs:
            self.gps_thread.endGPS()

        self.disableToolbarItems()

        # TODO: Need to delete any tools ...
        for layer, mapTool in self.createMapToolDict.items  ():
            try:
                status = layer.rollBack()
            except Exception as e:
                None
                """reply = QMessageBox.information(None, "Information",
                                                    "Problem rolling back changes" + str(self.currLayer.commitErrors()),
                                                    QMessageBox.Ok)"""
            del mapTool

        self.createMapToolDict = {}

        try:
            self.iface.currentLayerChanged.disconnect(self.changeCurrLayer2)
        except Exception as e:
            TOMsMessageLog.logMessage(
                "In disableFeaturesWithGPSToolbarItems. Issue with disconnects for currentLayerChanged {}".format(e),
                level=Qgis.Warning)

        try:
            self.canvas.mapToolSet.disconnect(self.changeMapTool2)
        except Exception as e:
            TOMsMessageLog.logMessage(
                "In disableFeaturesWithGPSToolbarItems. Issue with disconnects for mapToolSet {}".format(
                    e),
                level=Qgis.Warning)

        try:
            self.canvas.extentsChanged.disconnect(self.changeExtents)
        except Exception as e:
            TOMsMessageLog.logMessage(
                "In disableFeaturesWithGPSToolbarItems. Issue with disconnects for extentsChanged {}".format(
                    e),
                level=Qgis.Warning)

        self.tableNames.removePathFromLayerForms()

    def setCloseCaptureGPSFeaturesFlag(self):
        self.closeCaptureGPSFeatures = True
        self.gpsAvailable = True

    def onGroupTriggered(self, action):
        # hold the current action
        self.currGnssAction = action
        TOMsMessageLog.logMessage("In onGroupTriggered: curr action is {}".format(action.text()), level=Qgis.Info)

    """ 
        Using signals for ChangeTool and ChangeLayer to manage the tools - with the following functions
    """
    def isGnssTool(self, mapTool):

        if (isinstance(mapTool, CreateRestrictionTool) or
           isinstance(mapTool, GeometryInfoMapTool) or
           isinstance(mapTool, RemoveRestrictionTool)):
            return True

        return False

    def changeMapTool2(self):
        TOMsMessageLog.logMessage(
            "In changeMapTool2 ...", level=Qgis.Info)

        currMapTool = self.iface.mapCanvas().mapTool()

        if not self.isGnssTool(currMapTool):
            TOMsMessageLog.logMessage(
                "In changeMapTool2. Unchecking action ...", level=Qgis.Info)
            if self.currGnssAction:
                self.currGnssAction.setChecked(False)
        else:
            TOMsMessageLog.logMessage(
            "In changeMapTool2. No action for gnssTools.", level=Qgis.Info)

        TOMsMessageLog.logMessage(
            "In changeMapTool2. finished.", level=Qgis.Info)
        #print('tool unset')

    def changeCurrLayer2(self):
        TOMsMessageLog.logMessage("In changeLayer2 ... ", level=Qgis.Info)

        try:
            currMapTool = self.iface.mapCanvas().mapTool()
            self.currGnssAction.setChecked(False)
        except Exception as e:
            None

        """if self.isGnssTool(currMapTool):
            TOMsMessageLog.logMessage("In changeLayer2. Action triggered ... ", level=Qgis.Info)
            self.currGnssAction.trigger()  # assumption is that there is an action associated with the tool
        else:
            TOMsMessageLog.logMessage(
            "In changeLayer2. No action for currentMapTool.", level=Qgis.Info)"""

        TOMsMessageLog.logMessage(
            "In changeLayer2. finished.", level=Qgis.Info)
        print('layer changed')


    def doCreateRestriction(self):

        TOMsMessageLog.logMessage("In doCreateRestriction", level=Qgis.Info)

        self.currLayer = self.iface.activeLayer()
        if not self.currLayer:
            reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...",
                                            QMessageBox.Ok)
            return

        # TODO: Check that this is a restriction layer

        if self.actionCreateRestriction.isChecked():

            TOMsMessageLog.logMessage("In doCreateRestriction - tool activated", level=Qgis.Info)
            TOMsMessageLog.logMessage(
                "In doCreateRestriction: current map tool {}".format(type(self.iface.mapCanvas().mapTool()).__name__),
                level=Qgis.Info)

            self.createRestrictionMapTool = self.createMapToolDict.get(self.currLayer)

            if not self.createRestrictionMapTool:
                TOMsMessageLog.logMessage("In doCreateRestriction. creating new map tool", level=Qgis.Info)
                self.createRestrictionMapTool = CreateRestrictionTool(self.iface, self.currLayer)
                self.createMapToolDict[self.currLayer] = self.createRestrictionMapTool

            TOMsMessageLog.logMessage("In doCreateRestriction. Here 1", level=Qgis.Info)

            self.iface.mapCanvas().setMapTool(self.createRestrictionMapTool)

            TOMsMessageLog.logMessage("In doCreateRestriction. Here 2", level=Qgis.Info)

            if not self.createRestrictionMapTool.isCapturing():
                if self.currLayer.isEditable() == True:
                    if self.currLayer.commitChanges() == False:
                        reply = QMessageBox.information(None, "Information",
                                                        "Problem committing changes" + str(self.currLayer.commitErrors()),
                                                        QMessageBox.Ok)
                    else:
                        TOMsMessageLog.logMessage("In doCreateRestriction: changes committed", level=Qgis.Info)

                if self.currLayer.readOnly() == True:
                    TOMsMessageLog.logMessage("In doCreateRestriction - Not able to start transaction ...",
                                             level=Qgis.Info)
                else:
                    if self.currLayer.startEditing() == False:
                        reply = QMessageBox.information(None, "Information",
                                                        "Could not start transaction on " + self.currLayer.name(),
                                                        QMessageBox.Ok)
                        return

            TOMsMessageLog.logMessage("In doCreateRestriction. Here 3", level=Qgis.Info)

        else:

            TOMsMessageLog.logMessage("In doCreateRestriction - tool deactivated", level=Qgis.Info)

            if self.createRestrictionMapTool:
                self.iface.mapCanvas().unsetMapTool(self.createRestrictionMapTool)

            self.currMapTool = None
            self.currentlySelectedLayer = None

            self.actionCreateRestriction.setChecked(False)

            # TODO: stop editting on layers??

        TOMsMessageLog.logMessage("In doCreateRestriction. Here 4", level=Qgis.Info)



    # -- end of tools for signals

    def changeExtents(self):
        TOMsMessageLog.logMessage("In changeExtents ... ", level=Qgis.Info)

    def doAddGPSLocation(self):

        # need to have a addPointFromGPS function within each tool

        TOMsMessageLog.logMessage("In doAddGPSLocation", level=Qgis.Info)

        if self.gpsConnection:

            if self.curr_gps_location:
                try:
                    status = self.createRestrictionMapTool.addPointFromGPS(self.curr_gps_location, self.curr_gps_info)
                except Exception as e:
                    TOMsMessageLog.logMessage("In doAddGPSLocation: Problem adding gnss location: {}".format(e), level=Qgis.Warning)
                    reply = QMessageBox.information(self.iface.mainWindow(), "Error",
                                                    "Problem adding gnss location ... ",
                                                    QMessageBox.Ok)
            else:
                reply = QMessageBox.information(self.iface.mainWindow(), "Information",
                                                "No position found ...",
                                                QMessageBox.Ok)
        else:

            reply = QMessageBox.information(self.iface.mainWindow(), "Information", "You need to activate the tool first ...",
                                            QMessageBox.Ok)

    def doRestrictionDetails(self):
        """
            Select point and then display details. Assume that there is only one of these map tools in existence at any one time ??
        """
        TOMsMessageLog.logMessage("In doRestrictionDetails", level=Qgis.Info)

        # TODO: Check whether or not there is a create maptool available. If so, stop this and finish using that/those tools

        if not self.iface.activeLayer():
            reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...",
                                            QMessageBox.Ok)
            return

        if self.actionRestrictionDetails.isChecked():

            TOMsMessageLog.logMessage("In doRestrictionDetails - tool activated", level=Qgis.Warning)

            self.showRestrictionMapTool = GeometryInfoMapTool(self.iface)
            self.iface.mapCanvas().setMapTool(self.showRestrictionMapTool)
            self.showRestrictionMapTool.notifyFeatureFound.connect(self.showRestrictionDetails)

        else:

            TOMsMessageLog.logMessage("In doRestrictionDetails - tool deactivated", level=Qgis.Warning)

            if self.showRestrictionMapTool:
                self.iface.mapCanvas().unsetMapTool(self.showRestrictionMapTool)

            self.actionRestrictionDetails.setChecked(False)

    #@pyqtSlot(str)
    def showRestrictionDetails(self, closestLayer, closestFeature):

        TOMsMessageLog.logMessage(
            "In showRestrictionDetails ... Layer: " + str(closestLayer.name()),
            level=Qgis.Info)

        self.showRestrictionMapTool.notifyFeatureFound.disconnect(self.showRestrictionDetails)

        # TODO: could improve ... basically check to see if transaction in progress ...
        if closestLayer.isEditable() == True:
            reply = QMessageBox.question(None, "Information",
                                            "There is a transaction in progress on this layer. This action will rollback back any changes. Do you want to continue?",
                                            QMessageBox.Yes, QMessageBox.No)
            if reply == QMessageBox.No:
                return
            if closestLayer.commitChanges() == False:
                reply = QMessageBox.information(None, "Information",
                                                "Problem committing changes" + str(closestLayer.commitErrors()),
                                                QMessageBox.Ok)
            else:
                TOMsMessageLog.logMessage("In showRestrictionDetails: changes committed", level=Qgis.Info)

        """if self.iface.activeLayer().readOnly() == True:
            TOMsMessageLog.logMessage("In showSignDetails - Not able to start transaction ...",
                                     level=Qgis.Info)
        else:
            if self.iface.activeLayer().startEditing() == False:
                reply = QMessageBox.information(None, "Information",
                                                "Could not start transaction on " + self.currLayer.name(),
                                                QMessageBox.Ok)
                return"""

        self.dialog = self.iface.getFeatureForm(closestLayer, closestFeature)
        #self.TOMsUtils.setupRestrictionDialog(self.dialog, closestLayer, closestFeature)
        self.setupFieldRestrictionDialog(self.dialog, closestLayer, closestFeature)

        self.dialog.show()

    """
        Decided that it is best to use the QGIS select/delete tools to manage removals. So these functions are not used
    """
    def doRemoveRestriction(self):

        TOMsMessageLog.logMessage("In doRemoveRestriction", level=Qgis.Info)

        self.currLayer = self.iface.activeLayer()

        if not self.currLayer:
            reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...",
                                            QMessageBox.Ok)
            return

        if self.currLayer.readOnly() == True:
            """reply = QMessageBox.information(None, "Information",
                                            "Could not start transaction on " + self.currLayer.name(), QMessageBox.Ok)"""
            TOMsMessageLog.logMessage("In doRemoveRestriction - Not able to start transaction ...", level=Qgis.Info)
            self.actionRemoveRestriction.setChecked(False)
            return

        if self.actionRemoveRestriction.isChecked():

            TOMsMessageLog.logMessage("In doRemoveRestriction - tool activated", level=Qgis.Warning)

            """self.mapTool = self.deleteMapToolDict.get(self.currLayer)

            if not self.mapTool:
                self.mapTool = RemoveRestrictionTool(self.iface)
                self.deleteMapToolDict[self.currLayer] =  self.mapTool"""

            self.mapTool = RemoveRestrictionTool(self.iface)
            #self.removeRestrictionMapTool.setAction(self.actionRemoveRestriction)
            self.iface.mapCanvas().setMapTool(self.removeRestrictionMapTool)
            #self.gpsMapTool = True
            #self.removeRestrictionMapTool.deactivated.connect(functools.partial(self.deactivateAction, self.actionRemoveRestriction))
            #self.iface.currentLayerChanged.connect(self.changeCurrLayer)
            #self.canvas.mapToolSet.connect(self.changeMapTool)

            self.removeRestrictionMapTool.notifyFeatureFound.connect(self.removeRestriction)

        else:

            TOMsMessageLog.logMessage("In doRemoveRestriction - tool deactivated", level=Qgis.Warning)

            self.removeRestrictionMapTool.notifyFeatureFound.disconnect(self.removeRestriction)

            #self.canvas.mapToolSet.disconnect(self.changeMapTool)
            #self.iface.currentLayerChanged.disconnect(self.changeCurrLayer)

            self.iface.mapCanvas().unsetMapTool(self.removeRestrictionMapTool)
            #self.removeRestrictionMapTool.deactivate()
            #self.mapTool = None
            self.actionRemoveRestriction.setChecked(False)

    #@pyqtSlot(str)
    def removeRestriction(self, closestLayer, closestFeature):

        TOMsMessageLog.logMessage(
            "In removeRestriction ... Layer: " + str(closestLayer.name()),
            level=Qgis.Info)

        if closestLayer.isEditable() == True:
            if closestLayer.commitChanges() == False:
                reply = QMessageBox.information(None, "Information",
                                                "Problem committing changes" + str(closestLayer.commitErrors()),
                                                QMessageBox.Ok)
            else:
                TOMsMessageLog.logMessage("In removeRestriction: changes committed", level=Qgis.Info)

        if self.currLayer.startEditing() == False:
            reply = QMessageBox.information(None, "Information",
                                            "Could not start transaction on " + self.currLayer.name(),
                                            QMessageBox.Ok)
            return

        # TODO: Sort out this for UPDATE
        # self.setDefaultRestrictionDetails(closestFeature, closestLayer)

        closestLayer.deleteFeature(closestFeature.id())

        if closestLayer.commitChanges() == False:
            reply = QMessageBox.information(None, "Information",
                                            "Problem committing changes" + str(closestLayer.commitErrors()),
                                            QMessageBox.Ok)
        else:
            TOMsMessageLog.logMessage("In removeRestriction: changes committed", level=Qgis.Info)

    """
        This is a tool for adding a point feature. currently only used for signs, but could be used for any point
    """
    def doCreateSign(self):

        TOMsMessageLog.logMessage("In doCreateSign", level=Qgis.Info)

        if self.actionCreateSign.isChecked():

            self.currMapTool = self.canvas.mapTool()
            self.currentlySelectedLayer = self.iface.activeLayer()
            self.signsLayer = self.tableNames.setLayer("Signs")

            self.iface.setActiveLayer(self.signsLayer)

            self.createPointMapTool = CreatePointTool(self.iface, self.signsLayer)

            TOMsMessageLog.logMessage("In doCreateSign - tool activated", level=Qgis.Info)

            self.signsLayer.editingStopped.connect(self.reinstateMapTool)

            self.actionCreateSign.setChecked(False)

            self.iface.mapCanvas().setMapTool(self.createPointMapTool)

            """ add the point from the gnss """
            try:
                status = self.canvas.mapTool().addPointFromGPS(self.curr_gps_location, self.curr_gps_info)
            except Exception as e:
                TOMsMessageLog.logMessage("In doCreateSign: Problem adding gnss location: {}".format(e),
                                          level=Qgis.Warning)
                reply = QMessageBox.information(self.iface.mainWindow(), "Error",
                                                "Problem adding gnss location ... ",
                                                QMessageBox.Ok)

    """
        Not currently used, but want to develop ...
    """
    def doCreateMTR(self):

        TOMsMessageLog.logMessage("In doCreateMTR", level=Qgis.Info)

        if self.actionCreateMTR.isChecked():

            TOMsMessageLog.logMessage("In doCreateMTR - tool activated", level=Qgis.Info)

            # Open MTR form ...

            try:
                self.thisMtrForm
            except AttributeError:
                self.thisMtrForm = mtrForm(self.iface)

            #res = mtrFormFactory.prepareForm(self.iface, self.dbConn, self.dialog)
            #self.mtrTypeCB = self.dialog.findChild(QComboBox, "cmb_MTR_list")
            #self.mtrTypeCB.activated[str].connect(self.onLocalChanged)
            #self.currDialog.findChild(QComboBox, "cmb_MTR_list").activated[str].connect(self.onChanged)


            """ Need to setup dialog:
                a. create drop down
                b. link structure of form to different options from drop down, e.g., Access Restriction needs ?? attributes and one point, Turn Restriction needs ?? attributes and two points
                c. link getPoint actions to buttons
            """
            status = self.thisMtrForm.show()
            # Run the dialog event loop
            result = self.thisMtrForm.exec_()
            #

        else:

            TOMsMessageLog.logMessage("In doCreateMTR - tool deactivated", level=Qgis.Info)

            #self.iface.mapCanvas().unsetMapTool(self.mapTool)
            #self.mapTool = None
            self.actionCreateMTR.setChecked(False)
            self.gpsMapTool = False

    def onLocalChanged(self, text):
        TOMsMessageLog.logMessage(
            "In generateFirstStageForm::selectionchange.  " + text, level=Qgis.Info)
        res = mtrFormFactory.prepareForm(self.iface, self.dbConn, self.dialog, text)

    """
        Used with the createSign tool to reinstate the last used maptool, i.e., to allow the interupt of feature creation
    """

    def reinstateMapTool(self):

        TOMsMessageLog.logMessage("In reinstateMapTool ... ", level=Qgis.Info)
        self.iface.activeLayer().editingStopped.disconnect(self.reinstateMapTool)

        if self.currMapTool:

            TOMsMessageLog.logMessage(
                "In reinstateMapTool. layer to be reinstated {} using tool {}".format(self.currentlySelectedLayer.name(), self.currMapTool.toolName()),
                level=Qgis.Warning)
            # now reinstate
            if self.currentlySelectedLayer:
                self.iface.setActiveLayer(self.currentlySelectedLayer)

            self.iface.mapCanvas().setMapTool(self.currMapTool)


    def doMoveFeatureToDifferentLayer(self):
        """
            Select point and then display details. Assume that there is only one of these map tools in existence at any one time ??
        """
        TOMsMessageLog.logMessage("In doMoveFeatureToDifferentLayer", level=Qgis.Info)

        # TODO: Check whether or not there is a create maptool available. If so, stop this and finish using that/those tools

        if not self.iface.activeLayer():
            reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...",
                                            QMessageBox.Ok)
            return

        if self.actionMoveFeatureToDifferentLayer.isChecked():

            TOMsMessageLog.logMessage("In doMoveFeatureToDifferentLayer - tool activated", level=Qgis.Warning)

            self.moveFeatureToDifferentLayerMapTool = ChangeLayerMapTool(self.iface, self.localTransaction)
            self.iface.mapCanvas().setMapTool(self.moveFeatureToDifferentLayerMapTool)
            #self.showRestrictionMapTool.notifyFeatureFound.connect(self.showRestrictionDetails)

        else:

            TOMsMessageLog.logMessage("In doMoveFeatureToDifferentLayer - tool deactivated", level=Qgis.Warning)

            if self.moveFeatureToDifferentLayerMapTool:
                self.iface.mapCanvas().unsetMapTool(self.moveFeatureToDifferentLayerMapTool)

            self.actionMoveFeatureToDifferentLayer.setChecked(False)


    #@pyqtSlot(QgsGpsConnection)
    def gpsStarted(self, connection):
        TOMsMessageLog.logMessage("In enableTools - GPS connection found ",
                                     level=Qgis.Info)

        self.gpsConnection = connection

        # marker
        self.marker = QgsVertexMarker(self.canvas)
        self.marker.setColor(QColor(255, 0, 0))  # (R,G,B)
        self.marker.setIconSize(10)
        self.marker.setIconType(QgsVertexMarker.ICON_CIRCLE)
        self.marker.setPenWidth(3)

        self.enableGnssToolbarItem()
        reply = QMessageBox.information(None, "Information",
                                            "Connection found",
                                            QMessageBox.Ok)

    #@pyqtSlot()
    def gpsStopped(self):
        TOMsMessageLog.logMessage("In enableTools - GPS connection stopped ",
                                     level=Qgis.Warning)

        self.gps_thread.deleteLater()
        self.thread.quit()
        self.thread.wait()
        self.thread.deleteLater()

        if self.gpsConnection:
            if self.canvas is not None:
                self.marker.hide()
                self.canvas.scene().removeItem(self.marker)

        self.gpsConnection = None
        self.disableGnssToolbarItem()

    #@pyqtSlot()
    def gpsPositionProvided(self, mapPointXY, gpsInfo):
        """reply = QMessageBox.information(None, "Information",
                                            "Position provided",
                                            QMessageBox.Ok)"""
        TOMsMessageLog.logMessage("In enableTools - ******** initial GPS location provided " + mapPointXY.asWkt(),
                                     level=Qgis.Info)

        self.curr_gps_location = mapPointXY
        self.curr_gps_info = gpsInfo

        wgs84_pointXY = QgsPointXY(gpsInfo.longitude, gpsInfo.latitude)
        wgs84_point = QgsPoint(wgs84_pointXY)
        wgs84_point.transform(self.transformation)
        x = wgs84_point.x()
        y = wgs84_point.y()
        new_mapPointXY = QgsPointXY(x, y)

        TOMsMessageLog.logMessage("In enableTools - ******** transformed GPS location provided " + str(gpsInfo.longitude) + ":" + str(gpsInfo.latitude) + "; " + new_mapPointXY.asWkt(),
                                     level=Qgis.Info)

        if gpsInfo.pdop >= 1:  # gps ok
            self.marker.setColor(QColor(0, 200, 0))
        else:
            self.marker.setColor(QColor(255, 0, 0))
        self.marker.setCenter(mapPointXY)
        self.marker.show()
        #self.canvas.setCenter(mapPointXY)

        """TOMsMessageLog.logMessage("In enableTools: distance from last fix: {}".format(self.lastCentre.distance(mapPointXY)),
                                     level=Qgis.Info)"""
        if self.lastCentre.distance(mapPointXY) > self.roamDistance:
            self.lastCentre = mapPointXY
            self.canvas.setCenter(mapPointXY)
            TOMsMessageLog.logMessage(
                "In enableTools: distance from last fix: {}".format(self.lastCentre.distance(mapPointXY)),
                level=Qgis.Warning)
            self.canvas.refresh()

        # TODO: populate message bar with details about satellites, etc

    #@pyqtSlot(Exception, str)
    def gpsErrorEncountered(self, e):
        TOMsMessageLog.logMessage("In enableTools - GPS connection has error {}".format(e),
                                     level=Qgis.Warning)
        """self.actionCreateRestriction.setEnabled(False)
        self.actionAddGPSLocation.setEnabled(False)"""
        self.disableGnssToolbarItem()
Example #21
0
class batchHillshader:
    """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',
                                   'batch_hillshader_{}.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)
        # Create the dialog (after translation) and keep reference
        self.dlg = batchHillshaderDialog(iface)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr('&Batch Hillshader')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar('Batch Hillshader')
        self.toolbar.setObjectName('Batch Hillshader')

    # 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('Batch Hillshader', 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
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        aboutIcon = QIcon(':/plugins/batch_hillshader/icons/aboutIcon.png')
        self.actionAbout = QAction(aboutIcon, 'About', self.iface.mainWindow())
        self.actionAbout.triggered.connect(self.about)
        self.actionAbout.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.iface.addPluginToMenu(self.menu, self.actionAbout)

        self.actions.append(action)
        self.actions.append(self.actionAbout)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""
        icon_path = ':/plugins/batch_hillshader/icons/icon.png'
        self.add_action(icon_path,
                        text=self.tr('Batch Hillshader'),
                        callback=self.run,
                        parent=self.iface.mainWindow())

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(self.tr('&Batch Hillshader'), action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def run(self):
        """Run method that performs all the real work"""
        # show the dialog
        print("Run called")
        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 about(self):
        DlgAbout(self.iface.mainWindow()).exec_()
Example #22
0
class WellLogView(QWidget):

    DEFAULT_COLUMN_WIDTH = 150

    def __init__(self, title=None, image_dir=None, parent=None):
        QWidget.__init__(self, parent)

        toolbar = QToolBar()
        self.__log_scene = MyScene(0, 0, 600, 600)
        self.__log_view = LogGraphicsView(self.__log_scene)
        self.__log_view.setAlignment(Qt.AlignLeft | Qt.AlignTop)

        self.__log_scene.sceneRectChanged.connect(self.on_rect_changed)

        if image_dir is None:
            image_dir = os.path.join(os.path.dirname(__file__), "img")

        self.__action_move_column_left = QAction(
            QIcon(os.path.join(image_dir, "left.svg")),
            "Move the column to the left", toolbar)
        self.__action_move_column_left.triggered.connect(
            self.on_move_column_left)
        self.__action_move_column_right = QAction(
            QIcon(os.path.join(image_dir, "right.svg")),
            "Move the column to the right", toolbar)
        self.__action_move_column_right.triggered.connect(
            self.on_move_column_right)

        self.__action_edit_style = QAction(
            QIcon(os.path.join(image_dir, "symbology.svg")),
            "Edit column style", toolbar)
        self.__action_edit_style.triggered.connect(self.on_edit_style)

        self.__action_add_column = QAction(
            QIcon(os.path.join(image_dir, "add.svg")), "Add a data column",
            toolbar)
        self.__action_add_column.triggered.connect(self.on_add_column)

        self.__action_remove_column = QAction(
            QIcon(os.path.join(image_dir, "remove.svg")), "Remove the column",
            toolbar)
        self.__action_remove_column.triggered.connect(self.on_remove_column)

        #self.__action_move_content_right = QAction("Move content right", toolbar)
        #self.__action_move_content_left = QAction("Move content left", toolbar)
        #self.__action_move_content_left.triggered.connect(self.on_move_content_left)
        #self.__action_move_content_right.triggered.connect(self.on_move_content_right)

        toolbar.addAction(self.__action_move_column_left)
        toolbar.addAction(self.__action_move_column_right)
        toolbar.addAction(self.__action_edit_style)
        toolbar.addAction(self.__action_add_column)
        toolbar.addAction(self.__action_remove_column)

        #self.__toolbar.addAction(self.__action_move_content_left)
        #self.__toolbar.addAction(self.__action_move_content_right)

        self.__title_label = QLabel()
        if title is not None:
            self.set_title(title)

        self.__status_bar = QStatusBar()

        vbox = QVBoxLayout()
        vbox.addWidget(self.__title_label)
        vbox.addWidget(toolbar)
        vbox.addWidget(self.__log_view)
        vbox.addWidget(self.__status_bar)
        self.setLayout(vbox)

        self.__station_id = None
        # (log_item, legend_item) for each column
        self.__columns = []
        # { layer : (log_item, legend_item) }
        self.__data2logitems = {}
        self.__column_widths = []

        self._min_z = 0
        self._max_z = 40

        self.__allow_mouse_translation = True
        self.__translation_orig = None

        self.__style_dir = os.path.join(os.path.dirname(__file__), 'styles')

        self.select_column(-1)

        # by default we have a Z scale
        self.add_z_scale()

    def on_rect_changed(self, rect):
        for item, _ in self.__columns:
            item.set_height(rect.height())

    def set_title(self, title):
        self.__title_label.setText(title)

    def _place_items(self):
        x = 0
        for i, c in enumerate(self.__columns):
            item, legend = c
            width = self.__column_widths[i]
            legend.setPos(x, 0)
            item.setPos(x, legend.boundingRect().height())
            x += width
        self.__log_view.setMinimumSize(x,
                                       self.__log_view.minimumSize().height())

    def _add_column(self, log_item, legend_item):
        self.__log_scene.addItem(log_item)
        self.__log_scene.addItem(legend_item)

        log_item.set_min_depth(self._min_z)
        log_item.set_max_depth(self._max_z)
        self.__columns.append((log_item, legend_item))
        self.__column_widths.append(log_item.boundingRect().width())

        self._place_items()

    def _fit_to_max_depth(self):
        self._min_z = min([
            i.min_depth() for i, _ in self.__columns
            if i.min_depth() is not None
        ])
        self._max_z = max([
            i.max_depth() for i, _ in self.__columns
            if i.max_depth() is not None
        ])
        # if we have only one value, center it on a 2 meters range
        if self._min_z == self._max_z:
            self._min_z -= 1.0
            self._max_z += 1.0

    def _update_column_depths(self):
        for item, _ in self.__columns:
            item.set_min_depth(self._min_z)
            item.set_max_depth(self._max_z)
            item.update()

    def add_z_scale(self, title="Depth"):
        scale_item = ZScaleItem(self.DEFAULT_COLUMN_WIDTH / 2,
                                self.__log_scene.height(), self._min_z,
                                self._max_z)
        legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH / 2,
                                 title,
                                 unit_of_measure="m")
        self._add_column(scale_item, legend_item)

    def remove_data_column(self, data):
        """Remove data column from widget

        :param data: data to be removed
        """

        # Column doesn't exist
        if data not in self.__data2logitems:
            raise ValueError("Impossible to remove data column : given data"
                             " object doesn't exist")

        log_item, legend_item = self.__data2logitems[data]
        for i, (pitem, litem) in enumerate(self.__columns):
            if pitem == log_item and litem == legend_item:
                self.__columns.pop(i)
                self.__column_widths.pop(i)
                del self.__data2logitems[data]
                self.__log_scene.removeItem(log_item)
                self.__log_scene.removeItem(legend_item)
                return

        # Columns not found
        assert False

    def clear_data_columns(self):
        # remove item from scenes
        for (item, legend) in self.__columns:
            self.__log_scene.removeItem(legend)
            self.__log_scene.removeItem(item)

        # remove from internal lists
        self.__columns = []
        self.__column_widths = []
        self.__data2logitems = {}

        self.__selected_column = -1
        self._place_items()
        self._update_button_visibility()

    def on_plot_tooltip(self, txt, station_name=None):
        if station_name is not None:
            self.__status_bar.showMessage(
                u"Station: {} ".format(station_name) + txt)
        else:
            self.__status_bar.showMessage(txt)

    def add_data_column(self, data, title, uom, station_name=None):
        plot_item = PlotItem(size=QSizeF(self.DEFAULT_COLUMN_WIDTH,
                                         self.__log_scene.height()),
                             render_type=POLYGON_RENDERER,
                             x_orientation=ORIENTATION_DOWNWARD,
                             y_orientation=ORIENTATION_LEFT_TO_RIGHT)

        plot_item.set_layer(data.get_layer())
        plot_item.tooltipRequested.connect(
            lambda txt: self.on_plot_tooltip(txt, station_name))

        legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH,
                                 title,
                                 unit_of_measure=uom)
        data.data_modified.connect(
            lambda data=data: self._update_data_column(data))

        self.__data2logitems[data] = (plot_item, legend_item)
        self._add_column(plot_item, legend_item)
        self._update_data_column(data)
        self._update_column_depths()

    def _update_data_column(self, data):

        plot_item, legend_item = self.__data2logitems[data]

        y_values = data.get_y_values()
        x_values = data.get_x_values()
        if y_values is None or x_values is None:
            plot_item.set_data_window(None)
            return

        plot_item.set_data(data.get_x_values(), data.get_y_values())

        #        r = QRectF(0, min_y, (max_x-min_x)/delta, max_y)
        #        plot_item.set_data_window(r)

        # legend
        min_str = "{:.1f}".format(min(data.get_y_values()))
        max_str = "{:.1f}".format(max(data.get_y_values()))
        legend_item.set_scale(min_str, max_str)

        self.__log_scene.update()

    def add_stratigraphy(self, layer, column_mapping, title):
        item = StratigraphyItem(self.DEFAULT_COLUMN_WIDTH,
                                self.__log_scene.height(),
                                style_file=os.path.join(
                                    self.__style_dir,
                                    "stratigraphy_style.xml"))
        legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title)

        item.set_layer(layer)
        item.tooltipRequested.connect(self.on_plot_tooltip)

        item.set_data(
            [[f[c] if c is not None else None for c in column_mapping]
             for f in layer.getFeatures()])

        self._add_column(item, legend_item)

    def add_imagery(self, image_filename, title, depth_from, depth_to):
        item = ImageryDataItem(self.DEFAULT_COLUMN_WIDTH,
                               self.__log_scene.height(), image_filename,
                               depth_from, depth_to)

        legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title)

        self._add_column(item, legend_item)

    def select_column_at(self, pos):
        x = pos.x()
        c = 0
        selected = -1
        for i, width in enumerate(self.__column_widths):
            if x >= c and x < c + width:
                selected = i
                break
            c += width
        self.select_column(selected)

    def select_column(self, idx):
        self.__selected_column = idx
        for i, p in enumerate(self.__columns):
            item, legend = p
            item.set_selected(idx == i)
            legend.set_selected(idx == i)
            item.update()
            legend.update()

        self._update_button_visibility()

    def selected_column(self):
        return self.__selected_column

    def _update_button_visibility(self):
        idx = self.__selected_column
        self.__action_move_column_left.setEnabled(idx != -1 and idx > 0)
        self.__action_move_column_right.setEnabled(
            idx != -1 and idx < len(self.__columns) - 1)
        self.__action_edit_style.setEnabled(idx != -1)
        self.__action_remove_column.setEnabled(idx != -1)

    def on_move_column_left(self):
        if self.__selected_column < 1:
            return

        sel = self.__selected_column
        self.__columns[
            sel -
            1], self.__columns[sel] = self.__columns[sel], self.__columns[sel -
                                                                          1]
        self.__column_widths[sel - 1], self.__column_widths[
            sel] = self.__column_widths[sel], self.__column_widths[sel - 1]
        self.__selected_column -= 1
        self._place_items()
        self._update_button_visibility()

    def on_move_column_right(self):
        if self.__selected_column == -1 or self.__selected_column >= len(
                self.__columns) - 1:
            return

        sel = self.__selected_column
        self.__columns[
            sel +
            1], self.__columns[sel] = self.__columns[sel], self.__columns[sel +
                                                                          1]
        self.__column_widths[sel + 1], self.__column_widths[
            sel] = self.__column_widths[sel], self.__column_widths[sel + 1]
        self.__selected_column += 1
        self._place_items()
        self._update_button_visibility()

    def on_remove_column(self):
        if self.__selected_column == -1:
            return

        sel = self.__selected_column

        # remove item from scenes
        item, legend = self.__columns[sel]
        self.__log_scene.removeItem(legend)
        self.__log_scene.removeItem(item)

        # remove from internal list
        del self.__columns[sel]
        del self.__column_widths[sel]
        self.__selected_column = -1
        self._place_items()
        self._update_button_visibility()

    def on_edit_style(self):
        if self.__selected_column == -1:
            return

        item = self.__columns[self.__selected_column][0]
        item.edit_style()

    def on_add_column(self):
        # to be overridden by subclasses
        pass
Example #23
0
class XYZHubConnector(object):
    """base plugin"""
    def __init__(self, iface):
        """init"""
        import sys
        print(sys.version)
        self.iface = iface
        self.web_menu = "&XYZ Hub Connector"
        self.init_modules()
        self.obj = self

    def initGui(self):
        """startup"""

        parent = self.iface.mainWindow()

        ######## action, button

        icon = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/xyz.png"))
        icon_bbox = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/bbox.svg"))
        self.action_connect = QAction(icon, "XYZ Hub Connection", parent)
        self.action_connect.setWhatsThis(
            QCoreApplication.translate(PLUGIN_NAME, "WhatsThis message"))
        self.action_connect.setStatusTip(
            QCoreApplication.translate(PLUGIN_NAME, "status tip message"))

        self.action_magic_sync = QAction("Magic Sync (EXPERIMENTAL)", parent)

        if self.iface.activeLayer() is None:
            self.action_magic_sync.setEnabled(False)

        # self.action_magic_sync.setVisible(False) # disable magic sync

        ######## CONNECT action, button

        self.action_connect.triggered.connect(self.open_connection_dialog)
        self.action_magic_sync.triggered.connect(self.open_magic_sync_dialog)

        ######## Add the toolbar + button
        self.toolbar = self.iface.addToolBar(PLUGIN_NAME)
        self.toolbar.setObjectName("XYZ Hub Connector")

        self.actions = [self.action_connect]

        for a in self.actions:
            self.iface.addPluginToWebMenu(self.web_menu, a)

        # # uncomment to use menu button
        # tool_btn = QToolButton(self.toolbar)
        # for a in self.actions:
        #     tool_btn.addAction(a)
        # tool_btn.setDefaultAction(self.action_connect)
        # tool_btn.setPopupMode(tool_btn.MenuButtonPopup)
        # self.xyz_widget_action = self.toolbar.addWidget(tool_btn) # uncomment to use menu button

        self.toolbar.addAction(self.action_connect)

        self.action_help = None

        progress = QProgressBar()
        progress.setMinimum(0)
        progress.setMaximum(0)
        progress.reset()
        progress.hide()
        # progress = self.iface.statusBarIface().children()[2] # will be hidden by qgis
        self.iface.statusBarIface().addPermanentWidget(progress)
        self.pb = progress

    def init_modules(self):
        # util.init_module()

        # parent = self.iface.mainWindow()
        parent = QgsProject.instance()

        self.secret = Secret(config.USER_PLUGIN_DIR + "/secret.ini")
        ######## Init xyz modules
        self.map_basemap_meta = basemap.load_default_xml()
        self.auth_manager = AuthManager(config.USER_PLUGIN_DIR + "/auth.ini")

        self.token_model = GroupTokenModel(parent)
        # self.layer = LayerManager(parent, self.iface)

        self.network = NetManager(parent)

        self.con_man = LoaderManager()
        self.con_man.config(self.network)
        self.layer_man = LayerManager()

        ######## data flow
        # self.conn_info = SpaceConnectionInfo()

        ######## token
        self.token_model.load_ini(config.USER_PLUGIN_DIR + "/token.ini")

        ######## CALLBACK

        self.con_man.ld_pool.signal.progress.connect(
            self.cb_progress_busy)  #, Qt.QueuedConnection
        self.con_man.ld_pool.signal.finished.connect(self.cb_progress_done)

        QgsProject.instance().layersWillBeRemoved["QStringList"].connect(
            self.layer_man.remove)
        QgsProject.instance().layersWillBeRemoved["QStringList"].connect(
            self.con_man.remove)

        # self.iface.currentLayerChanged.connect( self.cb_layer_selected) # UNCOMMENT

        if LOG_TO_FILE:
            QgsApplication.messageLog().messageReceived.connect(cb_log_qgis)

    def unload_modules(self):
        # self.con_man.disconnect_ux( self.iface)
        QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect(
            self.layer_man.remove)
        QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect(
            self.con_man.remove)

        # utils.disconnect_silent(self.iface.currentLayerChanged)

        # self.iface.mapCanvas().extentsChanged.disconnect( self.debug_reload)

        self.secret.deactivate()

        # close_file_logger()
        pass

    def unload(self):
        """teardown"""
        self.unload_modules()
        # remove the plugin menu item and icon
        self.iface.removePluginWebMenu(self.web_menu, self.action_help)

        self.toolbar.clear(
        )  # remove action from custom toolbar (toolbar still exist)
        self.toolbar.deleteLater()

        for a in self.actions:
            self.iface.removePluginWebMenu(self.web_menu, a)

    ###############
    # Callback
    ###############
    def cb_layer_selected(self, qlayer):
        flag_xyz = True if qlayer is not None and self.layer.is_xyz_supported_layer(
            qlayer) else False
        # disable magic sync
        # self.action_magic_sync.setEnabled(flag_xyz)

    ###############
    # Callback of action (main function)
    ###############
    def cb_success_msg(self, msg, info=""):
        self.iface.messageBar().pushMessage(msg, info, Qgis.Success, 5)

    def make_cb_success(self, msg, info=""):
        def _cb_success_msg():
            txt = info
            self.cb_success_msg(msg, txt)

        return _cb_success_msg

    def make_cb_success_args(self, msg, info=""):
        def _cb_success_msg(args):
            a, kw = parse_qt_args(args)
            txt = ". ".join(map(str, a))
            self.cb_success_msg(msg, txt)

        return _cb_success_msg

    def cb_handle_error_msg(self, e):
        err = parse_exception_obj(e)
        if isinstance(err, ChainInterrupt):
            e0, idx = err.args[0:2]
            if isinstance(
                    e0,
                (net_handler.NetworkError, net_handler.NetworkTimeout)):
                ok = self.show_net_err(e0)
                if ok: return
            elif isinstance(e0, EmptyXYZSpaceError):
                ret = exec_warning_dialog(
                    "XYZ Hub", "Requested query returns no features")
                return
        self.show_err_msgbar(err)

    def show_net_err(self, err):
        reply_tag, status, reason, body, err_str, url = err.args[:6]
        if reply_tag in ["count", "statistics"]:  # too many error
            # msg = "Network Error: %s: %s. %s"%(status, reason, err_str)
            return 1

        detail = "\n".join(["Request:", url, "", "Response:", body])
        msg = ("%s: %s\n" % (status, reason) +
               "There was a problem connecting to the server")
        if status == 403:
            msg += "\n\n" + "Please make sure that the token has WRITE permission"
        ret = exec_warning_dialog("Network Error", msg, detail)
        return 1

    def show_err_msgbar(self, err):
        self.iface.messageBar().pushMessage(TAG_PLUGIN, repr(err),
                                            Qgis.Warning, 3)
        msg = format_traceback(err)
        QgsMessageLog.logMessage(msg, TAG_PLUGIN, Qgis.Warning)

    def cb_progress_busy(self, n_active):
        if n_active > 1: return
        self.flag_pb_show = True
        self.cb_progress_refresh()

    def cb_progress_done(self):
        self.flag_pb_show = False
        self.cb_progress_refresh()

    def cb_progress_refresh(self):
        if not hasattr(self, "flag_pb_show"): return

        pb = self.pb
        if self.flag_pb_show:
            pb.show()
            # print_qgis("show",pb)
        else:
            pb.hide()
            # print_qgis("hide")

    ###############
    # Action (main function)
    ###############
    # unused
    def load_bbox(self, con, args):
        bbox = bbox_utils.extend_to_bbox(
            bbox_utils.get_bounding_box(self.iface))
        a, kw = parse_qt_args(args)
        kw["bbox"] = bbox
        kw["limit"] = 1000
        con.start(*a, **kw)

    # UNUSED
    def refresh_canvas(self):
        # self.iface.activeLayer().triggerRepaint()
        self.iface.mapCanvas().refresh()

    def previous_canvas_extent(self):
        self.iface.mapCanvas().zoomToPreviousExtent()

    #

    def new_main_dialog(self):
        parent = self.iface.mainWindow()
        dialog = MainDialog(parent)

        dialog.config(self.token_model)
        dialog.config_secret(self.secret)
        auth = self.auth_manager.get_auth()
        dialog.config_basemap(self.map_basemap_meta, auth)

        con = self.con_man.make_con("create")
        con.signal.finished.connect(
            dialog.btn_use.clicked.emit)  # can be optimized !!
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("list")
        con.signal.results.connect(make_fun_args(dialog.cb_display_spaces))
        con.signal.error.connect(self.cb_handle_error_msg)
        con.signal.error.connect(lambda e: dialog.cb_enable_token_ui())
        con.signal.finished.connect(dialog.cb_enable_token_ui)

        con = self.con_man.make_con("edit")
        con.signal.finished.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("delete")
        con.signal.results.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("stat")
        con.signal.results.connect(make_fun_args(
            dialog.cb_display_space_count))
        con.signal.error.connect(self.cb_handle_error_msg)

        ############ clear cache btn
        dialog.signal_clear_cache.connect(self.open_clear_cache_dialog)

        ############ add map tile btn
        dialog.signal_add_basemap.connect(self.add_basemap_layer)

        ############ btn: new, edit, delete space
        dialog.signal_new_space.connect(self.start_new_space)
        dialog.signal_edit_space.connect(self.start_edit_space)
        dialog.signal_del_space.connect(self.start_delete_space)

        ############ Use Token btn
        dialog.signal_use_token.connect(lambda a: self.con_man.finish_fast())
        dialog.signal_use_token.connect(self.start_use_token)

        ############ get count
        dialog.signal_space_count.connect(
            self.start_count_feat,
            Qt.QueuedConnection)  # queued -> non-blocking ui

        ############ connect btn
        dialog.signal_space_connect.connect(self.start_load_layer)

        ############ upload btn
        dialog.signal_upload_space.connect(self.start_upload_space)

        return dialog

    def start_new_space(self, args):
        con = self.con_man.get_con("create")
        con.start_args(args)

    def start_edit_space(self, args):
        con = self.con_man.get_con("edit")
        con.start_args(args)

    def start_delete_space(self, args):
        con = self.con_man.get_con("delete")
        con.start_args(args)

    def start_use_token(self, args):
        con = self.con_man.get_con("list")
        con.start_args(args)

    def start_count_feat(self, args):
        con = self.con_man.get_con("stat")
        con.start_args(args)

    def start_upload_space(self, args):
        con_upload = UploadLayerController(self.network, n_parallel=2)
        self.con_man.add_background(con_upload)
        # con_upload.signal.finished.connect( self.make_cb_success("Uploading finish") )
        con_upload.signal.results.connect(
            self.make_cb_success_args("Uploading finish"))
        con_upload.signal.error.connect(self.cb_handle_error_msg)

        con = InitUploadLayerController(self.network)
        self.con_man.add_background(con)

        con.signal.results.connect(con_upload.start_args)
        con.signal.error.connect(self.cb_handle_error_msg)

        con.start_args(args)

    def start_load_layer(self, args):
        # create new con
        # config
        # run

        ############ connect btn
        con_load = LoadLayerController(self.network, n_parallel=1)
        self.con_man.add_background(con_load)
        # con_load.signal.finished.connect( self.make_cb_success("Loading finish") )
        con_load.signal.results.connect(
            self.make_cb_success_args("Loading finish"))
        # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_load.signal.error.connect(self.cb_handle_error_msg)

        con_load.start_args(args)

        # con.signal.results.connect( self.layer_man.add_args) # IMPORTANT

    def add_basemap_layer(self, args):
        a, kw = parse_qt_args(args)
        meta, app_id, app_code = a
        self.auth_manager.save(app_id, app_code)
        basemap.add_basemap_layer(meta, app_id, app_code)

    ###############
    # Open dialog
    ###############
    def open_clear_cache_dialog(self):
        dialog = ConfirmDialog(
            "Delete cache will make loaded layer unusable !!")
        ret = dialog.exec_()
        if ret != dialog.Ok: return

        utils.clear_cache()

    def open_connection_dialog(self):
        dialog = self.new_main_dialog()

        vlayer = self.iface.activeLayer()
        dialog.set_layer(vlayer)

        dialog.exec_()
        self.con_man.finish_fast()
        # self.startTime = time.time()

    # not used
    def open_magic_sync_dialog(self):
        pass
Example #24
0
class MessageViewer(QtWebKitWidgets.QWebView):

    """A simple message queue."""

    static_message_count = 0

    # noinspection PyOldStyleClasses
    def __init__(self, the_parent):
        _ = the_parent  # NOQA needed for promoted Qt widget in designer
        super(MessageViewer, self).__init__()
        self.setWindowTitle('Message Viewer')
        # We use this var to keep track of the last allocated div id
        # in cases where we are assigning divs ids so we can scroll to them
        self.last_id = 0

        # whether to show or not dev only options
        self.dev_mode = setting('developer_mode', False, expected_type=bool)

        if self.dev_mode:
            self.settings().globalSettings().setAttribute(
                QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)

        # Always gets replaced when a new message is passed
        self.static_message = None
        # Always get appended until the next static message is called,
        # then cleared
        self.dynamic_messages = []
        self.dynamic_messages_log = []
        # self.show()

        self.action_show_log = QAction(self.tr('Show log'), None)
        self.action_show_log.setEnabled(False)
        # noinspection PyUnresolvedReferences
        self.action_show_log.triggered.connect(self.show_log)

        self.action_show_report = QAction(self.tr('Show report'), None)
        self.action_show_report.setEnabled(False)
        # noinspection PyUnresolvedReferences
        self.action_show_report.triggered.connect(self.show_report)

        self.action_print_to_pdf = QAction(self.tr('Save as PDF'), None)
        self.action_print_to_pdf.setEnabled(True)
        self.action_print_to_pdf.triggered.connect(self.generate_pdf)

        self.log_path = None
        self.report_path = None
        self._impact_path = None

        self._html_loaded_flag = False
        # noinspection PyUnresolvedReferences
        self.loadFinished.connect(self.html_loaded_slot)

    @property
    def impact_path(self):
        """Getter to impact path."""
        return self._impact_path

    @impact_path.setter
    def impact_path(self, value):
        """Setter to impact path.

        :param value: The impact path.
        :type value: str
        """
        self._impact_path = value
        if value is None:
            self.action_show_report.setEnabled(False)
            self.action_show_log.setEnabled(False)
            self.report_path = None
            self.log_path = None
        else:
            self.action_show_report.setEnabled(True)
            self.action_show_log.setEnabled(True)
            self.log_path = '%s.log.html' % self.impact_path
            self.report_path = '%s.report.html' % self.impact_path

        self.save_report_to_html()
        self.save_log_to_html()
        self.show_report()

    def contextMenuEvent(self, event):
        """Slot automatically called by Qt on right click on the WebView.

        :param event: the event that caused the context menu to be called.
        """

        context_menu = QMenu(self)

        # add select all
        action_select_all = self.page().action(
            QtWebKitWidgets.QWebPage.SelectAll
        )
        action_select_all.setEnabled(not self.page_to_text() == '')
        context_menu.addAction(action_select_all)

        # add copy
        action_copy = self.page().action(QtWebKitWidgets.QWebPage.Copy)
        if qt_at_least('4.8.0'):
            action_copy.setEnabled(not self.selectedHtml() == '')
        else:
            action_copy.setEnabled(not self.selectedText() == '')
        context_menu.addAction(action_copy)

        # add show in browser
        action_page_to_html_file = QAction(
            self.tr('Open in web browser'), None)
        # noinspection PyUnresolvedReferences
        action_page_to_html_file.triggered.connect(
            self.open_current_in_browser)
        context_menu.addAction(action_page_to_html_file)

        # Add the PDF export menu
        context_menu.addAction(self.action_print_to_pdf)

        # add load report
        context_menu.addAction(self.action_show_report)

        # add load log
        context_menu.addAction(self.action_show_log)

        # add view source if in dev mode
        if self.dev_mode:
            action_copy = self.page().action(
                QtWebKitWidgets.QWebPage.InspectElement
            )
            action_copy.setEnabled(True)
            context_menu.addAction(action_copy)

            # add view to_text if in dev mode
            action_page_to_stdout = QAction(self.tr('log pageToText'),
                                            None)
            # noinspection PyUnresolvedReferences
            action_page_to_stdout.triggered.connect(self.page_to_stdout)
            context_menu.addAction(action_page_to_stdout)

        # show the menu
        context_menu.setVisible(True)
        context_menu.exec_(event.globalPos())

    def static_message_event(self, sender, message):
        """Static message event handler - set message state based on event.

        Static message events will clear the message buffer before displaying
        themselves.

        :param sender: Unused - the object that sent the message.
        :type sender: Object, None

        :param message: A message to show in the viewer.
        :type message: safe.messaging.message.Message
        """

        self.static_message_count += 1

        if message == self.static_message:
            return
        # LOGGER.debug('Static message event %i' % self.static_message_count)
        _ = sender  # NOQA
        self.dynamic_messages = []
        self.static_message = message
        self.show_messages()

    def error_message_event(self, sender, message):
        """Error message event handler - set message state based on event.

        Error messages are treated as dynamic messages - they don't clear the
        message buffer.

        :param sender: The object that sent the message.
        :type sender: Object, None

        :param message: A message to show in the viewer.
        :type message: safe.messaging.Message
        """
        # LOGGER.debug('Error message event')
        self.dynamic_message_event(sender, message)

    def dynamic_message_event(self, sender, message):
        """Dynamic event handler - set message state based on event.

        Dynamic messages don't clear the message buffer.

        :param sender: Unused - the object that sent the message.
        :type sender: Object, None

        :param message: A message to show in the viewer.
        :type message: safe.messaging.Message
        """
        # LOGGER.debug('Dynamic message event')
        _ = sender  # NOQA
        self.dynamic_messages.append(message)
        self.dynamic_messages_log.append(message)
        # Old way (works but causes full page refresh)
        self.show_messages()
        return

        # New way add html snippet to end of page, not currently working
        # self.last_id += 1
        # message.element_id = str(self.last_id)
        # # TODO probably we should do some escaping of quotes etc in message
        # html = message.to_html(in_div_flag=True)
        # html = html.replace('\'', '\\\'')
        # # We could run into side effect still if messages contain single
        # # quotes
        # LOGGER.debug('HTML: %s' % html)
        # js = 'document.body.innerHTML += \'%s\'' % html
        # LOGGER.debug('JAVASCRIPT: %s' % js)
        # self.page().mainFrame().evaluateJavaScript(js)
        # self.scrollToDiv()

    def clear_dynamic_messages_log(self):
        """Clear dynamic message log."""
        self.dynamic_messages_log = []

    def show_messages(self):
        """Show all messages."""
        if isinstance(self.static_message, MessageElement):
            # Handle sent Message instance
            string = html_header()
            if self.static_message is not None:
                string += self.static_message.to_html()

            # Keep track of the last ID we had so we can scroll to it
            self.last_id = 0
            for message in self.dynamic_messages:
                if message.element_id is None:
                    self.last_id += 1
                    message.element_id = str(self.last_id)

                html = message.to_html(in_div_flag=True)
                if html is not None:
                    string += html

            string += html_footer()
        elif (isinstance(self.static_message, str)):
            # Handle sent text directly
            string = self.static_message
        elif self.static_message is not None:
            string = str(self.static_message)
        elif not self.static_message:
            # handle dynamic message
            # Handle sent Message instance
            string = html_header()

            # Keep track of the last ID we had so we can scroll to it
            self.last_id = 0
            for message in self.dynamic_messages:
                if message.element_id is None:
                    self.last_id += 1
                    message.element_id = str(self.last_id)

                html = message.to_html(in_div_flag=True)
                if html is not None:
                    string += html

            string += html_footer()

        # Set HTML
        self.load_html(HTML_STR_MODE, string)

    def to_message(self):
        """Collate all message elements to a single message."""
        my_message = m.Message()
        if self.static_message is not None:
            my_message.add(self.static_message)
        for myDynamic in self.dynamic_messages:
            my_message.add(myDynamic)
        return my_message

    def page_to_text(self):
        """Return the current page contents as plain text."""
        my_message = self.to_message()
        return my_message.to_text()

    def page_to_html(self):
        """Return the current page contents as html."""
        my_message = self.to_message()
        return my_message.to_html()

    def page_to_stdout(self):
        """Print to console the current page contents as plain text."""
        print((self.page_to_text()))

    def save_report_to_html(self):
        """Save report in the dock to html."""
        html = self.page().mainFrame().toHtml()
        if self.report_path is not None:
            html_to_file(html, self.report_path)
        else:
            msg = self.tr('report_path is not set')
            raise InvalidParameterError(msg)

    def save_log_to_html(self):
        """Helper to write the log out as an html file."""
        html = html_header()
        html += (
            '<img src="file:///%s/img/logos/inasafe-logo-url.png" '
            'title="InaSAFE Logo" alt="InaSAFE Logo" />' % resources_path())
        html += ('<h5 class="info"><i class="icon-info-sign icon-white"></i> '
                 '%s</h5>' % self.tr('Analysis log'))
        for item in self.dynamic_messages_log:
            html += "%s\n" % item.to_html()
        html += html_footer()
        if self.log_path is not None:
            html_to_file(html, self.log_path)
        else:
            msg = self.tr('log_path is not set')
            raise InvalidParameterError(msg)

    def show_report(self):
        """Show report."""
        self.action_show_report.setEnabled(False)
        self.action_show_log.setEnabled(True)
        self.load_html_file(self.report_path)

    def show_log(self):
        """Show log."""
        self.action_show_report.setEnabled(True)
        self.action_show_log.setEnabled(False)
        self.load_html_file(self.log_path)

    def open_current_in_browser(self):
        """Open current selected impact report in browser."""
        if self.impact_path is None:
            html = self.page().mainFrame().toHtml()
            html_to_file(html, open_browser=True)
        else:
            if self.action_show_report.isEnabled():
                # if show report is enable, we are looking at a log
                open_in_browser(self.log_path)
            else:
                open_in_browser(self.report_path)

    def generate_pdf(self):
        """Generate a PDF from the displayed content."""
        printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution)
        printer.setPageSize(QtGui.QPrinter.A4)
        printer.setColorMode(QtGui.QPrinter.Color)
        printer.setOutputFormat(QtGui.QPrinter.PdfFormat)
        report_path = unique_filename(suffix='.pdf')
        printer.setOutputFileName(report_path)
        self.print_(printer)
        url = QtCore.QUrl.fromLocalFile(report_path)
        # noinspection PyTypeChecker,PyCallByClass,PyArgumentList
        QtGui.QDesktopServices.openUrl(url)

    def load_html_file(self, file_path):
        """Load html file into webkit.

        :param file_path: The path of the html file
        :type file_path: str
        """
        self.load_html(HTML_FILE_MODE, file_path)

    def load_html(self, mode, html):
        """Load HTML to this class with the mode specified.

        There are two modes that can be used:
            * HTML_FILE_MODE: Directly from a local HTML file.
            * HTML_STR_MODE: From a valid HTML string.

        :param mode: The mode.
        :type mode: int

        :param html: The html that will be loaded. If the mode is a file,
            then it should be a path to the htm lfile. If the mode is a string,
            then it should be a valid HTML string.
        :type html: str
        """
        # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
        self._html_loaded_flag = False

        if mode == HTML_FILE_MODE:
            self.setUrl(QtCore.QUrl.fromLocalFile(html))
        elif mode == HTML_STR_MODE:
            self.setHtml(html)
        else:
            raise InvalidParameterError('The mode is not supported.')

        counter = 0
        sleep_period = 0.1  # sec
        timeout = 20  # it's generous enough!
        while not self._html_loaded_flag and counter < timeout:
            # Block until the event loop is done
            counter += sleep_period
            time.sleep(sleep_period)
            # noinspection PyArgumentList
            QgsApplication.processEvents()

    def html_loaded_slot(self, ok):
        """Slot called when the page is loaded.

        :param ok: Flag indicating if the html is loaded.
        :type ok: bool
        """
        self._html_loaded_flag = ok
Example #25
0
    def add_action(self,
                   name,
                   icon_path,
                   text,
                   callback,
                   toggle_flag=False,
                   enabled_flag=True,
                   checkable_flag=False,
                   visible_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 name: Objectname of the action. Serves also as key for the stored actions.
        :type name: str

        :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 toggle_flag: A flag indicating if the action should connect
            the toggled or triggered signal by default.
            Defaults to triggered (False)
        :type toggle_flag: bool

        :param checkable_flag: A flag indicating if the action should be checkable
            by default. Defaults to False.
        :type checkable: bool

        :param visible_flag: A flag indicating if the action should be displayed
            by default. Defaults to True.
        :type visible: 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
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.setObjectName(name)
        if toggle_flag:
            action.toggled.connect(callback)
        else:
            action.triggered.connect(callback)
        action.setEnabled(enabled_flag)
        action.setCheckable(checkable_flag)
        action.setVisible(visible_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[name] = action

        return action
Example #26
0
class InfoDock(infodock_widget, QWidget):
    featureupdated = pyqtSignal(object, object, list)
    resultscleared = pyqtSignal()
    activeLayerChanged = pyqtSignal(object)

    def __init__(self, parent):
        super(InfoDock, self).__init__(parent)
        self.setupUi(self)
        # TODO Doesn't currently work with webview. Loop back and fix this.
        install_touch_scroll(self.attributesView)

        self.forms = {}

        self.layerList.currentRowChanged.connect(self.layerIndexChanged)
        self.attributesView.linkClicked.connect(self.handle_link)
        self.attributesView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
        action = self.attributesView.pageAction(QWebPage.Copy)
        action.setShortcut(QKeySequence.Copy)
        self.grabGesture(Qt.SwipeGesture)
        self.setAttribute(Qt.WA_AcceptTouchEvents)
        self.editButton.pressed.connect(self.openform)
        self.editGeomButton.pressed.connect(self.editgeom)
        self.parent().installEventFilter(self)
        self.project = None
        self.startwidth = self.width()
        self.expaned = False
        self.layerList.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.expandAction = QAction(QIcon(":/icons/expand"), "Expand Panel", self)
        self.expandAction.triggered.connect(self.change_expanded_state)

        self.navigateAction = QAction(QIcon(":/icons/navigate"), "Navigate To..", self)
        self.navigateAction.triggered.connect(self._navigate_to_selection)

        self.moreActionsButton.pressed.connect(self._show_more_actions)
        self.navwidget.mousePressEvent = self._sink
        self.bottomWidget.mousePressEvent = self._sink
        self.navwidget.mouseReleaseEvent = self._sink
        self.bottomWidget.mouseReleaseEvent = self._sink
        self.navwidget.mouseMoveEvent = self._sink
        self.bottomWidget.mouseMoveEvent = self._sink
        self.deleteFeatureButton.pressed.connect(self.delete_feature)
        self.deleteFeatureButton.setCheckable(False)

        self.quickInspectButton.hide()
        self.quickInspectButton.pressed.connect(self.quick_inspect_feature)

        self.nextButton.pressed.connect(self.pagenext)
        self.prevButton.pressed.connect(self.pageback)

        RoamEvents.selectioncleared.connect(self.clearResults)
        RoamEvents.editgeometry_complete.connect(self.refreshcurrent)
        RoamEvents.editgeometry_invalid.connect(self.refreshcurrent)

    def _navigate_to_selection(self) -> None:
        """
        Set the GPS waypoint to the active feature
        """
        feature = self.selection.feature
        geom = feature.geometry()
        point = geom.centroid().asPoint()
        if GPS.waypoint == point:
            GPS.waypoint = None
        else:
            GPS.waypoint = point

    def _show_more_actions(self) -> None:
        """
        Show the more actions selector dialog.
        """
        dlg = PickActionDialog()
        self.navigateAction.setEnabled(GPS.isConnected)
        if not GPS.isConnected:
            self.navigateAction.setText("Navigate To.. (No GPS)")
        else:
            self.navigateAction.setText("Navigate To..")

        dlg.addactions([self.expandAction, self.navigateAction])
        dlg.exec_()

    def delete_feature(self) -> None:
        """
        Trigger the feature delete logic.
        Doesn't delete the feature here just fires the event to delete the feature.
        """
        cursor = self.selection
        RoamEvents.delete_feature(cursor.form, cursor.feature)

    def handle_link(self, url) -> None:
        """
        Handle any links that are fired
        :param url: The url that is clicked in the info dock
        :return:
        """
        if url.toString().endswith("/back"):
            self.pageback()
        elif url.toString().endswith("/next"):
            self.pagenext()
        else:
            RoamEvents.openurl.emit(url)

    def _sink(self, event) -> None:
        """
        Empty event sink to do nothing with the event.
        """
        return

    def change_expanded_state(self) -> None:
        """
        Expand or collapse the info panel view
        """
        if self.expaned:
            self._collapse()
        else:
            self._expand()

    def mousePressEvent(self, event):
        pos = self.mapToParent(event.pos())
        newevent = QMouseEvent(event.type(), pos, event.button(), event.buttons(), event.modifiers())
        self.parent().mousePressEvent(newevent)

    def mouseReleaseEvent(self, event):
        pos = self.mapToParent(event.pos())
        newevent = QMouseEvent(event.type(), pos, event.button(), event.buttons(), event.modifiers())
        self.parent().mouseReleaseEvent(newevent)

    def mouseMoveEvent(self, event):
        pos = self.mapToParent(event.pos())
        newevent = QMouseEvent(event.type(), pos, event.button(), event.buttons(), event.modifiers())
        self.parent().mouseMoveEvent(newevent)

    def _expand(self) -> None:
        """
        Expand the info panel.
        """
        self.resize(self.parent().width() - 10, self.parent().height())
        self.move(10, 0)
        self.expaned = True
        self.expandAction.setText("Collapse Panel")

    def _collapse(self) -> None:
        """
        Collapse the info panel back to the samller state
        :return:
        """
        self.resize(self.startwidth, self.parent().height())
        self.move(self.parent().width() - self.startwidth, 0)
        self.expaned = False
        self.expandAction.setText("Expand Panel")

    def eventFilter(self, object, event):
        if event.type() == QEvent.Resize:
            self._collapse()

        return super(InfoDock, self).eventFilter(object, event)

    def close(self):
        """
        Close the info panel dock.
        :return:
        """
        RoamEvents.selectioncleared.emit()
        super(InfoDock, self).close()

    def event(self, event):
        if event.type() == QEvent.Gesture:
            gesture = event.gesture(Qt.SwipeGesture)
            if gesture:
                self.pagenext()
        return QWidget.event(self, event)

    @property
    def selection(self):
        """
        Return the active selection in the info panel.
        :return:
        """
        item = self.layerList.item(self.layerList.currentRow())
        if not item:
            return

        cursor = item.data(Qt.UserRole)
        return cursor

    def openform(self):
        """
        Fire the open feature form event to open the form for the current feature.
        :return:
        """
        cursor = self.selection
        tools = self.project.layer_tools(cursor.layer)
        if 'inspection' in tools:
            config = tools['inspection']
            form, feature = self.get_inspection_config(cursor.feature, config)
            editmode = False
        else:
            form = cursor.form
            feature = cursor.feature
            editmode = True

        RoamEvents.load_feature_form(form, feature, editmode)

    def quick_inspect_feature(self):
        """
        Quick inspect the current feature
        """
        cursor = self.selection
        tools = self.project.layer_tools(cursor.layer)
        config = tools['inspection']
        form, feature = self.get_inspection_config(cursor.feature, config)
        editmode = False
        form.suppressform = True
        RoamEvents.load_feature_form(form, feature, editmode)
        # Leaking state is leaking.  But this is what we have for now.
        form.suppressform = False

    def get_inspection_config(self, current_feature, config):
        """
        Returns the inspection form and a copy of the feature for the new form.
        :param current_feature: The current feature to be copied
        :param config: The tool config
        :return:
        """
        form = config['form']
        newform = self.project.form_by_name(form)
        if config.get('mode', "copy").lower() == 'copy':
            geom = current_feature.geometry()
            mappings = config.get('field_mapping', {})
            data = {}
            for fieldfrom, fieldto in mappings.items():
                data[fieldto] = current_feature[fieldfrom]

            newgeom = QgsGeometry(geom)
            newfeature = newform.new_feature(geometry=newgeom, data=data)

            return newform, newfeature
        else:
            raise NotImplementedError("Only copy mode supported currently")

    def editgeom(self) -> None:
        """
        Trigger the event to edit the geometry of the active feature.
        """
        cursor = self.selection
        RoamEvents.editgeometry.emit(cursor.form, cursor.feature)
        self.editGeomButton.setEnabled(False)
        self.deleteFeatureButton.setEnabled(False)

    def pageback(self) -> None:
        """
        Go back a page
        :return:
        """
        cursor = self.selection
        cursor.back()
        self.update(cursor)

    def pagenext(self) -> None:
        """
        Go to the next page.
        :return:
        """
        cursor = self.selection
        cursor.next()
        self.update(cursor)

    def layerIndexChanged(self, index) -> None:
        """
        Called when the selected layer item changes.

        Updates the panel with layer selection.
        :param index: The new index of the selected
        """
        item = self.layerList.item(index)
        if not item:
            return

        cursor = item.data(Qt.UserRole)
        self.update(cursor)
        self.activeLayerChanged.emit(cursor.layer)

    def setResults(self, results, forms, project) -> None:
        """
        Set the results for the info panel.
        :param results: Dict of layer and features for the selection.
        :param forms: The forms from the project.
        :param project: The active project.
        """
        lastrow = self.layerList.currentRow()
        if lastrow == -1:
            lastrow = 0

        self.clearResults()
        self.forms = forms
        self.project = project

        for layer, features in results.items():
            if features:
                self._addResult(layer, features)

        self.layerList.setCurrentRow(lastrow)
        self.layerList.setMinimumWidth(self.layerList.sizeHintForColumn(0) + 20)
        size = 0
        for n in range(self.layerList.count()):
            size += self.layerList.sizeHintForRow(n)
        self.layerList.setMinimumHeight(size)
        self.layerList.setMaximumHeight(size)
        self.navwidget.show()

    def show(self) -> None:
        """
        Show or hide the layer panel based on the results count.
        """
        if self.layerList.count() > 0:
            super(InfoDock, self).show()
        else:
            self.hide()

    def _addResult(self, layer, features):
        layername = layer.name()
        forms = self.forms.get(layername, [])
        if not forms:
            item = QListWidgetItem(QIcon(), layername, self.layerList)
            item.setData(Qt.UserRole, FeatureCursor(layer, features))
            return

        for form in forms:
            selectname = self.project.selectlayer_name(form.layername)
            if selectname == layername:
                itemtext = "{} \n ({})".format(layername, form.label)
            else:
                itemtext = selectname
            icon = QIcon(form.icon)
            item = QListWidgetItem(icon, itemtext, self.layerList)
            item.setData(Qt.UserRole, FeatureCursor(layer, features, form))

    def refreshcurrent(self) -> None:
        """
        Refresh the dock with the updated selection.
        """
        self.update(self.selection)

    def update(self, cursor) -> None:
        """
        Update the data in the dock with the given cursor data.
        :param cursor: The cursor holding a pointer to the data and feature for the active feature.
        """
        if cursor is None:
            return

        try:
            feature = cursor.feature
        except NoFeature as ex:
            utils.exception(ex)
            return

        form = cursor.form
        layer = cursor.layer

        clear_image_cache()

        self.countLabel.setText(str(cursor))

        info1, results = self.generate_info("info1", self.project, layer, feature.id(), feature, countlabel=str(cursor))
        info2, _ = self.generate_info("info2", self.project, layer, feature.id(), feature, lastresults=results[0])

        if form:
            name = "{}".format(layer.name(), form.label)
        else:
            name = layer.name()

        info = dict(TITLE=name,
                    INFO1=info1,
                    INFO2=info2)

        html = updateTemplate(info, infotemplate)

        self.attributesView.setHtml(html, templates.baseurl)
        tools = self.project.layer_tools(layer)
        hasform = form is not None
        editattributes = ('edit_attributes' in tools or 'inspection' in tools) and hasform
        editgeom = 'edit_geom' in tools and hasform
        deletefeature = 'delete' in tools and hasform
        self.deleteFeatureButton.setVisible(deletefeature)
        self.quickInspectButton.setVisible('inspection' in tools)
        self.editButton.setVisible(editattributes)
        feature = cursor.feature
        self.editGeomButton.setVisible(editgeom)
        self.editGeomButton.setEnabled(True)
        self.featureupdated.emit(layer, feature, cursor.features)

    def generate_info(self, infoblock, project, layer, mapkey, feature, countlabel=None, lastresults=None):
        """
        Generate a info block for the display.
        :param infoblock: The info block name to generate.
        :param project: The active Roam project.
        :param layer: The active layer.
        :param mapkey: The current map key of the selected feature.  Normally just the primary key column from QGIS.
        :param feature: The selected feature.
        :param countlabel: The label to use as the count header.
        :param lastresults: The results from another info block. Normally info1 passed to info2.
        :returns:
        """
        infoblockdef = project.info_query(infoblock, layer.name())
        isinfo1 = infoblock == "info1"

        if not infoblockdef:
            if isinfo1:
                infoblockdef = {}
                infoblockdef['type'] = 'feature'
            else:
                return None, []

        if isinfo1:
            caption = infoblockdef.get('caption', "Record")
        else:
            caption = infoblockdef.get('caption', "Related Record")

        results = []
        error = None
        infotype = infoblockdef.get('type', 'feature')
        if infotype == 'sql':
            try:
                queryresults = self.results_from_query(infoblockdef, layer, feature, mapkey, lastresults=lastresults)
                if isinfo1 and not queryresults:
                    # If there is no results from the query and we are a info 1 block we grab from the feature.
                    results.append(results_from_feature(feature))
                else:
                    results = queryresults
            except database.DatabaseException as ex:
                RoamEvents.raisemessage("Query Error", ex.message, 3)
                utils.error(ex.message)
                if not isinfo1:
                    error = "<b> Error: {} <b>".format(ex.msg)
                else:
                    results.append(results_from_feature(feature))

        elif infotype == 'feature':
            featuredata = results_from_feature(feature)
            excludedfields = infoblockdef.get('hidden', [])
            for field in excludedfields:
                try:
                    del featuredata[field]
                except KeyError:
                    pass
            results.append(featuredata)
        else:
            return None, []

        blocks = []
        for count, result in enumerate(results, start=1):
            if isinfo1 and count == 1:
                countblock = countblocktemplate.substitute(count=countlabel)
            else:
                countblock = ''

            fields = result.keys()
            attributes = result.values()
            rows = create_data_html(fields, attributes, imagepath=self.project.image_folder)
            try:
                caption = caption.format(**dict(zip(fields, attributes)))
            except KeyError:
                pass

            blocks.append(updateTemplate(dict(ROWS=rows,
                                              HEADER=caption,
                                              CONTROLS=countblock),
                                         infoblocktemplate))
        if error:
            return error, []

        return '<br>'.join(blocks), results

    def results_from_query(self, infoblockdef, layer, feature, mapkey, lastresults=None) -> list:
        """
        Return the resutls from running a database query to get the feature results.
        :param infoblockdef: The info block project config section.
        :param layer: The QgsVectorLayer to get the connection from.
        :param feature: The feature to pull the map key from.
        :param mapkey: The mapkey to use if not set in the info block config or found in the last results.
        :param lastresults: Results of another info results block. Normally info 1
        :return: List of query results from running the query on the layer.
        """
        def get_key() -> str:
            try:
                keycolumn = infoblockdef['mapping']['mapkey']
                if keycolumn == 'from_info1':
                    if 'mapkey' in lastresults:
                        return lastresults['mapkey']
                    else:
                        # TODO Umm wat? Why is this returning a list?
                        return []
                else:
                    return feature[keycolumn]
            except KeyError:
                return mapkey

        def get_layer() -> str:
            connection = infoblockdef.get('connection', "from_layer")
            if isinstance(connection, dict):
                return layer_by_name(connection['layer'])
            elif connection == "from_layer":
                return layer
            else:
                raise NotImplementedError("{} is not a supported connection type".format(connection))

        if not lastresults:
            lastresults = {}

        sql = infoblockdef['query']
        layer = get_layer()

        db = database.Database.fromLayer(layer)
        mapkey = get_key()
        attributes = values_from_feature(feature, safe_names=True)
        attributes['mapkey'] = mapkey
        # Run the SQL text though the QGIS expression engine first.
        sql = roam.api.utils.replace_expression_placeholders(sql, feature)
        results = db.query(sql, **attributes)
        results = list(results)
        return results

    def clearResults(self) -> None:
        """
        Clear the results in the info panel.
        """
        self.layerList.clear()
        self.attributesView.setHtml('')
        self.editButton.setVisible(False)
        self.editGeomButton.setEnabled(True)
        self.editButton.setEnabled(True)
        self.deleteFeatureButton.setEnabled(True)
        self.navwidget.hide()
class QgepProfileDockWidget(QDockWidget, DOCK_WIDGET_UI):
    # Signal emitted when the widget is closed
    closed = pyqtSignal()
    canvas = None
    addDockWidget = None
    # Lookup table for vertical exaggeration values
    veLUT = {
        1: 1,
        2: 2,
        3: 3,
        4: 5,
        5: 10,
        6: 20,
        7: 30,
        8: 50,
        9: 100,
        10: 500
    }

    def __init__(self, parent, canvas, add_dock_widget):
        QDockWidget.__init__(self, parent)
        self.setupUi(self)

        self.selectCurrentPathAction = QAction(self.tr('Select current path'), self.selectButton)
        self.selectCurrentPathAction.triggered.connect(self.onSelectCurrentPathAction)
        self.selectButton.setDefaultAction(self.selectCurrentPathAction)
        self.configureSelectionAction = QAction(self.tr('Configure Select'), self.selectButton)
        self.configureSelectionAction.triggered.connect(self.onConfigureSelectAction)
        self.selectButton.addAction(self.configureSelectionAction)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.canvas = canvas
        self.addDockWidget = add_dock_widget

    def showIt(self):
        # self.setLocation( Qt.BottomDockWidgetArea )
        self.location = Qt.BottomDockWidgetArea
        minsize = self.minimumSize()
        maxsize = self.maximumSize()
        self.setMinimumSize(minsize)
        self.setMaximumSize(maxsize)
        self.canvas.setRenderFlag(False)

        self.addDockWidget(self.location, self)
        self.canvas.setRenderFlag(True)

        self.printButton.clicked.connect(self.onPrintButtonClicked)

        self.mSliderVerticalExaggeration.valueChanged.connect(self.onVerticalExaggerationChanged)

    def closeEvent(self, event):
        self.closed.emit()
        return QDockWidget.closeEvent(self, event)

    def addPlotWidget(self, plot_widget):
        self.plotWidget = plot_widget
        self.verticalLayoutForPlot.addWidget(self.plotWidget)
        ve_val = self.veLUT[self.mSliderVerticalExaggeration.value()]
        self.plotWidget.changeVerticalExaggeration(ve_val)

    @pyqtSlot(int)
    def onVerticalExaggerationChanged(self, value):
        ve_val = self.veLUT[value]
        self.mLblVerticalExaggeration.setText(str(ve_val) + 'x')
        self.plotWidget.changeVerticalExaggeration(ve_val)

    @pyqtSlot()
    def onPrintButtonClicked(self):
        self.plotWidget.printProfile()

    @pyqtSlot()
    def onConfigureSelectAction(self):
        dlg = QDialog()
        dlg.setWindowTitle(self.tr('Selection Options'))
        dlg.setLayout(QGridLayout())

        ww_current_checkbox = QCheckBox(self.tr('Wastewater current'))
        status, _ = QgsProject.instance().readBoolEntry('Qgep', 'FollowWastewaterCurrent', True)
        ww_current_checkbox.setChecked(status)
        ww_planned_checkbox = QCheckBox(self.tr('Wastewater planned'))
        status, _ = QgsProject.instance().readBoolEntry('Qgep', 'FollowWastewaterPlanned', True)
        ww_planned_checkbox.setChecked(status)
        rw_current_checkbox = QCheckBox(self.tr('Rainwater current'))
        status, _ = QgsProject.instance().readBoolEntry('Qgep', 'FollowRainwaterCurrent', True)
        rw_current_checkbox.setChecked(status)
        rw_planned_checkbox = QCheckBox(self.tr('Rainwater planned'))
        status, _ = QgsProject.instance().readBoolEntry('Qgep', 'FollowRainwaterPlanned', True)
        rw_planned_checkbox.setChecked(status)
        btn_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        btn_box.accepted.connect(dlg.accept)
        btn_box.rejected.connect(dlg.reject)
        dlg.layout().addWidget(ww_current_checkbox)
        dlg.layout().addWidget(ww_planned_checkbox)
        dlg.layout().addWidget(rw_current_checkbox)
        dlg.layout().addWidget(rw_planned_checkbox)
        dlg.layout().addWidget(btn_box)

        if dlg.exec_():
            QgsProject.instance().writeEntry('Qgep', 'FollowWastewaterCurrent', ww_current_checkbox.isChecked())
            QgsProject.instance().writeEntry('Qgep', 'FollowWastewaterPlanned', ww_planned_checkbox.isChecked())
            QgsProject.instance().writeEntry('Qgep', 'FollowRainwaterCurrent', rw_current_checkbox.isChecked())
            QgsProject.instance().writeEntry('Qgep', 'FollowRainwaterPlanned', rw_planned_checkbox.isChecked())

    @pyqtSlot()
    def onSelectCurrentPathAction(self):
        reaches = list()
        wastewater_nodes = list()
        wastewater_structures = list()

        for item in self.edges:
            item_information = item[2]
            if item_information['objType'] == 'reach':
                reaches.append(item_information['baseFeature'])

        for item in self.nodes:
            if item['objType'] == 'wastewater_node':
                wastewater_nodes.append(item['objId'])

        qgep_wastewater_structures_layer = QgepLayerManager.layer('vw_qgep_wastewater_structure')
        wastewater_nodes_layer = QgepLayerManager.layer('vw_wastewater_node')
        qgep_reach_layer = QgepLayerManager.layer('vw_qgep_reach')
        catchment_areas_layer = QgepLayerManager.layer('od_catchment_area')

        wastewater_node_list = ','.join(("'" + id + "'" for id in wastewater_nodes))
        reach_list = ','.join(("'" + id + "'" for id in reaches))

        if catchment_areas_layer:
            request = QgsFeatureRequest()
            filters = list()
            if QgsProject.instance().readBoolEntry('Qgep', 'FollowWastewaterCurrent', True)[0]:
                filters.append('fk_wastewater_networkelement_ww_current IN ({})'.format(wastewater_node_list))
            if QgsProject.instance().readBoolEntry('Qgep', 'FollowWastewaterPlanned', True)[0]:
                filters.append('fk_wastewater_networkelement_ww_planned IN ({})'.format(wastewater_node_list))
            if QgsProject.instance().readBoolEntry('Qgep', 'FollowRainwaterCurrent', True)[0]:
                filters.append('fk_wastewater_networkelement_rw_current IN ({})'.format(wastewater_node_list))
            if QgsProject.instance().readBoolEntry('Qgep', 'FollowRainwaterPlanned', True)[0]:
                filters.append('fk_wastewater_networkelement_rw_planned IN ({})'.format(wastewater_node_list))

            if filters:
                request.setFilterExpression(' OR '.join(filters))
                features = catchment_areas_layer.getFeatures(request)
                catchment_areas_layer.select([f.id() for f in features])

        if qgep_reach_layer:
            request = QgsFeatureRequest()
            request.setFilterExpression('obj_id IN ({})'.format(reach_list))
            features = qgep_reach_layer.getFeatures(request)
            qgep_reach_layer.select([f.id() for f in features])

        if wastewater_nodes_layer:
            request = QgsFeatureRequest()
            request.setFilterExpression('obj_id IN ({})'.format(wastewater_node_list))
            features = wastewater_nodes_layer.getFeatures(request)
            ids = list()
            for feature in features:
                ids.append(feature.id())
                wastewater_structures.append(feature['fk_wastewater_structure'])
            wastewater_nodes_layer.select(ids)

        wastewater_structure_list = ','.join(("'" + id + "'" for id in wastewater_structures if type(id) is str))

        if qgep_wastewater_structures_layer:
            request = QgsFeatureRequest()
            request.setFilterExpression('obj_id IN ({})'.format(wastewater_structure_list))
            features = qgep_wastewater_structures_layer.getFeatures(request)
            qgep_wastewater_structures_layer.select([f.id() for f in features])

    def setTree(self, nodes, edges):
        self.nodes = nodes
        self.edges = edges
        self.selectCurrentPathAction.setEnabled(self.nodes is not None)
    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.

        Parameters
        ----------
        icon_path : str
            Path to the icon for this action. Can be a resource path
            (e.g. ':/plugins/foo/bar.png') or a normal file system path
        text : str
            Text that should be shown in menu items for this action
        callback : function
            Function to be called when the action is triggered
        enabled_flag : bool, optional
            A flag indicating if the action should be enabled by default 
            (defaults to True)
        add_to_menu : bool, optional
            Flag indicating whether the action should also be added to the menu 
            (defaults to True)
        add_to_toolbar : bool, optional
            Flag indicating whether the action should also be added to the toolbar
            (defaults to True)
        status_tip : str or None, optional
            Text to show in a popup when mouse pointer hovers over the action 
            (defaults to None)
        whats_this : str or None, optional
            Text to show in the status bar when the mouse pointer hovers over
            the action (defaults to None)
        parent : QWidget or None, optional
            Parent widget for the new action (defaults to None)
        
        Returns
        ----------
        QAction
            The action that was created. Note that the action is also added to
            self.actions list.
        """

        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:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action
Example #29
0
class ProcessingPlugin:
    def __init__(self, iface):
        self.iface = iface
        self.options_factory = None
        self.drop_handler = None
        self.item_provider = None
        self.locator_filter = None
        self.edit_features_locator_filter = None
        self.initialized = False
        self.initProcessing()

    def initProcessing(self):
        if not self.initialized:
            self.initialized = True
            Processing.initialize()

    def initGui(self):
        self.options_factory = ProcessingOptionsFactory()
        self.options_factory.setTitle(self.tr('Processing'))
        iface.registerOptionsWidgetFactory(self.options_factory)
        self.drop_handler = ProcessingDropHandler()
        iface.registerCustomDropHandler(self.drop_handler)
        self.item_provider = ProcessingDataItemProvider()
        QgsApplication.dataItemProviderRegistry().addProvider(
            self.item_provider)
        self.locator_filter = AlgorithmLocatorFilter()
        iface.registerLocatorFilter(self.locator_filter)
        # Invalidate the locator filter for in-place when active layer changes
        iface.currentLayerChanged.connect(
            lambda _: self.iface.invalidateLocatorResults())
        self.edit_features_locator_filter = InPlaceAlgorithmLocatorFilter()
        iface.registerLocatorFilter(self.edit_features_locator_filter)

        self.toolbox = ProcessingToolbox()
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.toolbox)
        self.toolbox.hide()
        self.toolbox.visibilityChanged.connect(self.toolboxVisibilityChanged)

        self.resultsDock = ResultsDock()
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.resultsDock)
        self.resultsDock.hide()

        self.menu = QMenu(self.iface.mainWindow().menuBar())
        self.menu.setObjectName('processing')
        self.menu.setTitle(self.tr('Pro&cessing'))

        self.toolboxAction = QAction(self.tr('&Toolbox'),
                                     self.iface.mainWindow())
        self.toolboxAction.setCheckable(True)
        self.toolboxAction.setObjectName('toolboxAction')
        self.toolboxAction.setIcon(
            QgsApplication.getThemeIcon("/processingAlgorithm.svg"))
        self.iface.registerMainWindowAction(
            self.toolboxAction,
            QKeySequence('Ctrl+Alt+T').toString(QKeySequence.NativeText))
        self.toolboxAction.toggled.connect(self.openToolbox)
        self.iface.attributesToolBar().insertAction(
            self.iface.actionOpenStatisticalSummary(), self.toolboxAction)
        self.menu.addAction(self.toolboxAction)

        self.modelerAction = QAction(
            QgsApplication.getThemeIcon("/processingModel.svg"),
            QCoreApplication.translate('ProcessingPlugin',
                                       'Graphical &Modeler…'),
            self.iface.mainWindow())
        self.modelerAction.setObjectName('modelerAction')
        self.modelerAction.triggered.connect(self.openModeler)
        self.iface.registerMainWindowAction(
            self.modelerAction,
            QKeySequence('Ctrl+Alt+M').toString(QKeySequence.NativeText))
        self.menu.addAction(self.modelerAction)

        self.historyAction = QAction(
            QgsApplication.getThemeIcon("/mIconHistory.svg"),
            QCoreApplication.translate('ProcessingPlugin', '&History…'),
            self.iface.mainWindow())
        self.historyAction.setObjectName('historyAction')
        self.historyAction.triggered.connect(self.openHistory)
        self.iface.registerMainWindowAction(
            self.historyAction,
            QKeySequence('Ctrl+Alt+H').toString(QKeySequence.NativeText))
        self.menu.addAction(self.historyAction)
        self.toolbox.processingToolbar.addAction(self.historyAction)

        self.resultsAction = QAction(
            QgsApplication.getThemeIcon("/processingResult.svg"),
            self.tr('&Results Viewer'), self.iface.mainWindow())
        self.resultsAction.setCheckable(True)
        self.iface.registerMainWindowAction(
            self.resultsAction,
            QKeySequence('Ctrl+Alt+R').toString(QKeySequence.NativeText))

        self.menu.addAction(self.resultsAction)
        self.toolbox.processingToolbar.addAction(self.resultsAction)
        self.resultsDock.visibilityChanged.connect(
            self.resultsAction.setChecked)
        self.resultsAction.toggled.connect(self.resultsDock.setUserVisible)

        self.toolbox.processingToolbar.addSeparator()

        self.editInPlaceAction = QAction(
            QgsApplication.getThemeIcon("/mActionProcessSelected.svg"),
            self.tr('Edit Features In-Place'), self.iface.mainWindow())
        self.editInPlaceAction.setObjectName('editInPlaceFeatures')
        self.editInPlaceAction.setCheckable(True)
        self.editInPlaceAction.toggled.connect(self.editSelected)
        self.menu.addAction(self.editInPlaceAction)
        self.toolbox.processingToolbar.addAction(self.editInPlaceAction)

        self.toolbox.processingToolbar.addSeparator()

        self.optionsAction = QAction(
            QgsApplication.getThemeIcon("/mActionOptions.svg"),
            self.tr('Options'), self.iface.mainWindow())
        self.optionsAction.setObjectName('optionsAction')
        self.optionsAction.triggered.connect(self.openProcessingOptions)
        self.toolbox.processingToolbar.addAction(self.optionsAction)

        menuBar = self.iface.mainWindow().menuBar()
        menuBar.insertMenu(self.iface.firstRightStandardMenu().menuAction(),
                           self.menu)

        self.menu.addSeparator()

        initializeMenus()
        createMenus()

        # In-place editing button state sync
        self.iface.currentLayerChanged.connect(self.sync_in_place_button_state)
        self.iface.mapCanvas().selectionChanged.connect(
            self.sync_in_place_button_state)
        self.iface.actionToggleEditing().triggered.connect(
            partial(self.sync_in_place_button_state, None))
        self.sync_in_place_button_state()

    def sync_in_place_button_state(self, layer=None):
        """Synchronise the button state with layer state"""

        if layer is None:
            layer = self.iface.activeLayer()

        old_enabled_state = self.editInPlaceAction.isEnabled()

        new_enabled_state = layer is not None and layer.type(
        ) == QgsMapLayer.VectorLayer
        self.editInPlaceAction.setEnabled(new_enabled_state)

        if new_enabled_state != old_enabled_state:
            self.toolbox.set_in_place_edit_mode(
                new_enabled_state and self.editInPlaceAction.isChecked())

    def openProcessingOptions(self):
        self.iface.showOptionsDialog(self.iface.mainWindow(),
                                     currentPage='processingOptions')

    def unload(self):
        self.toolbox.setVisible(False)
        self.iface.removeDockWidget(self.toolbox)
        self.iface.attributesToolBar().removeAction(self.toolboxAction)

        self.resultsDock.setVisible(False)
        self.iface.removeDockWidget(self.resultsDock)

        self.toolbox.deleteLater()
        self.menu.deleteLater()

        # delete temporary output files
        folder = QgsProcessingUtils.tempFolder()
        if QDir(folder).exists():
            shutil.rmtree(folder, True)

        # also delete temporary help files
        folder = tempHelpFolder()
        if QDir(folder).exists():
            shutil.rmtree(folder, True)

        self.iface.unregisterMainWindowAction(self.toolboxAction)
        self.iface.unregisterMainWindowAction(self.modelerAction)
        self.iface.unregisterMainWindowAction(self.historyAction)
        self.iface.unregisterMainWindowAction(self.resultsAction)

        self.iface.unregisterOptionsWidgetFactory(self.options_factory)
        self.iface.deregisterLocatorFilter(self.locator_filter)
        self.iface.deregisterLocatorFilter(self.edit_features_locator_filter)
        self.iface.unregisterCustomDropHandler(self.drop_handler)
        QgsApplication.dataItemProviderRegistry().removeProvider(
            self.item_provider)

        removeMenus()
        Processing.deinitialize()

    def openToolbox(self, show):
        self.toolbox.setUserVisible(show)

    def toolboxVisibilityChanged(self, visible):
        self.toolboxAction.setChecked(visible)

    def openModeler(self):
        dlg = ModelerDialog()
        dlg.update_model.connect(self.updateModel)
        dlg.show()

    def updateModel(self):
        model_provider = QgsApplication.processingRegistry().providerById(
            'model')
        model_provider.refreshAlgorithms()

    def openResults(self):
        if self.resultsDock.isVisible():
            self.resultsDock.hide()
        else:
            self.resultsDock.show()

    def openHistory(self):
        dlg = HistoryDialog()
        dlg.exec_()

    def tr(self, message):
        return QCoreApplication.translate('ProcessingPlugin', message)

    def editSelected(self, enabled):
        self.toolbox.set_in_place_edit_mode(enabled)
Example #30
0
    def selectOutput(self):
        popupMenu = QMenu()

        if not self.default_selection:
            if self.parameter.flags(
            ) & QgsProcessingParameterDefinition.FlagOptional:
                actionSkipOutput = QAction(self.tr('Skip Output'),
                                           self.btnSelect)
                actionSkipOutput.triggered.connect(self.skipOutput)
                popupMenu.addAction(actionSkipOutput)

            if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
                    and self.parameter.supportsNonFileBasedOutput():
                # use memory layers for temporary layers if supported
                actionSaveToTemp = QAction(self.tr('Create Temporary Layer'),
                                           self.btnSelect)
            elif isinstance(self.parameter,
                            QgsProcessingParameterFolderDestination):
                actionSaveToTemp = QAction(
                    self.tr('Save to a Temporary Directory'), self.btnSelect)
            else:
                actionSaveToTemp = QAction(self.tr('Save to a Temporary File'),
                                           self.btnSelect)
            actionSaveToTemp.triggered.connect(self.saveToTemporary)
            popupMenu.addAction(actionSaveToTemp)

        if isinstance(self.parameter, QgsProcessingParameterFolderDestination):
            actionSaveToFile = QAction(
                QCoreApplication.translate('DestinationSelectionPanel',
                                           'Save to Directory…'),
                self.btnSelect)
            actionSaveToFile.triggered.connect(self.selectDirectory)
        else:
            actionSaveToFile = QAction(
                QCoreApplication.translate('DestinationSelectionPanel',
                                           'Save to File…'), self.btnSelect)
            actionSaveToFile.triggered.connect(self.selectFile)
        popupMenu.addAction(actionSaveToFile)

        if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
                and self.parameter.supportsNonFileBasedOutput():
            actionSaveToGpkg = QAction(
                QCoreApplication.translate('DestinationSelectionPanel',
                                           'Save to GeoPackage…'),
                self.btnSelect)
            actionSaveToGpkg.triggered.connect(self.saveToGeopackage)
            popupMenu.addAction(actionSaveToGpkg)
            actionSaveToPostGIS = QAction(
                QCoreApplication.translate('DestinationSelectionPanel',
                                           'Save to PostGIS Table…'),
                self.btnSelect)
            actionSaveToPostGIS.triggered.connect(self.saveToPostGIS)
            settings = QgsSettings()
            settings.beginGroup('/PostgreSQL/connections/')
            names = settings.childGroups()
            settings.endGroup()
            actionSaveToPostGIS.setEnabled(bool(names))
            popupMenu.addAction(actionSaveToPostGIS)

        actionSetEncoding = QAction(
            QCoreApplication.translate('DestinationSelectionPanel',
                                       'Change File Encoding ({})…').format(
                                           self.encoding), self.btnSelect)
        actionSetEncoding.triggered.connect(self.selectEncoding)
        popupMenu.addAction(actionSetEncoding)

        popupMenu.exec_(QCursor.pos())
Example #31
0
class PythonConsoleWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setWindowTitle(
            QCoreApplication.translate("PythonConsole", "Python Console"))

        self.settings = QgsSettings()

        self.shell = ShellScintilla(self)
        self.setFocusProxy(self.shell)
        self.shellOut = ShellOutputScintilla(self)
        self.tabEditorWidget = EditorTabWidget(self)

        # ------------ UI -------------------------------

        self.splitterEditor = QSplitter(self)
        self.splitterEditor.setOrientation(Qt.Horizontal)
        self.splitterEditor.setHandleWidth(6)
        self.splitterEditor.setChildrenCollapsible(True)

        self.shellOutWidget = QWidget(self)
        self.shellOutWidget.setLayout(QVBoxLayout())
        self.shellOutWidget.layout().setContentsMargins(0, 0, 0, 0)
        self.shellOutWidget.layout().addWidget(self.shellOut)

        self.splitter = QSplitter(self.splitterEditor)
        self.splitter.setOrientation(Qt.Vertical)
        self.splitter.setHandleWidth(3)
        self.splitter.setChildrenCollapsible(False)
        self.splitter.addWidget(self.shellOutWidget)
        self.splitter.addWidget(self.shell)

        # self.splitterEditor.addWidget(self.tabEditorWidget)

        self.splitterObj = QSplitter(self.splitterEditor)
        self.splitterObj.setHandleWidth(3)
        self.splitterObj.setOrientation(Qt.Horizontal)
        # self.splitterObj.setSizes([0, 0])
        # self.splitterObj.setStretchFactor(0, 1)

        self.widgetEditor = QWidget(self.splitterObj)
        self.widgetFind = QWidget(self)

        self.listClassMethod = QTreeWidget(self.splitterObj)
        self.listClassMethod.setColumnCount(2)
        objInspLabel = QCoreApplication.translate("PythonConsole",
                                                  "Object Inspector")
        self.listClassMethod.setHeaderLabels([objInspLabel, ''])
        self.listClassMethod.setColumnHidden(1, True)
        self.listClassMethod.setAlternatingRowColors(True)

        # self.splitterEditor.addWidget(self.widgetEditor)
        # self.splitterObj.addWidget(self.listClassMethod)
        # self.splitterObj.addWidget(self.widgetEditor)

        # Hide side editor on start up
        self.splitterObj.hide()
        self.listClassMethod.hide()
        # Hide search widget on start up
        self.widgetFind.hide()

        icon_size = iface.iconSize(
            dockedToolbar=True) if iface else QSize(16, 16)

        sizes = self.splitter.sizes()
        self.splitter.setSizes(sizes)

        # ----------------Restore Settings------------------------------------

        self.restoreSettingsConsole()

        # ------------------Toolbar Editor-------------------------------------

        # Action for Open File
        openFileBt = QCoreApplication.translate("PythonConsole",
                                                "Open Script…")
        self.openFileButton = QAction(self)
        self.openFileButton.setCheckable(False)
        self.openFileButton.setEnabled(True)
        self.openFileButton.setIcon(
            QgsApplication.getThemeIcon("mActionScriptOpen.svg"))
        self.openFileButton.setMenuRole(QAction.PreferencesRole)
        self.openFileButton.setIconVisibleInMenu(True)
        self.openFileButton.setToolTip(openFileBt)
        self.openFileButton.setText(openFileBt)

        openExtEditorBt = QCoreApplication.translate(
            "PythonConsole", "Open in External Editor")
        self.openInEditorButton = QAction(self)
        self.openInEditorButton.setCheckable(False)
        self.openInEditorButton.setEnabled(True)
        self.openInEditorButton.setIcon(
            QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg"))
        self.openInEditorButton.setMenuRole(QAction.PreferencesRole)
        self.openInEditorButton.setIconVisibleInMenu(True)
        self.openInEditorButton.setToolTip(openExtEditorBt)
        self.openInEditorButton.setText(openExtEditorBt)
        # Action for Save File
        saveFileBt = QCoreApplication.translate("PythonConsole", "Save")
        self.saveFileButton = QAction(self)
        self.saveFileButton.setCheckable(False)
        self.saveFileButton.setEnabled(False)
        self.saveFileButton.setIcon(
            QgsApplication.getThemeIcon("mActionFileSave.svg"))
        self.saveFileButton.setMenuRole(QAction.PreferencesRole)
        self.saveFileButton.setIconVisibleInMenu(True)
        self.saveFileButton.setToolTip(saveFileBt)
        self.saveFileButton.setText(saveFileBt)
        # Action for Save File As
        saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As…")
        self.saveAsFileButton = QAction(self)
        self.saveAsFileButton.setCheckable(False)
        self.saveAsFileButton.setEnabled(True)
        self.saveAsFileButton.setIcon(
            QgsApplication.getThemeIcon("mActionFileSaveAs.svg"))
        self.saveAsFileButton.setMenuRole(QAction.PreferencesRole)
        self.saveAsFileButton.setIconVisibleInMenu(True)
        self.saveAsFileButton.setToolTip(saveAsFileBt)
        self.saveAsFileButton.setText(saveAsFileBt)
        # Action Cut
        cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut")
        self.cutEditorButton = QAction(self)
        self.cutEditorButton.setCheckable(False)
        self.cutEditorButton.setEnabled(True)
        self.cutEditorButton.setIcon(
            QgsApplication.getThemeIcon("mActionEditCut.svg"))
        self.cutEditorButton.setMenuRole(QAction.PreferencesRole)
        self.cutEditorButton.setIconVisibleInMenu(True)
        self.cutEditorButton.setToolTip(cutEditorBt)
        self.cutEditorButton.setText(cutEditorBt)
        # Action Copy
        copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy")
        self.copyEditorButton = QAction(self)
        self.copyEditorButton.setCheckable(False)
        self.copyEditorButton.setEnabled(True)
        self.copyEditorButton.setIcon(
            QgsApplication.getThemeIcon("mActionEditCopy.svg"))
        self.copyEditorButton.setMenuRole(QAction.PreferencesRole)
        self.copyEditorButton.setIconVisibleInMenu(True)
        self.copyEditorButton.setToolTip(copyEditorBt)
        self.copyEditorButton.setText(copyEditorBt)
        # Action Paste
        pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste")
        self.pasteEditorButton = QAction(self)
        self.pasteEditorButton.setCheckable(False)
        self.pasteEditorButton.setEnabled(True)
        self.pasteEditorButton.setIcon(
            QgsApplication.getThemeIcon("mActionEditPaste.svg"))
        self.pasteEditorButton.setMenuRole(QAction.PreferencesRole)
        self.pasteEditorButton.setIconVisibleInMenu(True)
        self.pasteEditorButton.setToolTip(pasteEditorBt)
        self.pasteEditorButton.setText(pasteEditorBt)
        # Action Run Script (subprocess)
        runScriptEditorBt = QCoreApplication.translate("PythonConsole",
                                                       "Run Script")
        self.runScriptEditorButton = QAction(self)
        self.runScriptEditorButton.setCheckable(False)
        self.runScriptEditorButton.setEnabled(True)
        self.runScriptEditorButton.setIcon(
            QgsApplication.getThemeIcon("mActionStart.svg"))
        self.runScriptEditorButton.setMenuRole(QAction.PreferencesRole)
        self.runScriptEditorButton.setIconVisibleInMenu(True)
        self.runScriptEditorButton.setToolTip(runScriptEditorBt)
        self.runScriptEditorButton.setText(runScriptEditorBt)
        # Action Run Script (subprocess)
        commentEditorBt = QCoreApplication.translate("PythonConsole",
                                                     "Comment")
        self.commentEditorButton = QAction(self)
        self.commentEditorButton.setCheckable(False)
        self.commentEditorButton.setEnabled(True)
        self.commentEditorButton.setIcon(
            QgsApplication.getThemeIcon(
                "console/iconCommentEditorConsole.svg"))
        self.commentEditorButton.setMenuRole(QAction.PreferencesRole)
        self.commentEditorButton.setIconVisibleInMenu(True)
        self.commentEditorButton.setToolTip(commentEditorBt)
        self.commentEditorButton.setText(commentEditorBt)
        # Action Run Script (subprocess)
        uncommentEditorBt = QCoreApplication.translate("PythonConsole",
                                                       "Uncomment")
        self.uncommentEditorButton = QAction(self)
        self.uncommentEditorButton.setCheckable(False)
        self.uncommentEditorButton.setEnabled(True)
        self.uncommentEditorButton.setIcon(
            QgsApplication.getThemeIcon(
                "console/iconUncommentEditorConsole.svg"))
        self.uncommentEditorButton.setMenuRole(QAction.PreferencesRole)
        self.uncommentEditorButton.setIconVisibleInMenu(True)
        self.uncommentEditorButton.setToolTip(uncommentEditorBt)
        self.uncommentEditorButton.setText(uncommentEditorBt)
        # Action for Object browser
        objList = QCoreApplication.translate("PythonConsole",
                                             "Object Inspector…")
        self.objectListButton = QAction(self)
        self.objectListButton.setCheckable(True)
        self.objectListButton.setEnabled(
            self.settings.value("pythonConsole/enableObjectInsp",
                                False,
                                type=bool))
        self.objectListButton.setIcon(
            QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg"))
        self.objectListButton.setMenuRole(QAction.PreferencesRole)
        self.objectListButton.setIconVisibleInMenu(True)
        self.objectListButton.setToolTip(objList)
        self.objectListButton.setText(objList)
        # Action for Find text
        findText = QCoreApplication.translate("PythonConsole", "Find Text")
        self.findTextButton = QAction(self)
        self.findTextButton.setCheckable(True)
        self.findTextButton.setEnabled(True)
        self.findTextButton.setIcon(
            QgsApplication.getThemeIcon("console/iconSearchEditorConsole.svg"))
        self.findTextButton.setMenuRole(QAction.PreferencesRole)
        self.findTextButton.setIconVisibleInMenu(True)
        self.findTextButton.setToolTip(findText)
        self.findTextButton.setText(findText)

        # ----------------Toolbar Console-------------------------------------

        # Action Show Editor
        showEditor = QCoreApplication.translate("PythonConsole", "Show Editor")
        self.showEditorButton = QAction(self)
        self.showEditorButton.setEnabled(True)
        self.showEditorButton.setCheckable(True)
        self.showEditorButton.setIcon(
            QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg"))
        self.showEditorButton.setMenuRole(QAction.PreferencesRole)
        self.showEditorButton.setIconVisibleInMenu(True)
        self.showEditorButton.setToolTip(showEditor)
        self.showEditorButton.setText(showEditor)
        # Action for Clear button
        clearBt = QCoreApplication.translate("PythonConsole", "Clear Console")
        self.clearButton = QAction(self)
        self.clearButton.setCheckable(False)
        self.clearButton.setEnabled(True)
        self.clearButton.setIcon(
            QgsApplication.getThemeIcon("console/iconClearConsole.svg"))
        self.clearButton.setMenuRole(QAction.PreferencesRole)
        self.clearButton.setIconVisibleInMenu(True)
        self.clearButton.setToolTip(clearBt)
        self.clearButton.setText(clearBt)
        # Action for settings
        optionsBt = QCoreApplication.translate("PythonConsole", "Options…")
        self.optionsButton = QAction(self)
        self.optionsButton.setCheckable(False)
        self.optionsButton.setEnabled(True)
        self.optionsButton.setIcon(
            QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"))
        self.optionsButton.setMenuRole(QAction.PreferencesRole)
        self.optionsButton.setIconVisibleInMenu(True)
        self.optionsButton.setToolTip(optionsBt)
        self.optionsButton.setText(optionsBt)
        # Action for Run script
        runBt = QCoreApplication.translate("PythonConsole", "Run Command")
        self.runButton = QAction(self)
        self.runButton.setCheckable(False)
        self.runButton.setEnabled(True)
        self.runButton.setIcon(
            QgsApplication.getThemeIcon("console/mIconRunConsole.svg"))
        self.runButton.setMenuRole(QAction.PreferencesRole)
        self.runButton.setIconVisibleInMenu(True)
        self.runButton.setToolTip(runBt)
        self.runButton.setText(runBt)

        # Help button
        self.helpConsoleAction = QAction(self)
        self.helpConsoleAction.setEnabled(True)
        self.helpConsoleAction.setText(
            QCoreApplication.translate("PythonConsole", "Python Console Help"))
        self.helpAPIAction = QAction(self)
        self.helpAPIAction.setEnabled(True)
        self.helpAPIAction.setText(
            QCoreApplication.translate("PythonConsole",
                                       "PyQGIS API Documentation"))
        self.helpCookbookAction = QAction(self)
        self.helpCookbookAction.setEnabled(True)
        self.helpCookbookAction.setText(
            QCoreApplication.translate("PythonConsole", "PyQGIS Cookbook"))

        self.helpMenu = QMenu(self)
        self.helpMenu.addAction(self.helpConsoleAction)
        self.helpMenu.addAction(self.helpAPIAction)
        self.helpMenu.addAction(self.helpCookbookAction)

        helpBt = QCoreApplication.translate("PythonConsole", "Help…")
        self.helpButton = QToolButton(self)
        self.helpButton.setPopupMode(QToolButton.InstantPopup)
        self.helpButton.setEnabled(True)
        self.helpButton.setIcon(
            QgsApplication.getThemeIcon("console/iconHelpConsole.svg"))
        self.helpButton.setToolTip(helpBt)
        self.helpButton.setMenu(self.helpMenu)

        self.toolBar = QToolBar()
        self.toolBar.setEnabled(True)
        self.toolBar.setFocusPolicy(Qt.NoFocus)
        self.toolBar.setContextMenuPolicy(Qt.DefaultContextMenu)
        self.toolBar.setLayoutDirection(Qt.LeftToRight)
        self.toolBar.setIconSize(icon_size)
        self.toolBar.setMovable(False)
        self.toolBar.setFloatable(False)
        self.toolBar.addAction(self.clearButton)
        self.toolBar.addAction(self.runButton)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.showEditorButton)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.optionsButton)
        self.toolBar.addWidget(self.helpButton)

        self.toolBarEditor = QToolBar()
        self.toolBarEditor.setEnabled(False)
        self.toolBarEditor.setFocusPolicy(Qt.NoFocus)
        self.toolBarEditor.setContextMenuPolicy(Qt.DefaultContextMenu)
        self.toolBarEditor.setLayoutDirection(Qt.LeftToRight)
        self.toolBarEditor.setIconSize(icon_size)
        self.toolBarEditor.setMovable(False)
        self.toolBarEditor.setFloatable(False)
        self.toolBarEditor.addAction(self.openFileButton)
        self.toolBarEditor.addAction(self.openInEditorButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.saveFileButton)
        self.toolBarEditor.addAction(self.saveAsFileButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.runScriptEditorButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.cutEditorButton)
        self.toolBarEditor.addAction(self.copyEditorButton)
        self.toolBarEditor.addAction(self.pasteEditorButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.findTextButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.commentEditorButton)
        self.toolBarEditor.addAction(self.uncommentEditorButton)
        self.toolBarEditor.addSeparator()
        self.toolBarEditor.addAction(self.objectListButton)

        self.widgetButton = QWidget()
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.widgetButton.sizePolicy().hasHeightForWidth())
        self.widgetButton.setSizePolicy(sizePolicy)

        self.widgetButtonEditor = QWidget(self.widgetEditor)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.widgetButtonEditor.sizePolicy().hasHeightForWidth())
        self.widgetButtonEditor.setSizePolicy(sizePolicy)

        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.shellOut.sizePolicy().hasHeightForWidth())
        self.shellOut.setSizePolicy(sizePolicy)

        self.shellOut.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.shell.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)

        # ------------ Layout -------------------------------

        self.mainLayout = QGridLayout(self)
        self.mainLayout.setMargin(0)
        self.mainLayout.setSpacing(0)
        self.mainLayout.addWidget(self.widgetButton, 0, 0, 1, 1)
        self.mainLayout.addWidget(self.splitterEditor, 0, 1, 1, 1)

        self.shellOutWidget.layout().insertWidget(0, self.toolBar)

        self.layoutEditor = QGridLayout(self.widgetEditor)
        self.layoutEditor.setMargin(0)
        self.layoutEditor.setSpacing(0)
        self.layoutEditor.addWidget(self.toolBarEditor, 0, 1, 1, 1)
        self.layoutEditor.addWidget(self.widgetButtonEditor, 1, 0, 2, 1)
        self.layoutEditor.addWidget(self.tabEditorWidget, 1, 1, 1, 1)
        self.layoutEditor.addWidget(self.widgetFind, 2, 1, 1, 1)

        #  Layout for the find widget
        self.layoutFind = QGridLayout(self.widgetFind)
        self.layoutFind.setContentsMargins(0, 0, 0, 0)
        self.lineEditFind = QgsFilterLineEdit()
        self.lineEditFind.setShowSearchIcon(True)
        placeHolderTxt = QCoreApplication.translate("PythonConsole",
                                                    "Enter text to find…")

        self.lineEditFind.setPlaceholderText(placeHolderTxt)
        self.toolBarFindText = QToolBar()
        self.toolBarFindText.setIconSize(icon_size)
        self.findNextButton = QAction(self)
        self.findNextButton.setEnabled(False)
        toolTipfindNext = QCoreApplication.translate("PythonConsole",
                                                     "Find Next")
        self.findNextButton.setToolTip(toolTipfindNext)
        self.findNextButton.setIcon(
            QgsApplication.getThemeIcon(
                "console/iconSearchNextEditorConsole.svg"))
        self.findPrevButton = QAction(self)
        self.findPrevButton.setEnabled(False)
        toolTipfindPrev = QCoreApplication.translate("PythonConsole",
                                                     "Find Previous")
        self.findPrevButton.setToolTip(toolTipfindPrev)
        self.findPrevButton.setIcon(
            QgsApplication.getThemeIcon(
                "console/iconSearchPrevEditorConsole.svg"))
        self.caseSensitive = QCheckBox()
        caseSensTr = QCoreApplication.translate("PythonConsole",
                                                "Case Sensitive")
        self.caseSensitive.setText(caseSensTr)
        self.wholeWord = QCheckBox()
        wholeWordTr = QCoreApplication.translate("PythonConsole", "Whole Word")
        self.wholeWord.setText(wholeWordTr)
        self.wrapAround = QCheckBox()
        self.wrapAround.setChecked(True)
        wrapAroundTr = QCoreApplication.translate("PythonConsole",
                                                  "Wrap Around")
        self.wrapAround.setText(wrapAroundTr)

        self.toolBarFindText.addWidget(self.lineEditFind)
        self.toolBarFindText.addAction(self.findPrevButton)
        self.toolBarFindText.addAction(self.findNextButton)
        self.toolBarFindText.addWidget(self.caseSensitive)
        self.toolBarFindText.addWidget(self.wholeWord)
        self.toolBarFindText.addWidget(self.wrapAround)

        self.layoutFind.addWidget(self.toolBarFindText, 0, 1, 1, 1)

        # ------------ Add first Tab in Editor -------------------------------

        # self.tabEditorWidget.newTabEditor(tabName='first', filename=None)

        # ------------ Signal -------------------------------

        self.findTextButton.triggered.connect(self._toggleFind)
        self.objectListButton.toggled.connect(self.toggleObjectListWidget)
        self.commentEditorButton.triggered.connect(self.commentCode)
        self.uncommentEditorButton.triggered.connect(self.uncommentCode)
        self.runScriptEditorButton.triggered.connect(self.runScriptEditor)
        self.cutEditorButton.triggered.connect(self.cutEditor)
        self.copyEditorButton.triggered.connect(self.copyEditor)
        self.pasteEditorButton.triggered.connect(self.pasteEditor)
        self.showEditorButton.toggled.connect(self.toggleEditor)
        self.clearButton.triggered.connect(self.shellOut.clearConsole)
        self.optionsButton.triggered.connect(self.openSettings)
        self.runButton.triggered.connect(self.shell.entered)
        self.openFileButton.triggered.connect(self.openScriptFile)
        self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor)
        self.saveFileButton.triggered.connect(self.saveScriptFile)
        self.saveAsFileButton.triggered.connect(self.saveAsScriptFile)
        self.helpConsoleAction.triggered.connect(self.openHelpConsole)
        self.helpAPIAction.triggered.connect(self.openHelpAPI)
        self.helpCookbookAction.triggered.connect(self.openHelpCookbook)
        self.listClassMethod.itemClicked.connect(self.onClickGoToLine)
        self.lineEditFind.returnPressed.connect(self._findNext)
        self.findNextButton.triggered.connect(self._findNext)
        self.findPrevButton.triggered.connect(self._findPrev)
        self.lineEditFind.textChanged.connect(self._textFindChanged)

        self.findScut = QShortcut(QKeySequence.Find, self.widgetEditor)
        self.findScut.setContext(Qt.WidgetWithChildrenShortcut)
        self.findScut.activated.connect(self._openFind)

        self.findNextScut = QShortcut(QKeySequence.FindNext, self.widgetEditor)
        self.findNextScut.setContext(Qt.WidgetWithChildrenShortcut)
        self.findNextScut.activated.connect(self._findNext)

        self.findPreviousScut = QShortcut(QKeySequence.FindPrevious,
                                          self.widgetEditor)
        self.findPreviousScut.setContext(Qt.WidgetWithChildrenShortcut)
        self.findPreviousScut.activated.connect(self._findPrev)

        # Escape on editor hides the find bar
        self.findScut = QShortcut(Qt.Key_Escape, self.widgetEditor)
        self.findScut.setContext(Qt.WidgetWithChildrenShortcut)
        self.findScut.activated.connect(self._closeFind)

    def _toggleFind(self):
        self.tabEditorWidget.currentWidget().newEditor.toggleFindWidget()

    def _openFind(self):
        self.tabEditorWidget.currentWidget().newEditor.openFindWidget()

    def _closeFind(self):
        self.tabEditorWidget.currentWidget().newEditor.closeFindWidget()

    def _findNext(self):
        self.tabEditorWidget.currentWidget().newEditor.findText(True)

    def _findPrev(self):
        self.tabEditorWidget.currentWidget().newEditor.findText(False)

    def _textFindChanged(self):
        if self.lineEditFind.text():
            self.findNextButton.setEnabled(True)
            self.findPrevButton.setEnabled(True)
            self.tabEditorWidget.currentWidget().newEditor.findText(
                True, showMessage=False, findFirst=True)
        else:
            self.lineEditFind.setStyleSheet('')
            self.findNextButton.setEnabled(False)
            self.findPrevButton.setEnabled(False)

    def onClickGoToLine(self, item, column):
        tabEditor = self.tabEditorWidget.currentWidget().newEditor
        if item.text(1) == 'syntaxError':
            check = tabEditor.syntaxCheck(fromContextMenu=False)
            if check and not tabEditor.isReadOnly():
                self.tabEditorWidget.currentWidget().save()
            return
        linenr = int(item.text(1))
        itemName = str(item.text(0))
        charPos = itemName.find(' ')
        if charPos != -1:
            objName = itemName[0:charPos]
        else:
            objName = itemName
        tabEditor.goToLine(str.encode(objName), linenr)

    def toggleEditor(self, checked):
        self.splitterObj.show() if checked else self.splitterObj.hide()
        if not self.tabEditorWidget:
            self.tabEditorWidget.enableToolBarEditor(checked)
            self.tabEditorWidget.restoreTabsOrAddNew()

    def toggleObjectListWidget(self, checked):
        self.listClassMethod.show() if checked else self.listClassMethod.hide()

    def pasteEditor(self):
        self.tabEditorWidget.currentWidget().newEditor.paste()

    def cutEditor(self):
        self.tabEditorWidget.currentWidget().newEditor.cut()

    def copyEditor(self):
        self.tabEditorWidget.currentWidget().newEditor.copy()

    def runScriptEditor(self):
        self.tabEditorWidget.currentWidget().newEditor.runScriptCode()

    def commentCode(self):
        self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(True)

    def uncommentCode(self):
        self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(False)

    def openScriptFileExtEditor(self):
        tabWidget = self.tabEditorWidget.currentWidget()
        path = tabWidget.path
        import subprocess
        try:
            subprocess.Popen([os.environ['EDITOR'], path])
        except KeyError:
            QDesktopServices.openUrl(QUrl.fromLocalFile(path))

    def openScriptFile(self):
        lastDirPath = self.settings.value("pythonConsole/lastDirPath",
                                          QDir.homePath())
        openFileTr = QCoreApplication.translate("PythonConsole", "Open File")
        fileList, selected_filter = QFileDialog.getOpenFileNames(
            self, openFileTr, lastDirPath, "Script file (*.py)")
        if fileList:
            for pyFile in fileList:
                for i in range(self.tabEditorWidget.count()):
                    tabWidget = self.tabEditorWidget.widget(i)
                    if tabWidget.path == pyFile:
                        self.tabEditorWidget.setCurrentWidget(tabWidget)
                        break
                else:
                    tabName = QFileInfo(pyFile).fileName()
                    self.tabEditorWidget.newTabEditor(tabName, pyFile)

                    lastDirPath = QFileInfo(pyFile).path()
                    self.settings.setValue("pythonConsole/lastDirPath", pyFile)
                    self.updateTabListScript(pyFile, action='append')

    def saveScriptFile(self):
        tabWidget = self.tabEditorWidget.currentWidget()
        try:
            tabWidget.save()
        except (IOError, OSError) as error:
            msgText = QCoreApplication.translate(
                'PythonConsole',
                'The file <b>{0}</b> could not be saved. Error: {1}').format(
                    tabWidget.path, error.strerror)
            self.callWidgetMessageBarEditor(msgText, 2, False)

    def saveAsScriptFile(self, index=None):
        tabWidget = self.tabEditorWidget.currentWidget()
        if not index:
            index = self.tabEditorWidget.currentIndex()
        if not tabWidget.path:
            fileName = self.tabEditorWidget.tabText(index) + '.py'
            folder = self.settings.value("pythonConsole/lastDirPath",
                                         QDir.homePath())
            pathFileName = os.path.join(folder, fileName)
            fileNone = True
        else:
            pathFileName = tabWidget.path
            fileNone = False
        saveAsFileTr = QCoreApplication.translate("PythonConsole",
                                                  "Save File As")
        filename, filter = QFileDialog.getSaveFileName(self, saveAsFileTr,
                                                       pathFileName,
                                                       "Script file (*.py)")
        if filename:
            try:
                tabWidget.save(filename)
            except (IOError, OSError) as error:
                msgText = QCoreApplication.translate(
                    'PythonConsole',
                    'The file <b>{0}</b> could not be saved. Error: {1}'
                ).format(tabWidget.path, error.strerror)
                self.callWidgetMessageBarEditor(msgText, 2, False)
                if fileNone:
                    tabWidget.path = None
                else:
                    tabWidget.path = pathFileName
                return

            if not fileNone:
                self.updateTabListScript(pathFileName, action='remove')

    def openHelpConsole(self):
        QgsHelp.openHelp("plugins/python_console.html")

    def openHelpAPI(self):
        m = re.search(r'^([0-9]+)\.([0-9]+)\.', Qgis.QGIS_VERSION)
        if m:
            QDesktopServices.openUrl(
                QUrl('https://qgis.org/pyqgis/{}.{}/'.format(
                    m.group(1), m.group(2))))

    def openHelpCookbook(self):
        m = re.search(r'^([0-9]+)\.([0-9]+)\.', Qgis.QGIS_VERSION)
        if m:
            QDesktopServices.openUrl(
                QUrl(
                    'https://docs.qgis.org/{}.{}/en/docs/pyqgis_developer_cookbook/index.html'
                    .format(m.group(1), m.group(2))))

    def openSettings(self):
        if optionsDialog(self).exec_():
            self.shell.refreshSettingsShell()
            self.shellOut.refreshSettingsOutput()
            self.tabEditorWidget.refreshSettingsEditor()

    def callWidgetMessageBar(self, text):
        self.shellOut.widgetMessageBar(iface, text)

    def callWidgetMessageBarEditor(self, text, level, timed):
        self.tabEditorWidget.widgetMessageBar(iface, text, level, timed)

    def updateTabListScript(self, script, action=None):
        if action == 'remove':
            self.tabListScript.remove(script)
        elif action == 'append':
            if not self.tabListScript:
                self.tabListScript = []
            if script not in self.tabListScript:
                self.tabListScript.append(script)
        else:
            self.tabListScript = []
        self.settings.setValue("pythonConsole/tabScripts", self.tabListScript)

    def saveSettingsConsole(self):
        self.settings.setValue("pythonConsole/splitterConsole",
                               self.splitter.saveState())
        self.settings.setValue("pythonConsole/splitterObj",
                               self.splitterObj.saveState())
        self.settings.setValue("pythonConsole/splitterEditor",
                               self.splitterEditor.saveState())

        self.shell.writeHistoryFile(True)

    def restoreSettingsConsole(self):
        storedTabScripts = self.settings.value("pythonConsole/tabScripts", [])
        self.tabListScript = storedTabScripts
        self.splitter.restoreState(
            self.settings.value("pythonConsole/splitterConsole", QByteArray()))
        self.splitterEditor.restoreState(
            self.settings.value("pythonConsole/splitterEditor", QByteArray()))
        self.splitterObj.restoreState(
            self.settings.value("pythonConsole/splitterObj", QByteArray()))
Example #32
0
class ProcessingPlugin:

    def __init__(self, iface):
        self.iface = iface
        self.options_factory = None
        self.drop_handler = None
        self.item_provider = None
        self.locator_filter = None
        self.edit_features_locator_filter = None
        self.initialized = False
        self.initProcessing()

    def initProcessing(self):
        if not self.initialized:
            self.initialized = True
            Processing.initialize()

    def initGui(self):
        self.options_factory = ProcessingOptionsFactory()
        self.options_factory.setTitle(self.tr('Processing'))
        iface.registerOptionsWidgetFactory(self.options_factory)
        self.drop_handler = ProcessingDropHandler()
        iface.registerCustomDropHandler(self.drop_handler)
        self.item_provider = ProcessingDataItemProvider()
        QgsApplication.dataItemProviderRegistry().addProvider(self.item_provider)
        self.locator_filter = AlgorithmLocatorFilter()
        iface.registerLocatorFilter(self.locator_filter)
        # Invalidate the locator filter for in-place when active layer changes
        iface.currentLayerChanged.connect(lambda _: self.iface.invalidateLocatorResults())
        self.edit_features_locator_filter = InPlaceAlgorithmLocatorFilter()
        iface.registerLocatorFilter(self.edit_features_locator_filter)

        self.toolbox = ProcessingToolbox()
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.toolbox)
        self.toolbox.hide()
        self.toolbox.visibilityChanged.connect(self.toolboxVisibilityChanged)

        self.resultsDock = ResultsDock()
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.resultsDock)
        self.resultsDock.hide()

        self.menu = QMenu(self.iface.mainWindow().menuBar())
        self.menu.setObjectName('processing')
        self.menu.setTitle(self.tr('Pro&cessing'))

        self.toolboxAction = QAction(self.tr('&Toolbox'), self.iface.mainWindow())
        self.toolboxAction.setCheckable(True)
        self.toolboxAction.setObjectName('toolboxAction')
        self.toolboxAction.setIcon(
            QgsApplication.getThemeIcon("/processingAlgorithm.svg"))
        self.iface.registerMainWindowAction(self.toolboxAction,
                                            QKeySequence('Ctrl+Alt+T').toString(QKeySequence.NativeText))
        self.toolboxAction.toggled.connect(self.openToolbox)
        self.iface.attributesToolBar().insertAction(self.iface.actionOpenStatisticalSummary(), self.toolboxAction)
        self.menu.addAction(self.toolboxAction)

        self.modelerAction = QAction(
            QgsApplication.getThemeIcon("/processingModel.svg"),
            QCoreApplication.translate('ProcessingPlugin', 'Graphical &Modeler…'), self.iface.mainWindow())
        self.modelerAction.setObjectName('modelerAction')
        self.modelerAction.triggered.connect(self.openModeler)
        self.iface.registerMainWindowAction(self.modelerAction,
                                            QKeySequence('Ctrl+Alt+M').toString(QKeySequence.NativeText))
        self.menu.addAction(self.modelerAction)

        self.historyAction = QAction(
            QgsApplication.getThemeIcon("/mIconHistory.svg"),
            QCoreApplication.translate('ProcessingPlugin', '&History…'), self.iface.mainWindow())
        self.historyAction.setObjectName('historyAction')
        self.historyAction.triggered.connect(self.openHistory)
        self.iface.registerMainWindowAction(self.historyAction,
                                            QKeySequence('Ctrl+Alt+H').toString(QKeySequence.NativeText))
        self.menu.addAction(self.historyAction)
        self.toolbox.processingToolbar.addAction(self.historyAction)

        self.resultsAction = QAction(
            QgsApplication.getThemeIcon("/processingResult.svg"),
            self.tr('&Results Viewer'), self.iface.mainWindow())
        self.resultsAction.setCheckable(True)
        self.iface.registerMainWindowAction(self.resultsAction,
                                            QKeySequence('Ctrl+Alt+R').toString(QKeySequence.NativeText))

        self.menu.addAction(self.resultsAction)
        self.toolbox.processingToolbar.addAction(self.resultsAction)
        self.resultsDock.visibilityChanged.connect(self.resultsAction.setChecked)
        self.resultsAction.toggled.connect(self.resultsDock.setUserVisible)

        self.toolbox.processingToolbar.addSeparator()

        self.editInPlaceAction = QAction(
            QgsApplication.getThemeIcon("/mActionProcessSelected.svg"),
            self.tr('Edit Features In-Place'), self.iface.mainWindow())
        self.editInPlaceAction.setObjectName('editInPlaceFeatures')
        self.editInPlaceAction.setCheckable(True)
        self.editInPlaceAction.toggled.connect(self.editSelected)
        self.menu.addAction(self.editInPlaceAction)
        self.toolbox.processingToolbar.addAction(self.editInPlaceAction)

        self.toolbox.processingToolbar.addSeparator()

        self.optionsAction = QAction(
            QgsApplication.getThemeIcon("/mActionOptions.svg"),
            self.tr('Options'), self.iface.mainWindow())
        self.optionsAction.setObjectName('optionsAction')
        self.optionsAction.triggered.connect(self.openProcessingOptions)
        self.toolbox.processingToolbar.addAction(self.optionsAction)

        menuBar = self.iface.mainWindow().menuBar()
        menuBar.insertMenu(
            self.iface.firstRightStandardMenu().menuAction(), self.menu)

        self.menu.addSeparator()

        initializeMenus()
        createMenus()

        # In-place editing button state sync
        self.iface.currentLayerChanged.connect(self.sync_in_place_button_state)
        self.iface.mapCanvas().selectionChanged.connect(self.sync_in_place_button_state)
        self.iface.actionToggleEditing().triggered.connect(partial(self.sync_in_place_button_state, None))
        self.sync_in_place_button_state()

    def sync_in_place_button_state(self, layer=None):
        """Synchronise the button state with layer state"""

        if layer is None:
            layer = self.iface.activeLayer()

        old_enabled_state = self.editInPlaceAction.isEnabled()

        new_enabled_state = layer is not None and layer.type() == QgsMapLayerType.VectorLayer
        self.editInPlaceAction.setEnabled(new_enabled_state)

        if new_enabled_state != old_enabled_state:
            self.toolbox.set_in_place_edit_mode(new_enabled_state and self.editInPlaceAction.isChecked())

    def openProcessingOptions(self):
        self.iface.showOptionsDialog(self.iface.mainWindow(), currentPage='processingOptions')

    def unload(self):
        self.toolbox.setVisible(False)
        self.iface.removeDockWidget(self.toolbox)
        self.iface.attributesToolBar().removeAction(self.toolboxAction)

        self.resultsDock.setVisible(False)
        self.iface.removeDockWidget(self.resultsDock)

        self.toolbox.deleteLater()
        self.menu.deleteLater()

        # delete temporary output files
        folder = QgsProcessingUtils.tempFolder()
        if QDir(folder).exists():
            shutil.rmtree(folder, True)

        # also delete temporary help files
        folder = tempHelpFolder()
        if QDir(folder).exists():
            shutil.rmtree(folder, True)

        self.iface.unregisterMainWindowAction(self.toolboxAction)
        self.iface.unregisterMainWindowAction(self.modelerAction)
        self.iface.unregisterMainWindowAction(self.historyAction)
        self.iface.unregisterMainWindowAction(self.resultsAction)

        self.iface.unregisterOptionsWidgetFactory(self.options_factory)
        self.iface.deregisterLocatorFilter(self.locator_filter)
        self.iface.deregisterLocatorFilter(self.edit_features_locator_filter)
        self.iface.unregisterCustomDropHandler(self.drop_handler)
        QgsApplication.dataItemProviderRegistry().removeProvider(self.item_provider)

        removeMenus()
        Processing.deinitialize()

    def openToolbox(self, show):
        self.toolbox.setUserVisible(show)

    def toolboxVisibilityChanged(self, visible):
        self.toolboxAction.setChecked(visible)

    def openModeler(self):
        dlg = ModelerDialog()
        dlg.update_model.connect(self.updateModel)
        dlg.show()

    def updateModel(self):
        model_provider = QgsApplication.processingRegistry().providerById('model')
        model_provider.refreshAlgorithms()

    def openResults(self):
        if self.resultsDock.isVisible():
            self.resultsDock.hide()
        else:
            self.resultsDock.show()

    def openHistory(self):
        dlg = HistoryDialog()
        dlg.exec_()

    def tr(self, message):
        return QCoreApplication.translate('ProcessingPlugin', message)

    def editSelected(self, enabled):
        self.toolbox.set_in_place_edit_mode(enabled)
    def initGui(self):
        # Add GUI elements
        self.splash_screen = ui.SplashScreen(self)
        self.config_dialog = ui.ConfigDialog()
        self.tilesManager = tiles.TilesManager()

        self.toolbar = self.iface.addToolBar("Travel Time Platform Toolbar")

        # Logo
        button = QPushButton(resources.logo, "")
        button.setIconSize(QSize(112, 30))
        button.setFlat(True)
        button.pressed.connect(self.show_splash)
        self.toolbar.addWidget(button)
        self.toolbar.addSeparator()

        # Show toolbox action
        self.action_show_toolbox = QAction(resources.icon_general,
                                           tr("Show the toolbox"),
                                           self.iface.mainWindow())
        self.action_show_toolbox.triggered.connect(self.show_toolbox)
        self.toolbar.addAction(self.action_show_toolbox)
        self.iface.addPluginToMenu(u"&Travel Time Platform",
                                   self.action_show_toolbox)

        # Add tiles
        tiles_menu = QMenu()
        for key, tile in self.tilesManager.tiles.items():
            action = QAction(tile["resource"], tile["label"], tiles_menu)
            action.triggered.connect(
                functools.partial(self.tilesManager.add_layer, key))
            action.setEnabled(self.tilesManager.has_tiles)
            tiles_menu.addAction(action)
        if not self.tilesManager.has_tiles:
            action = QAction(tr("Request access to backgrounds"), tiles_menu)
            action.triggered.connect(
                lambda: self.tilesManager.request_access())
            tiles_menu.addAction(action)

        tiles_button = QToolButton()
        tiles_button.setToolTip(tr("Add background"))
        tiles_button.setIcon(resources.icon_tiles)
        tiles_button.setMenu(tiles_menu)
        tiles_button.setPopupMode(QToolButton.InstantPopup)

        # self.action_show_toolbox.triggered.connect(self.show_toolbox)
        self.toolbar.addWidget(tiles_button)
        # self.iface.addPluginToMenu(u"&Travel Time Platform", self.action_show_toolbox)
        self.toolbar.addSeparator()

        # Show help actions
        self.action_show_help = QAction(resources.icon_help, tr("Help"),
                                        self.iface.mainWindow())
        self.action_show_help.triggered.connect(self.show_splash)
        self.toolbar.addAction(self.action_show_help)
        self.iface.addPluginToMenu(u"&Travel Time Platform",
                                   self.action_show_help)

        # Show config actions
        self.action_show_config = QAction(
            resources.icon_config,
            tr("Configure Travel Time Platform plugin"),
            self.iface.mainWindow(),
        )
        self.action_show_config.triggered.connect(self.show_config)
        self.toolbar.addAction(self.action_show_config)
        self.iface.addPluginToMenu(u"&Travel Time Platform",
                                   self.action_show_config)

        # Add the provider to the registry
        QgsApplication.processingRegistry().addProvider(self.provider)

        # Show splash screen
        if not QSettings().value(
                "travel_time_platform/spashscreen_dontshowagain", False,
                type=bool):
            self.show_splash()
Example #34
0
    def initGui(self):

        self.initProcessing()

        plugin_dir = str(Path(__file__).resolve().parent)
        main_icon = QIcon(os.path.join(plugin_dir, "icon.png"))

        # Open the online help
        if Qgis.QGIS_VERSION_INT >= 31000:
            self.help_action_about_menu = QAction(main_icon, 'Cadastre', self.iface.mainWindow())
            self.iface.pluginHelpMenu().addAction(self.help_action_about_menu)
            self.help_action_about_menu.triggered.connect(self.open_help)

        actions = {
            "import_action": (
                "database.png",
                "Importer des données",
                "",
                self.open_import_dialog,
                True
            ),
            "search_action": (
                "search.png",
                "Outils de recherche",
                "",
                self.toggle_search_dialog,
                True
            ),
            "load_action": (
                "output.png",
                "Charger des données",
                "",
                self.open_load_dialog,
                True
            ),
            "export_action": (
                "mActionSaveAsPDF.png",
                "Exporter la vue",
                "",
                self.export_view,
                True
            ),
            "option_action": (
                "config.png",
                "Configurer le plugin",
                "",
                self.open_option_dialog,
                True
            ),
            "about_action": (
                "about.png",
                "À propos",
                "",
                self.open_about_dialog,
                True
            ),
            "help_action": (
                "about.png",
                "Aide",
                "",
                self.open_help,
                True
            ),
            "version_action": (
                "about.png",
                "Notes de version",
                "",
                self.open_message_dialog,
                True
            )
        }

        for key in actions:
            icon_path = os.path.join(plugin_dir, 'icons', actions[key][0])
            icon = QIcon(icon_path)
            action = QAction(QIcon(icon), actions[key][1], self.iface.mainWindow())
            if actions[key][2] != "":
                action.setShortcut(QKeySequence(actions[key][2]))
            setattr(self, key, action)
            action.setEnabled(actions[key][4])
            action.setObjectName(key)
            action.triggered.connect(actions[key][3])

        if not self.cadastre_search_dialog:
            dialog = CadastreSearchDialog(self.iface)
            self.cadastre_search_dialog = dialog

        self.menu = QMenu("&Cadastre")
        self.menu.setObjectName("Cadastre")
        self.menu.setIcon(main_icon)

        # Add Cadastre to Extension menu
        self.menu.addAction(self.import_action)
        self.menu.addAction(self.load_action)
        self.menu.addSeparator()
        self.menu.addAction(self.search_action)
        self.menu.addAction(self.export_action)
        self.menu.addSeparator()
        self.menu.addAction(self.option_action)
        self.menu.addAction(self.about_action)
        self.menu.addAction(self.version_action)
        self.menu.addAction(self.help_action)

        menuBar = self.iface.mainWindow().menuBar()
        menu = menuBar
        for child in menuBar.children():
            if child.objectName() == "mPluginMenu":
                menu = child
                break
        menu.addMenu(self.menu)

        # Add cadastre toolbar
        self.toolbar = self.iface.addToolBar('&Cadastre')
        self.toolbar.setObjectName("cadastreToolbar")

        # Create action for "Parcelle information"
        self.identifyParcelleAction = QAction(
            QIcon(os.path.join(
                plugin_dir,
                "icons",
                "toolbar",
                "get-parcelle-info.png"
            )),
            "Infos parcelle",
            self.iface.mainWindow()
        )
        self.identifyParcelleAction.setCheckable(True)
        self.identifyParcelleAction.triggered.connect(self.setIndentifyParcelleTool)

        self.toolbar.addAction(self.import_action)
        self.toolbar.addAction(self.load_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.search_action)
        self.toolbar.addAction(self.identifyParcelleAction)
        self.toolbar.addAction(self.export_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.option_action)
        self.toolbar.addAction(self.about_action)

        self.setActionsExclusive()

        # Disable some dialogs on CI : about and changelog
        on_ci = os.getenv("QGIS_TESTING") == 'True'

        # Display About window on first use
        s = QSettings()
        firstUse = s.value("cadastre/isFirstUse", 1, type=int)
        if firstUse == 1 and not on_ci:
            s.setValue("cadastre/isFirstUse", 0)
            self.open_about_dialog()

        # Display some messages depending on version number
        mConfig = configparser.ConfigParser()
        metadataFile = plugin_dir + "/metadata.txt"
        mConfig.read(metadataFile, encoding='utf-8')
        self.mConfig = mConfig
        if not on_ci:
            myVersion = mConfig.get('general', 'version').replace('.', '_')
            myVersionMsg = s.value("cadastre/version_%s" % myVersion, 1, type=int)
            if myVersionMsg == 1:
                s.setValue("cadastre/version_%s" % myVersion, 0)
                self.open_message_dialog()

        # Project load or create : refresh search and identify tool
        self.iface.projectRead.connect(self.onProjectRead)
        self.iface.newProjectCreated.connect(self.onNewProjectCreated)

        # Delete layers from table when deleted from registry
        lr = QgsProject.instance()
        lr.layersRemoved.connect(self.checkIdentifyParcelleTool)

        self.updateSearchButton()

        self.cadastre_search_dialog.visibilityChanged.connect(self.updateSearchButton)
class ShapeTools(object):
    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.settingsDialog = None
        self.shapeDialog = None
        self.xyLineDialog = None
        self.geodesicDensifyDialog = None
        self.azDigitizerTool = None
        self.previousLayer = None
        self.toolbar = self.iface.addToolBar('Shape Tools Toolbar')
        self.toolbar.setObjectName('ShapeToolsToolbar')
        self.provider = ShapeToolsProvider()

    def initGui(self):
        self.azDigitizerTool = AzDigitizerTool(self.iface)

        # Initialize the create shape menu item
        icon = QIcon(os.path.dirname(__file__) + '/images/shapes.png')
        self.shapeAction = QAction(icon, tr('Create Shapes'),
                                   self.iface.mainWindow())
        self.shapeAction.setObjectName('stCreateShapes')
        self.shapeAction.triggered.connect(self.shapeTool)
        self.iface.addPluginToVectorMenu('Shape Tools', self.shapeAction)
        self.toolbar.addAction(self.shapeAction)

        # Initialize the XY to Line menu item
        icon = QIcon(os.path.dirname(__file__) + '/images/xyline.png')
        self.xyLineAction = QAction(icon, tr('XY to Line'),
                                    self.iface.mainWindow())
        self.xyLineAction.setObjectName('stXYtoLine')
        self.xyLineAction.triggered.connect(self.xyLineTool)
        self.iface.addPluginToVectorMenu('Shape Tools', self.xyLineAction)
        self.toolbar.addAction(self.xyLineAction)

        # Initialize the Geodesic Densifier menu item
        icon = QIcon(
            os.path.dirname(__file__) + '/images/geodesicDensifier.png')
        self.geodesicDensifyAction = QAction(icon,
                                             tr('Geodesic Shape Densifier'),
                                             self.iface.mainWindow())
        self.geodesicDensifyAction.setObjectName('stGeodesicDensifier')
        self.geodesicDensifyAction.triggered.connect(self.geodesicDensifyTool)
        self.iface.addPluginToVectorMenu('Shape Tools',
                                         self.geodesicDensifyAction)
        self.toolbar.addAction(self.geodesicDensifyAction)

        # Initialize Geodesic Measure Tool
        self.geodesicMeasureTool = GeodesicMeasureTool(self.iface,
                                                       self.iface.mainWindow())
        icon = QIcon(os.path.dirname(__file__) + '/images/measure.png')
        self.measureAction = QAction(icon, tr('Geodesic Measure Tool'),
                                     self.iface.mainWindow())
        self.measureAction.setObjectName('stGeodesicMeasureTool')
        self.measureAction.triggered.connect(self.measureTool)
        self.measureAction.setCheckable(True)
        self.iface.addPluginToVectorMenu('Shape Tools', self.measureAction)
        self.toolbar.addAction(self.measureAction)

        # Initialize the Azimuth Distance Digitize function
        icon = QIcon(os.path.dirname(__file__) + '/images/dazdigitize.png')
        self.digitizeAction = QAction(icon, tr('Azimuth Distance Digitizer'),
                                      self.iface.mainWindow())
        self.digitizeAction.setObjectName('stAzDistanceDigitizer')
        self.digitizeAction.triggered.connect(self.setShowAzDigitizerTool)
        self.digitizeAction.setCheckable(True)
        self.digitizeAction.setEnabled(False)
        self.iface.addPluginToVectorMenu(u'Shape Tools', self.digitizeAction)
        self.toolbar.addAction(self.digitizeAction)

        # Settings
        icon = QIcon(os.path.dirname(__file__) + '/images/settings.png')
        self.settingsAction = QAction(icon, tr('Settings'),
                                      self.iface.mainWindow())
        self.settingsAction.setObjectName('shapeToolsSettings')
        self.settingsAction.triggered.connect(self.settings)
        self.iface.addPluginToVectorMenu('Shape Tools', self.settingsAction)

        # Help
        icon = QIcon(os.path.dirname(__file__) + '/images/help.png')
        self.helpAction = QAction(icon, tr('Shape Tools Help'),
                                  self.iface.mainWindow())
        self.helpAction.setObjectName('shapeToolsHelp')
        self.helpAction.triggered.connect(self.help)
        self.iface.addPluginToVectorMenu('Shape Tools', self.helpAction)

        self.iface.currentLayerChanged.connect(self.currentLayerChanged)
        self.canvas.mapToolSet.connect(self.unsetTool)
        self.enableDigitizeTool()

        # Add the processing provider
        QgsApplication.processingRegistry().addProvider(self.provider)

    def unsetTool(self, tool):
        try:
            if not isinstance(tool, GeodesicMeasureTool):
                self.measureAction.setChecked(False)
                self.geodesicMeasureTool.closeDialog()
            if not isinstance(tool, AzDigitizerTool):
                self.digitizeAction.setChecked(False)
        except:
            pass

    def unload(self):
        self.canvas.unsetMapTool(self.azDigitizerTool)

        # remove from menu
        self.iface.removePluginVectorMenu('Shape Tools', self.shapeAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.xyLineAction)
        self.iface.removePluginVectorMenu('Shape Tools',
                                          self.geodesicDensifyAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.measureAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.digitizeAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.settingsAction)
        self.iface.removePluginVectorMenu('Shape Tools', self.helpAction)
        # Remove from toolbar
        self.iface.removeToolBarIcon(self.shapeAction)
        self.iface.removeToolBarIcon(self.xyLineAction)
        self.iface.removeToolBarIcon(self.geodesicDensifyAction)
        self.iface.removeToolBarIcon(self.measureAction)
        self.iface.removeToolBarIcon(self.digitizeAction)
        self.azDigitizerTool = None
        # remove the toolbar
        del self.toolbar

        QgsApplication.processingRegistry().removeProvider(self.provider)

    def shapeTool(self):
        if self.shapeDialog is None:
            self.shapeDialog = Vector2ShapeWidget(self.iface,
                                                  self.iface.mainWindow())
        self.shapeDialog.show()

    def setShowAzDigitizerTool(self):
        self.digitizeAction.setChecked(True)
        self.canvas.setMapTool(self.azDigitizerTool)

    def xyLineTool(self):
        results = processing.execAlgorithmDialog('shapetools:xy2line', {})

    def geodesicDensifyTool(self):
        results = processing.execAlgorithmDialog(
            'shapetools:geodesicdensifier', {})

    def measureTool(self):
        self.measureAction.setChecked(True)
        self.canvas.setMapTool(self.geodesicMeasureTool)

    def settings(self):
        if self.settingsDialog is None:
            self.settingsDialog = SettingsWidget(self.iface,
                                                 self.iface.mainWindow())
        self.settingsDialog.show()

    def help(self):
        '''Display a help page'''
        url = QUrl.fromLocalFile(os.path.dirname(__file__) +
                                 '/index.html').toString()
        webbrowser.open(url, new=2)

    def currentLayerChanged(self):
        layer = self.iface.activeLayer()
        if self.previousLayer != None:
            try:
                self.previousLayer.editingStarted.disconnect(
                    self.layerEditingChanged)
            except:
                pass
            try:
                self.previousLayer.editingStopped.disconnect(
                    self.layerEditingChanged)
            except:
                pass
        self.previousLayer = None
        if layer != None:
            if isinstance(layer, QgsVectorLayer) and (
                (layer.wkbType() == QgsWkbTypes.Point) or
                (layer.wkbType() == QgsWkbTypes.LineString) or
                (layer.wkbType() == QgsWkbTypes.MultiLineString)):
                layer.editingStarted.connect(self.layerEditingChanged)
                layer.editingStopped.connect(self.layerEditingChanged)
                self.previousLayer = layer
        self.enableDigitizeTool()

    def layerEditingChanged(self):
        self.enableDigitizeTool()

    def enableDigitizeTool(self):
        self.digitizeAction.setEnabled(False)
        layer = self.iface.activeLayer()

        if layer != None and isinstance(layer, QgsVectorLayer) and (
            (layer.wkbType() == QgsWkbTypes.Point) or
            (layer.wkbType() == QgsWkbTypes.LineString) or
            (layer.wkbType()
             == QgsWkbTypes.MultiLineString)) and layer.isEditable():
            self.digitizeAction.setEnabled(True)
        else:
            self.digitizeAction.setChecked(False)
Example #36
0
class SplinePlugin(object):
    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.plugin_dir = os.path.dirname(__file__)
        self.proc_provider = None
        locale = QSettings().value("locale/userLocale")[0:2]
        locale_path = os.path.join(self.plugin_dir, "i18n",
                                   "splineplugin_{}.qm".format(locale))
        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        self.tool = SplineTool(self.iface)
        self.connected_layer = None
        self.settings_dialog = None
        self.action_settings = None
        self.action_spline = None

    def initProcessing(self):
        self.proc_provider = proc_provider()
        QgsApplication.processingRegistry().addProvider(self.proc_provider)

    def initGui(self):
        self.initProcessing()
        self.action_settings = QAction(
            QCoreApplication.translate("Spline", "Settings"),
            self.iface.mainWindow())
        self.action_settings.setObjectName("splineAction")
        self.action_settings.triggered.connect(self.open_settings)

        self.iface.addPluginToVectorMenu(u"Digitize Spline",
                                         self.action_settings)

        ico = QIcon(icon_path("icon.png"))
        self.action_spline = QAction(
            ico,
            QCoreApplication.translate("spline", "Digitize Splines"),
            self.iface.mainWindow(),
        )
        self.action_spline.setObjectName("actionSpline")
        self.action_spline.setEnabled(False)
        self.action_spline.setCheckable(True)

        # Connect to signals for button behaviour
        self.action_spline.triggered.connect(self.digitize)
        self.iface.currentLayerChanged.connect(self.layer_changed)
        self.layer_changed()  # to enable when plugin is loaded

        self.canvas.mapToolSet.connect(self.deactivate)

        # Add actions to the toolbar
        self.iface.addToolBarIcon(self.action_spline)

    def unload(self):
        QgsApplication.processingRegistry().removeProvider(self.proc_provider)
        self.iface.removeToolBarIcon(self.action_spline)
        self.iface.removePluginVectorMenu(u"Digitize Spline",
                                          self.action_settings)

    def open_settings(self):
        self.settings_dialog = SettingsDialog()
        self.settings_dialog.changed.connect(self.settings_changed)
        self.settings_dialog.show()

    def digitize(self):
        self.canvas.setMapTool(self.tool)
        self.action_spline.setChecked(True)

    def is_active_layer_for_spline(self):
        layer = self.iface.activeLayer()
        if layer is None:
            return False
        if layer.type() != QgsMapLayerType.VectorLayer:
            return False
        if not layer.geometryType() in (
                QgsWkbTypes.LineGeometry,
                QgsWkbTypes.PolygonGeometry,
        ):
            return False
        return True

    def enable_action(self):
        self.action_spline.setEnabled(False)
        self.action_spline.setChecked(False)
        if self.is_active_layer_for_spline():
            if self.iface.activeLayer().isEditable():
                self.action_spline.setEnabled(True)

    def layer_changed(self):
        self.deactivate()
        self.tool.deactivate()
        self.enable_action()
        self.connect_layer()

    def connect_layer(self):
        self.disconnect_layer()
        if not self.is_active_layer_for_spline():
            return
        layer = self.iface.activeLayer()
        if layer is None:
            return
        self.connected_layer = layer
        layer.editingStopped.connect(self.enable_action)
        layer.editingStarted.connect(self.enable_action)

    def disconnect_layer(self):
        try:
            self.connected_layer.editingStopped.disconnect(self.enable_action)
            self.connected_layer.editingStarted.disconnect(self.enable_action)
        except (RuntimeError, AttributeError, TypeError):
            pass
        self.connected_layer = None

    def deactivate(self):
        self.tool.deactivate()
        self.action_spline.setChecked(False)

    def settings_changed(self):
        self.tool.refresh()
Example #37
0
def create_action(
        icon_path,
        text,
        callback,
        enabled_flag=True,
        status_tip=None,
        whats_this=None,
        parent=None,
        object_name=None):

    """
    # adapted from RedLayers by E. Ferreguti
    Create an action.

    :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 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.

    :param object_name: Optional name to identify objects during customization
    :type object_name: str

    :returns: The action that was created.
    :rtype: QAction
    """

    icon = QIcon(icon_path)

    action = QAction(icon, text, parent)
    if callback:
        action.triggered.connect(callback)
    action.setEnabled(enabled_flag)

    if status_tip:
        action.setStatusTip(status_tip)

    if whats_this:
        action.setWhatsThis(whats_this)

    if object_name:
        action.setObjectName(object_name)

    return action
Example #38
0
class PlanetAOIFilter(AOI_FILTER_BASE, AOI_FILTER_WIDGET, PlanetFilterMixin):

    filtersChanged = pyqtSignal()
    savedSearchSelected = pyqtSignal(object)
    zoomToAOIRequested = pyqtSignal()

    def __init__(
        self,
        parent=None,
        plugin=None,
        color=MAIN_AOI_COLOR,
    ):
        super().__init__(parent=parent)
        self._plugin = plugin

        self.setupUi(self)

        self.emitFiltersChanged = False

        self.color = color

        self._aoi_box = QgsRubberBand(iface.mapCanvas(),
                                      QgsWkbTypes.PolygonGeometry)
        self._aoi_box.setFillColor(QColor(0, 0, 0, 0))
        self._aoi_box.setStrokeColor(color)
        self._aoi_box.setWidth(3)
        self._aoi_box.setLineStyle(Qt.DashLine)

        self._canvas: QgsMapCanvas = iface.mapCanvas()
        # This may later be a nullptr, if no active tool when queried
        self._cur_maptool = None

        self.leAOI.textChanged["QString"].connect(self.filters_changed)
        self.leAOI.textEdited["QString"].connect(self.validate_edited_aoi)

        self._setup_tool_buttons()

        # Extent line edit tools
        self.btnZoomToAOI.clicked.connect(self.zoom_to_aoi)
        self.btnCopyAOI.clicked.connect(self.copy_aoi_to_clipboard)

        self.p_client = PlanetClient.getInstance()

    def reset_aoi_box(self):
        self.leAOI.setText("")
        if self._aoi_box:
            self._aoi_box.reset(QgsWkbTypes.PolygonGeometry)

    def filters(self):
        filters = []
        if self.leAOI.text():
            try:
                qgsgeom = qgsgeometry_from_geojson(self.leAOI.text())
                if not qgsgeom.isEmpty():
                    geom_json = json.loads(qgsgeom.asJson())
                    filters.append(geom_filter(geom_json))
                else:
                    self._show_message("AOI not valid GeoJSON polygon",
                                       level=Qgis.Warning,
                                       duration=10)
            except Exception:
                self._show_message("AOI not valid JSON",
                                   level=Qgis.Warning,
                                   duration=10)
            finally:
                return filters

    def filters_as_json(self):
        filters = []
        if self.leAOI.text():
            filters.append(self.leAOI.text())

        return filters

    def set_from_request(self, request):
        self.emitFiltersChanged = False
        filters = filters_from_request(request, "geometry")
        if filters:
            geom = filters[0]["config"]
            txt = json.dumps(geom)
            self.leAOI.setText(txt)
        else:
            self.leAOI.setText("")
        self.emitFiltersChanged = True

    @pyqtSlot("QString")
    def filters_changed(self, value):
        if self.emitFiltersChanged:
            self.filtersChanged.emit()

    @pyqtSlot()
    def clean_up(self):
        self.reset_aoi_box()

    def _setup_tool_buttons(self):
        extent_menu = QMenu(self)

        canvas_act = QAction("Current visible extent", extent_menu)
        canvas_act.triggered[bool].connect(self.aoi_from_current_extent)
        extent_menu.addAction(canvas_act)

        active_act = QAction("Active map layer extent", extent_menu)
        active_act.triggered[bool].connect(self.aoi_from_active_layer_extent)
        extent_menu.addAction(active_act)

        full_act = QAction("All map layers extent", extent_menu)
        full_act.triggered[bool].connect(self.aoi_from_full_extent)
        extent_menu.addAction(full_act)

        self.btnExtent.setMenu(extent_menu)

        # Also show menu on click, to keep disclosure triangle visible
        self.btnExtent.clicked.connect(self.btnExtent.showMenu)

        draw_menu = QMenu(self)

        box_act = QAction("Rectangle", draw_menu)
        box_act.triggered[bool].connect(self.aoi_from_box)
        draw_menu.addAction(box_act)

        circle_act = QAction("Circle", draw_menu)
        circle_act.triggered[bool].connect(self.aoi_from_circle)
        draw_menu.addAction(circle_act)

        polygon_act = QAction("Polygon", draw_menu)
        polygon_act.triggered[bool].connect(self.aoi_from_polygon)
        draw_menu.addAction(polygon_act)

        self.btnDraw.setMenu(draw_menu)
        # Also show menu on click, to keep disclosure triangle visible
        self.btnDraw.clicked.connect(self.btnDraw.showMenu)

        selection_menu = QMenu(self)

        self.single_select_act = QAction("Single feature", selection_menu)
        self.single_select_act.triggered[bool].connect(self.aoi_from_feature)
        selection_menu.addAction(self.single_select_act)

        self.bound_select_act = QAction("Multiple features (bounding box)",
                                        selection_menu)
        self.bound_select_act.triggered[bool].connect(self.aoi_from_bound)
        selection_menu.addAction(self.bound_select_act)

        self.btnSelection.setMenu(selection_menu)
        # Also show menu on click, to keep disclosure triangle visible
        self.btnSelection.clicked.connect(self._toggle_selection_tools)
        self.btnSelection.clicked.connect(self.btnSelection.showMenu)

        upload_menu = QMenu(self)

        upload_act = QAction("Upload vector layer file", upload_menu)
        upload_act.triggered[bool].connect(self.upload_file)
        upload_menu.addAction(upload_act)

        self.btnUpload.setMenu(upload_menu)
        self.btnUpload.clicked.connect(self.btnUpload.showMenu)

    def upload_file(self):
        filename, _ = QFileDialog.getOpenFileName(self, "Select AOI file", "",
                                                  "All files(*.*)")
        if filename:
            layer = QgsVectorLayer(filename, "")
            self.aoi_from_layer(layer)

    def aoi_from_layer(self, layer):
        if not layer.isValid():
            self._show_message("Invalid layer",
                               level=Qgis.Warning,
                               duration=10)
        else:
            feature = next(layer.getFeatures(), None)
            if feature is None:
                self._show_message("Layer contains no features",
                                   level=Qgis.Warning,
                                   duration=10)
            else:
                geom = feature.geometry()

                transform = QgsCoordinateTransform(
                    layer.crs(),
                    QgsCoordinateReferenceSystem("EPSG:4326"),
                    QgsProject.instance(),
                )

                try:
                    geom.transform(transform)
                except QgsCsException:
                    self._show_message(
                        "Could not convert AOI to EPSG:4326",
                        level=Qgis.Warning,
                        duration=10,
                    )
                    return

                geom_json = geom.asJson(precision=6)

                self._aoi_box.setToGeometry(geom)

                self.leAOI.setText(geom_json)

                log.debug("AOI set to layer")

                self.zoom_to_aoi()

    def _toggle_selection_tools(self):
        active_layer = iface.activeLayer()
        is_vector = isinstance(active_layer, QgsVectorLayer)
        if is_vector and active_layer.selectedFeatureCount():
            if active_layer.selectedFeatureCount() > 1:
                self.single_select_act.setEnabled(False)
                self.bound_select_act.setEnabled(True)
            elif active_layer.selectedFeatureCount():
                self.single_select_act.setEnabled(True)
                self.bound_select_act.setEnabled(False)
            else:
                self.single_select_act.setEnabled(False)
                self.bound_select_act.setEnabled(False)
        else:
            self.single_select_act.setEnabled(False)
            self.bound_select_act.setEnabled(False)

    @pyqtSlot()
    def aoi_from_current_extent(self):
        """Return current map extent as geojson transformed to EPSG:4326"""
        canvas = iface.mapCanvas()
        transform = QgsCoordinateTransform(
            QgsProject.instance().crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance(),
        )

        canvas_extent: QgsRectangle = canvas.extent()
        try:
            transform_extent = transform.transformBoundingBox(canvas_extent)
        except QgsCsException:
            self._show_message("Could not convert AOI to EPSG:4326",
                               level=Qgis.Warning,
                               duration=10)
            return
        geom_extent = QgsGeometry.fromRect(transform_extent)
        extent_json = geom_extent.asJson(precision=6)

        self._aoi_box.setToGeometry(QgsGeometry.fromRect(canvas.extent()))

        self.leAOI.setText(extent_json)

        log.debug("AOI set to canvas extent")

        self.zoom_to_aoi()

    @pyqtSlot()
    def aoi_from_active_layer_extent(self):
        """Return active map layer extent as geojson transformed to EPSG:4326"""
        map_layer: QgsMapLayer = iface.activeLayer()
        if map_layer is None:
            log.debug("No active layer selected, skipping AOI extent")
            return

        if not map_layer.isValid():
            log.debug("Active map layer invalid, skipping AOI extent")
            return

        transform = QgsCoordinateTransform(
            map_layer.crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance(),
        )

        ml_extent: QgsRectangle = map_layer.extent()
        try:
            transform_extent = transform.transformBoundingBox(ml_extent)
        except QgsCsException:
            self._show_message("Could not convert AOI to EPSG:4326",
                               level=Qgis.Warning,
                               duration=10)
            return
        geom_extent = QgsGeometry.fromRect(transform_extent)
        extent_json = geom_extent.asJson(precision=6)

        self.leAOI.setText(extent_json)

        log.debug("AOI set to active layer extent")

        self.zoom_to_aoi()

    @pyqtSlot()
    def aoi_from_full_extent(self):
        """Return full data map extent as geojson transformed to EPSG:4326"""
        canvas = iface.mapCanvas()

        transform = QgsCoordinateTransform(
            QgsProject.instance().crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance(),
        )

        canvas_extent: QgsRectangle = canvas.fullExtent()
        if canvas_extent.isNull():  # Canvas not yet initialized
            return
        try:
            transform_extent = transform.transformBoundingBox(canvas_extent)
        except QgsCsException:
            self._show_message("Could not convert AOI to EPSG:4326",
                               level=Qgis.Warning,
                               duration=10)
            return
        geom_extent = QgsGeometry.fromRect(transform_extent)
        extent_json = geom_extent.asJson(precision=6)
        self._aoi_box.setToGeometry(QgsGeometry.fromRect(canvas_extent))

        self.leAOI.setText(extent_json)

        log.debug("AOI set to full data extent")

        self.zoom_to_aoi()

    @pyqtSlot()
    def aoi_from_box(self):
        self._cur_maptool: QgsMapTool = self._canvas.mapTool()
        self._aoi_box.reset(QgsWkbTypes.PolygonGeometry)
        aoi_draw = PlanetExtentMapTool(iface.mapCanvas())
        iface.mapCanvas().setMapTool(aoi_draw)
        aoi_draw.extentSelected.connect(self.set_draw_aoi)

    @pyqtSlot()
    def aoi_from_circle(self):
        self._cur_maptool: QgsMapTool = self._canvas.mapTool()
        self._aoi_box.reset(QgsWkbTypes.PolygonGeometry)
        aoi_draw = PlanetCircleMapTool(iface.mapCanvas())
        iface.mapCanvas().setMapTool(aoi_draw)
        aoi_draw.circleSelected.connect(self.set_draw_aoi)

    @pyqtSlot()
    def aoi_from_polygon(self):
        self._cur_maptool: QgsMapTool = self._canvas.mapTool()
        self._aoi_box.reset(QgsWkbTypes.PolygonGeometry)
        aoi_draw = PlanetPolyMapTool(iface.mapCanvas())
        iface.mapCanvas().setMapTool(aoi_draw)
        aoi_draw.polygonSelected.connect(self.set_draw_aoi)

    @pyqtSlot(object)
    def set_draw_aoi(self, aoi):
        transform = QgsCoordinateTransform(
            QgsProject.instance().crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance(),
        )

        aoi_json = None

        if isinstance(aoi, QgsRectangle):
            aoi_geom = QgsGeometry().fromRect(aoi)
            self._aoi_box.setToGeometry(aoi_geom)
            aoi_geom.transform(transform)
            aoi_json = aoi_geom.asJson(precision=6)

        if isinstance(aoi, QgsGeometry):
            self._aoi_box.setToGeometry(aoi)
            # TODO: validate geom is less than 500 vertices
            aoi.transform(transform)
            aoi_json = aoi.asJson(precision=6)

        if aoi_json:
            self.leAOI.setText(aoi_json)

            self._show_message("AOI set to drawn figure")
            self.zoom_to_aoi()
            if self._cur_maptool is not None:
                # Restore previously used maptool
                self._canvas.setMapTool(self._cur_maptool)
                self._cur_maptool = None
            else:
                # Fallback to activating pan tool
                iface.actionPan().trigger()
        else:
            self._show_message("AOI unable to be set",
                               level=Qgis.Warning,
                               duration=10)

    @pyqtSlot()
    def aoi_from_feature(self):
        layer = iface.activeLayer()
        if not isinstance(layer, QgsVectorLayer):
            self._show_message("Active layer must be a vector layer.",
                               level=Qgis.Warning,
                               duration=10)
            return

        if layer.selectedFeatureCount() > 1:
            self._show_message(
                "More than 1 feature. Searching by bbox.",
                level=Qgis.Warning,
                duration=10,
            )
            self.aoi_from_bound()
            return
        elif layer.selectedFeatureCount() < 1:
            self._show_message("No features selected.",
                               level=Qgis.Warning,
                               duration=10)
            return

        selected: QgsFeature = layer.selectedFeatures()[0]
        geom: QgsGeometry = selected.geometry()

        if geom.constGet().vertexCount() > 500:
            self._show_message(
                "More than 500 vertices. Searching by bbox.",
                level=Qgis.Warning,
                duration=10,
            )
            self.aoi_from_bound()
            return

        trans_layer = QgsCoordinateTransform(
            layer.sourceCrs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance(),
        )

        trans_canvas = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance().crs(),
            QgsProject.instance(),
        )

        # geom.transform(transform)
        geom.transform(trans_layer)
        geom_json = geom.asJson(precision=6)
        self.leAOI.setText(geom_json)

        geom.transform(trans_canvas)
        self._aoi_box.setToGeometry(geom,
                                    QgsCoordinateReferenceSystem("EPSG:4326"))
        self.zoom_to_aoi()

    @pyqtSlot()
    def aoi_from_bound(self):
        layer = iface.activeLayer()
        if not isinstance(layer, QgsVectorLayer):
            self._show_message("Active layer must be a vector layer.",
                               level=Qgis.Warning,
                               duration=10)
            return

        if layer.selectedFeatureCount() < 1:
            self._show_message("No features selected.",
                               level=Qgis.Warning,
                               duration=10)
            return

        bbox = layer.boundingBoxOfSelected()

        trans_layer = QgsCoordinateTransform(
            layer.sourceCrs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance(),
        )

        trans_canvas = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance().crs(),
            QgsProject.instance(),
        )

        transform_bbox = trans_layer.transformBoundingBox(bbox)
        geom_bbox = QgsGeometry.fromRect(transform_bbox)
        bbox_json = geom_bbox.asJson(precision=6)

        self.leAOI.setText(bbox_json)

        bbox_canvas = trans_canvas.transformBoundingBox(transform_bbox)
        self._aoi_box.setToGeometry(QgsGeometry.fromRect(bbox_canvas))

        self.zoom_to_aoi()

    def hide_aoi_if_matches_geom(self, geom):
        color = (QColor(0, 0, 0, 0)
                 if self._aoi_box.asGeometry().equals(geom) else self.color)
        self._aoi_box.setStrokeColor(color)

    def show_aoi(self):
        if self._aoi_box is not None:
            self._aoi_box.setStrokeColor(self.color)

    def aoi_geom(self):
        if self._aoi_box is not None:
            return self._aoi_box.asGeometry()

    def aoi_as_4326_geom(self):
        transform = QgsCoordinateTransform(
            QgsProject.instance().crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance(),
        )
        geom = self.aoi_geom()
        if geom is not None:
            geom.transform(transform)
        return geom

    @pyqtSlot()
    def zoom_to_aoi(self):
        if not self.leAOI.text():
            log.debug("No AOI defined, skipping zoom to AOI")
            return

        geom: QgsGeometry = qgsgeometry_from_geojson(self.leAOI.text())
        if geom.isEmpty():
            self._show_message("AOI GeoJSON geometry invalid",
                               level=Qgis.Warning,
                               duration=10)
            return

        self._aoi_box.setToGeometry(geom,
                                    QgsCoordinateReferenceSystem("EPSG:4326"))

        self.show_aoi()

        zoom_canvas_to_aoi(self.leAOI.text())

        self.zoomToAOIRequested.emit()

    @pyqtSlot()
    def copy_aoi_to_clipboard(self):
        if not self.leAOI.text():
            log.debug("No AOI defined, skipping zoom to AOI")
            return

        try:
            json_obj = json.loads(self.leAOI.text())
        except ValueError:
            return

        json_geom_txt = json.dumps(json_obj, indent=2)

        cb = QgsApplication.clipboard()
        cb.setText(json_geom_txt)

        self._show_message("AOI copied to clipboard")

    @pyqtSlot()
    def validate_edited_aoi(self):
        json_txt = self.leAOI.text()
        if not json_txt:
            self.reset_aoi_box()
            log.debug("No AOI defined, skipping validation")
            return

        try:
            json_obj = json.loads(json_txt)
        except ValueError:
            self._show_message("AOI GeoJSON is invalid",
                               level=Qgis.Warning,
                               duration=10)
            return

        try:
            json_geom = geometry_from_json(json_obj)
        except Exception:
            json_geom = None

        if not json_geom:
            self._show_message("AOI GeoJSON geometry invalid",
                               level=Qgis.Warning,
                               duration=10)
            return

        geom: QgsGeometry = qgsgeometry_from_geojson(json_geom)
        self._aoi_box.setToGeometry(geom,
                                    QgsCoordinateReferenceSystem("EPSG:4326"))

        self.leAOI.blockSignals(True)
        self.leAOI.setText(json.dumps(json_geom))
        self.leAOI.blockSignals(False)

        self.zoom_to_aoi()
Example #39
0
class WellLogView(QWidget):

    DEFAULT_COLUMN_WIDTH = 150

    # Emitted when some styles have been updated
    styles_updated = pyqtSignal()

    def __init__(self, title=None,image_dir=None, parent=None):
        QWidget.__init__(self, parent)

        self.toolbar = QToolBar()
        self.__log_scene = MyScene(0, 0, 600, 600)
        self.__log_view = LogGraphicsView(self.__log_scene)
        self.__log_view.setAlignment(Qt.AlignLeft|Qt.AlignTop)

        self.__log_scene.sceneRectChanged.connect(self.on_rect_changed)

        if image_dir is None:
            image_dir = os.path.join(os.path.dirname(__file__), "img")

        self.__action_move_column_left = QAction(QIcon(os.path.join(image_dir, "left.svg")), "Move the column to the left", self.toolbar)
        self.__action_move_column_left.triggered.connect(self.on_move_column_left)
        self.__action_move_column_right = QAction(QIcon(os.path.join(image_dir, "right.svg")), "Move the column to the right", self.toolbar)
        self.__action_move_column_right.triggered.connect(self.on_move_column_right)

        self.__action_edit_style = QAction(QIcon(os.path.join(image_dir, "symbology.svg")), "Edit column style", self.toolbar)
        self.__action_edit_style.triggered.connect(self.on_edit_style)

        self.__action_add_column = QAction(QIcon(os.path.join(image_dir, "add.svg")), "Add a data column from configured ones", self.toolbar)
        self.__action_add_column.triggered.connect(self.on_add_column)

        self.__action_remove_column = QAction(QIcon(os.path.join(image_dir, "remove.svg")), "Remove the column", self.toolbar)
        self.__action_remove_column.triggered.connect(self.on_remove_column)

        #self.__action_move_content_right = QAction("Move content right", self.toolbar)
        #self.__action_move_content_left = QAction("Move content left", self.toolbar)
        #self.__action_move_content_left.triggered.connect(self.on_move_content_left)
        #self.__action_move_content_right.triggered.connect(self.on_move_content_right)

        self.toolbar.addAction(self.__action_move_column_left)
        self.toolbar.addAction(self.__action_move_column_right)
        self.toolbar.addAction(self.__action_edit_style)
        self.toolbar.addAction(self.__action_add_column)
        self.toolbar.addAction(self.__action_remove_column)

        #self.__toolbar.addAction(self.__action_move_content_left)
        #self.__toolbar.addAction(self.__action_move_content_right)

        self.__title_label = QLabel()
        if title is not None:
            self.set_title(title)

        self.__status_bar = QStatusBar()

        vbox = QVBoxLayout()
        vbox.addWidget(self.__title_label)
        vbox.addWidget(self.toolbar)
        vbox.addWidget(self.__log_view)
        vbox.addWidget(self.__status_bar)
        self.setLayout(vbox)

        self.__station_id = None
        # (log_item, legend_item) for each column
        self.__columns = []
        # { layer : (log_item, legend_item) }
        self.__data2logitems = {}
        self.__column_widths = []

        self._min_z = 0
        self._max_z = 40

        self.__allow_mouse_translation = True
        self.__translation_orig = None

        self.__style_dir = os.path.join(os.path.dirname(__file__),
                                        'styles')

        self.select_column(-1)

        # by default we have a Z scale
        self.add_z_scale()
        
    def on_rect_changed(self, rect):
        for item, _ in self.__columns:
            item.set_height(rect.height())

    def set_title(self, title):
        self.__title_label.setText(title)

    def _place_items(self):
        x = 0
        for i, c in enumerate(self.__columns):
            item, legend = c
            width = self.__column_widths[i]
            legend.setPos(x, 0)
            item.setPos(x, legend.boundingRect().height())
            x += width

        rect = self.__log_scene.sceneRect()
        rect.setWidth(x)
        self.__log_scene.setSceneRect(rect)

    def _add_column(self, log_item, legend_item):
        self.__log_scene.addItem(log_item)
        self.__log_scene.addItem(legend_item)

        log_item.set_min_depth(self._min_z)
        log_item.set_max_depth(self._max_z)
        self.__columns.append((log_item, legend_item))
        self.__column_widths.append(log_item.boundingRect().width())

        self._place_items()

    def _fit_to_max_depth(self):
        self._min_z = min([i.min_depth() for i, _ in self.__columns if i.min_depth() is not None])
        self._max_z = max([i.max_depth() for i, _ in self.__columns if i.max_depth() is not None])
        # if we have only one value, center it on a 2 meters range
        if self._min_z == self._max_z:
            self._min_z -= 1.0
            self._max_z += 1.0

    def _update_column_depths(self):
        for item, _ in self.__columns:
            item.set_min_depth(self._min_z)
            item.set_max_depth(self._max_z)
            item.update()

    def add_z_scale(self, title="Depth"):
        scale_item = ZScaleItem(self.DEFAULT_COLUMN_WIDTH / 2, self.__log_scene.height(), self._min_z, self._max_z)
        legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH / 2, title, unit_of_measure="m")
        self._add_column(scale_item, legend_item)

    def remove_data_column(self, data):
        """Remove data column from widget

        :param data: data to be removed
        """

        # Column doesn't exist
        if data not in self.__data2logitems:
            raise ValueError("Impossible to remove data column : given data"
                             " object doesn't exist")

        log_item, legend_item = self.__data2logitems[data]
        for i, (pitem, litem) in enumerate(self.__columns):
            if pitem == log_item and litem == legend_item:
                self.__columns.pop(i)
                self.__column_widths.pop(i)
                del self.__data2logitems[data]
                self.__log_scene.removeItem(log_item)
                self.__log_scene.removeItem(legend_item)
                return

        # Columns not found
        assert False

    def clear_data_columns(self):
        # remove item from scenes
        for (item, legend) in self.__columns:
            self.__log_scene.removeItem(legend)
            self.__log_scene.removeItem(item)

        # remove from internal lists
        self.__columns = []
        self.__column_widths = []
        self.__data2logitems = {}
        
        self.__selected_column = -1
        self._place_items()
        self._update_button_visibility()

        # still add z scale
        self.add_z_scale()

    def on_plot_tooltip(self, txt, station_name = None):
        if station_name is not None:
            self.__status_bar.showMessage(u"Station: {} ".format(station_name) + txt)
        else:
            self.__status_bar.showMessage(txt)

    def add_data_column(self, data, title, uom, station_name = None, config = None):
        """
        Parameters
        ----------
        data: ??
        title: str
        uom: str
          Unit of measure
        station_name: str
          Station name
        config: PlotConfig
        """
        symbology, symbology_type = config.get_symbology()
        
        plot_item = PlotItem(size=QSizeF(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height()),
                             render_type = POLYGON_RENDERER if symbology_type is None else symbology_type,
                             symbology = symbology,
                             x_orientation = ORIENTATION_DOWNWARD,
                             y_orientation = ORIENTATION_LEFT_TO_RIGHT)
        plot_item.style_updated.connect(self.styles_updated)

        plot_item.set_layer(data.get_layer())
        plot_item.tooltipRequested.connect(lambda txt: self.on_plot_tooltip(txt, station_name))

        legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title, unit_of_measure=uom)
        data.data_modified.connect(lambda data=data : self._update_data_column(data, config))

        self.__data2logitems[data] = (plot_item, legend_item)
        self._add_column(plot_item, legend_item)
        self._update_data_column(data, config)
        self._update_column_depths()

    def _update_data_column(self, data, config):

        plot_item, legend_item = self.__data2logitems[data]

        y_values = data.get_y_values()
        x_values = data.get_x_values()
        if y_values is None or x_values is None:
            plot_item.set_data_window(None)
            return

        plot_item.set_data(data.get_x_values(), data.get_y_values())
        win = plot_item.data_window()
        min_x, min_y, max_x, max_y = win.left(), win.top(), win.right(), win.bottom()

        if config and config.get('min') is not None:
            min_y = float(config['min'])
        if config and config.get('max') is not None:
            max_y = float(config['max'])

        # legend
        legend_item.set_scale(min_y, max_y)
        plot_item.set_data_window(QRectF(min_x, min_y, max_x-min_x, max_y-min_y))

        self.__log_scene.update()

    def add_stratigraphy(self, layer, filter_expression, column_mapping, title, style_file=None, config=None):
        """Add stratigraphy data

        Parameters
        ----------
        layer: QgsVectorLayer
          The layer where stratigraphic data are stored
        filter_expression: str
          A QGIS expression to filter the vector layer
        column_mapping: dict
          Dictionary of column names
        title: str
          Title of the graph
        style_file: str
          Name of the style file to use
        config: PlotConfig
        """
        symbology = config.get_symbology()[0] if config else None
        item = StratigraphyItem(self.DEFAULT_COLUMN_WIDTH,
                                self.__log_scene.height(),
                                style_file=style_file if not symbology else None,
                                symbology=symbology,
                                column_mapping=column_mapping
        )
        item.style_updated.connect(self.styles_updated)
        legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title)

        item.set_layer(layer)
        item.tooltipRequested.connect(self.on_plot_tooltip)

        req = QgsFeatureRequest()
        req.setFilterExpression(filter_expression)
        item.set_data(list(layer.getFeatures(req)))

        self._add_column(item, legend_item)

    def add_imagery(self, image_filename, title, depth_from, depth_to):
        item = ImageryDataItem(self.DEFAULT_COLUMN_WIDTH,
                               self.__log_scene.height(),
                               image_filename,
                               depth_from,
                               depth_to)
        
        legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title)

        self._add_column(item, legend_item)

    def select_column_at(self, pos):
        x = pos.x()
        c = 0
        selected = -1
        for i, width in enumerate(self.__column_widths):
            if x >= c and x < c + width:
                selected = i
                break
            c += width
        self.select_column(selected)

    def select_column(self, idx):
        self.__selected_column = idx
        for i, p in enumerate(self.__columns):
            item, legend = p
            item.set_selected(idx == i)
            legend.set_selected(idx == i)
            item.update()
            legend.update()

        self._update_button_visibility()

    def selected_column(self):
        return self.__selected_column

    def _update_button_visibility(self):
        idx = self.__selected_column
        self.__action_move_column_left.setEnabled(idx != -1 and idx > 0)
        self.__action_move_column_right.setEnabled(idx != -1 and idx < len(self.__columns) - 1)

        item = self.__columns[idx][0] if idx > 0 else None
        self.__action_edit_style.setEnabled(bool(item and not isinstance(item, ImageryDataItem)))
        self.__action_remove_column.setEnabled(idx != -1)

    def on_move_column_left(self):
        if self.__selected_column < 1:
            return

        sel = self.__selected_column
        self.__columns[sel-1], self.__columns[sel] = self.__columns[sel], self.__columns[sel-1]
        self.__column_widths[sel-1], self.__column_widths[sel] = self.__column_widths[sel], self.__column_widths[sel-1]
        self.__selected_column -= 1
        self._place_items()
        self._update_button_visibility()

    def on_move_column_right(self):
        if self.__selected_column == -1 or self.__selected_column >= len(self.__columns) - 1:
            return

        sel = self.__selected_column
        self.__columns[sel+1], self.__columns[sel] = self.__columns[sel], self.__columns[sel+1]
        self.__column_widths[sel+1], self.__column_widths[sel] = self.__column_widths[sel], self.__column_widths[sel+1]
        self.__selected_column += 1
        self._place_items()
        self._update_button_visibility()

    def on_remove_column(self):
        if self.__selected_column == -1:
            return

        sel = self.__selected_column

        # remove item from scenes
        item, legend = self.__columns[sel]
        self.__log_scene.removeItem(legend)
        self.__log_scene.removeItem(item)

        # remove from internal list
        del self.__columns[sel]
        del self.__column_widths[sel]
        self.__selected_column = -1
        self._place_items()
        self._update_button_visibility()

    def on_edit_style(self):
        if self.__selected_column == -1:
            return

        item = self.__columns[self.__selected_column][0]
        item.edit_style()

    def on_add_column(self):
        # to be overridden by subclasses
        pass

    def styles(self):
        """Return the current style of each item"""
        return dict([(item.layer().id(), item.qgis_style())
                     for item, legend in self.__columns
                     if hasattr(item, "qgis_style")])
class PlanetMainFilters(MAIN_FILTERS_BASE, MAIN_FILTERS_WIDGET,
                        PlanetFilterMixin):

    leAOI: QLineEdit

    filtersChanged = pyqtSignal()
    savedSearchSelected = pyqtSignal(object)
    zoomToAOIRequested = pyqtSignal()

    def __init__(self, iface, parent=None, plugin=None,
                no_saved_search=False, color=MAIN_AOI_COLOR):
        super().__init__(parent=parent)
        self._iface: QgisInterface = iface
        self._plugin = plugin

        self.setupUi(self)

        self.emitFiltersChanged = False

        self.color = color

        self._aoi_box = QgsRubberBand(self._iface.mapCanvas(),
                                      QgsWkbTypes.PolygonGeometry)
        self._aoi_box.setFillColor(QColor(0, 0, 0, 0))
        self._aoi_box.setStrokeColor(color)
        self._aoi_box.setWidth(3)
        self._aoi_box.setLineStyle(Qt.DashLine)

        self._canvas: QgsMapCanvas = self._iface.mapCanvas()
        # This may later be a nullptr, if no active tool when queried
        self._cur_maptool = None

        # noinspection PyUnresolvedReferences
        self.leAOI.textChanged['QString'].connect(self.filters_changed)
        # noinspection PyUnresolvedReferences
        self.leAOI.textEdited['QString'].connect(self.validate_edited_aoi)

        self._setup_tool_buttons()

        # Extent line edit tools
        self.btnZoomToAOI.clicked.connect(self.zoom_to_aoi)
        self.btnCopyAOI.clicked.connect(self.copy_aoi_to_clipboard)

        self.p_client = PlanetClient.getInstance()
        self.p_client.loginChanged.connect(self.populate_saved_searches)

        self.comboSavedSearch.currentIndexChanged.connect(self.saved_search_selected)

        if no_saved_search:
            self.comboSavedSearch.setVisible(False)

    def populate_saved_searches(self, is_logged):
        if is_logged:
            self.comboSavedSearch.clear()
            self.comboSavedSearch.blockSignals(True)
            self.comboSavedSearch.addItem("[Select a Saved Search]")
            res = self.p_client.get_searches().get()
            for search in res["searches"]:
                self.comboSavedSearch.addItem(search["name"], search)
            self.comboSavedSearch.blockSignals(False)

    def add_saved_search(self, request):
        self.comboSavedSearch.blockSignals(True)
        self.comboSavedSearch.addItem(request["name"], request)
        self.comboSavedSearch.setCurrentIndex(self.comboSavedSearch.count() - 1)
        self.comboSavedSearch.blockSignals(False)

    def saved_search_selected(self, idx):
        if idx == 0:
            return
        request = self.comboSavedSearch.currentData()
        analytics_track("saved_search_accessed")
        self.savedSearchSelected.emit(request)

    def null_out_saved_search(self):
        self.comboSavedSearch.blockSignals(True)
        self.comboSavedSearch.setCurrentIndex(0)
        self.comboSavedSearch.blockSignals(False)

    def reset_aoi_box(self):
        self.leAOI.setText("")
        if self._aoi_box:
            self._aoi_box.reset(QgsWkbTypes.PolygonGeometry)

    def filters(self):
        filters = []
        if self.leAOI.text():
            # TODO: Validate GeoJSON; try planet.api.utils.probably_geojson()
            # noinspection PyBroadException
            try:
                qgsgeom = qgsgeometry_from_geojson(self.leAOI.text())
                if not qgsgeom.isEmpty():
                    geom_json = json.loads(qgsgeom.asJson())
                    filters.append(geom_filter(geom_json))
                else:
                    self._show_message("AOI not valid GeoJSON polygon",
                                       level=Qgis.Warning,
                                       duration=10)
            except Exception:
                self._show_message("AOI not valid JSON",
                                   level=Qgis.Warning,
                                   duration=10)
            finally:
                return filters

    def filters_as_json(self):
        filters = []
        if self.leAOI.text():
            filters.append(self.leAOI.text())

        return filters

    def set_from_request(self, request):
        self.emitFiltersChanged = False
        filters = filters_from_request(request, "geometry")
        if filters:
            geom = filters[0]["config"]
            txt = json.dumps(geom)
            self.leAOI.setText(txt)
        else:
            self.leAOI.setText("")
        self.emitFiltersChanged = True

    @pyqtSlot('QString')
    def filters_changed(self, value):
        if self.emitFiltersChanged:# noinspection PyUnresolvedReferences
            self.filtersChanged.emit()

    @pyqtSlot()
    def clean_up(self):
        self.reset_aoi_box()

    def _setup_tool_buttons(self):
        extent_menu = QMenu(self)

        canvas_act = QAction('Current visible extent', extent_menu)
        # noinspection PyUnresolvedReferences
        canvas_act.triggered[bool].connect(self.aoi_from_current_extent)
        extent_menu.addAction(canvas_act)

        active_act = QAction('Active map layer extent', extent_menu)
        # noinspection PyUnresolvedReferences
        active_act.triggered[bool].connect(self.aoi_from_active_layer_extent)
        extent_menu.addAction(active_act)

        full_act = QAction('All map layers extent', extent_menu)
        # noinspection PyUnresolvedReferences
        full_act.triggered[bool].connect(self.aoi_from_full_extent)
        extent_menu.addAction(full_act)

        self.btnExtent.setMenu(extent_menu)

        # Also show menu on click, to keep disclosure triangle visible
        self.btnExtent.clicked.connect(self.btnExtent.showMenu)

        draw_menu = QMenu(self)

        box_act = QAction('Rectangle', draw_menu)
        # noinspection PyUnresolvedReferences
        box_act.triggered[bool].connect(self.aoi_from_box)
        draw_menu.addAction(box_act)

        circle_act = QAction('Circle', draw_menu)
        # noinspection PyUnresolvedReferences
        circle_act.triggered[bool].connect(self.aoi_from_circle)
        draw_menu.addAction(circle_act)

        polygon_act = QAction('Polygon', draw_menu)
        # noinspection PyUnresolvedReferences
        polygon_act.triggered[bool].connect(self.aoi_from_polygon)
        draw_menu.addAction(polygon_act)

        self.btnDraw.setMenu(draw_menu)
        # Also show menu on click, to keep disclosure triangle visible
        self.btnDraw.clicked.connect(self.btnDraw.showMenu)

        selection_menu = QMenu(self)

        self.single_select_act = QAction('Single feature', selection_menu)
        # noinspection PyUnresolvedReferences
        self.single_select_act.triggered[bool].connect(self.aoi_from_feature)
        selection_menu.addAction(self.single_select_act)

        self.bound_select_act = QAction('Multiple features (bounding box)',
                                        selection_menu)
        # noinspection PyUnresolvedReferences
        self.bound_select_act.triggered[bool].connect(self.aoi_from_bound)
        selection_menu.addAction(self.bound_select_act)

        self.btnSelection.setMenu(selection_menu)
        # Also show menu on click, to keep disclosure triangle visible
        self.btnSelection.clicked.connect(self._toggle_selection_tools)
        self.btnSelection.clicked.connect(self.btnSelection.showMenu)

        upload_menu = QMenu(self)

        upload_act = QAction('Upload vector layer file', upload_menu)
        upload_act.triggered[bool].connect(self.upload_file)
        upload_menu.addAction(upload_act)


        self.btnUpload.setMenu(upload_menu)
        self.btnUpload.clicked.connect(self.btnUpload.showMenu)

    def upload_file(self):
        filename, _ = QFileDialog.getOpenFileName(self, "Select AOI file", "",
                                                  "All files(*.*)")
        if filename:
            layer = QgsVectorLayer(filename, "")
            self.aoi_from_layer(layer)

    def aoi_from_layer(self, layer):
        if not layer.isValid():
            self._show_message("Invalid layer",
                               level=Qgis.Warning,
                               duration=10)
        else:
            feature = next(layer.getFeatures(), None)
            if feature is None:
                self._show_message("Layer contains no features",
                                   level=Qgis.Warning,
                                   duration=10)
            else:
                geom = feature.geometry()

                transform = QgsCoordinateTransform(
                    layer.crs(),
                    QgsCoordinateReferenceSystem("EPSG:4326"),
                    QgsProject.instance())

                try:
                    geom.transform(transform)
                except QgsCsException as e:
                    self._show_message("Could not convert AOI to EPSG:4326",
                           level=Qgis.Warning,
                           duration=10)
                    return

                geom_json = geom.asJson(precision=6)

                self._aoi_box.setToGeometry(geom)

                self.leAOI.setText(geom_json)

                log.debug('AOI set to layer')

                self.zoom_to_aoi()

    def _toggle_selection_tools(self):
        active_layer = self._iface.activeLayer()
        is_vector = isinstance(active_layer, QgsVectorLayer)
        if is_vector and active_layer.selectedFeatureCount():
            if active_layer.selectedFeatureCount() > 1:
                self.single_select_act.setEnabled(False)
                self.bound_select_act.setEnabled(True)
            elif active_layer.selectedFeatureCount():
                self.single_select_act.setEnabled(True)
                self.bound_select_act.setEnabled(False)
            else:
                self.single_select_act.setEnabled(False)
                self.bound_select_act.setEnabled(False)
        else:
            self.single_select_act.setEnabled(False)
            self.bound_select_act.setEnabled(False)

    @pyqtSlot()
    # noinspection PyArgumentList
    def aoi_from_current_extent(self):
        """Return current map extent as geojson transformed to EPSG:4326
        """
        if not self._iface:
            log.debug('No iface object, skipping AOI extent')
            return

        canvas = self._iface.mapCanvas()
        # noinspection PyArgumentList
        transform = QgsCoordinateTransform(
            QgsProject.instance().crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance()
        )

        canvas_extent: QgsRectangle = canvas.extent()
        try:
            transform_extent = transform.transformBoundingBox(canvas_extent)
        except QgsCsException as e:
            self._show_message("Could not convert AOI to EPSG:4326",
                   level=Qgis.Warning,
                   duration=10)
            return
        # noinspection PyArgumentList
        geom_extent = QgsGeometry.fromRect(transform_extent)
        extent_json = geom_extent.asJson(precision=6)

        # noinspection PyArgumentList
        self._aoi_box.setToGeometry(QgsGeometry.fromRect(canvas.extent()))

        self.leAOI.setText(extent_json)

        log.debug('AOI set to canvas extent')

        self.zoom_to_aoi()

    @pyqtSlot()
    # noinspection PyArgumentList
    def aoi_from_active_layer_extent(self):
        """Return active map layer extent as geojson transformed to EPSG:4326
        """
        if not self._iface:
            log.debug('No iface object, skipping AOI extent')
            return

        map_layer: QgsMapLayer = self._iface.activeLayer()
        if map_layer is None:
            log.debug('No active layer selected, skipping AOI extent')
            return

        if not map_layer.isValid():
            log.debug('Active map layer invalid, skipping AOI extent')
            return

        # noinspection PyArgumentList
        transform = QgsCoordinateTransform(
            map_layer.crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance())

        ml_extent: QgsRectangle = map_layer.extent()
        try:
            transform_extent = transform.transformBoundingBox(ml_extent)
        except QgsCsException as e:
            self._show_message("Could not convert AOI to EPSG:4326",
                   level=Qgis.Warning,
                   duration=10)
            return
        # noinspection PyArgumentList
        geom_extent = QgsGeometry.fromRect(transform_extent)
        extent_json = geom_extent.asJson(precision=6)

        # noinspection PyArgumentList,PyCallByClass
        #self._aoi_box.setToGeometry(QgsGeometry.fromRect(ml_extent))

        self.leAOI.setText(extent_json)

        log.debug('AOI set to active layer extent')

        self.zoom_to_aoi()

    @pyqtSlot()
    # noinspection PyArgumentList
    def aoi_from_full_extent(self):
        """Return full data map extent as geojson transformed to EPSG:4326
        """
        if not self._iface:
            log.debug('No iface object, skipping AOI extent')
            return

        canvas = self._iface.mapCanvas()

        # noinspection PyArgumentList
        transform = QgsCoordinateTransform(
            QgsProject.instance().crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance())

        canvas_extent: QgsRectangle = canvas.fullExtent()
        if canvas_extent.isNull(): # Canvas not yet initialized
            return
        try:
            transform_extent = transform.transformBoundingBox(canvas_extent)
        except QgsCsException as e:
            self._show_message("Could not convert AOI to EPSG:4326",
                   level=Qgis.Warning,
                   duration=10)
            return
        # noinspection PyArgumentList
        geom_extent = QgsGeometry.fromRect(transform_extent)
        extent_json = geom_extent.asJson(precision=6)

        # noinspection PyArgumentList,PyCallByClass
        self._aoi_box.setToGeometry(QgsGeometry.fromRect(canvas_extent))

        self.leAOI.setText(extent_json)

        log.debug('AOI set to full data extent')

        self.zoom_to_aoi()

    @pyqtSlot()
    def aoi_from_box(self):
        self._cur_maptool: QgsMapTool = self._canvas.mapTool()
        self._aoi_box.reset(QgsWkbTypes.PolygonGeometry)
        aoi_draw = PlanetExtentMapTool(self._iface.mapCanvas())
        self._iface.mapCanvas().setMapTool(aoi_draw)
        aoi_draw.extentSelected.connect(self.set_draw_aoi)

    @pyqtSlot()
    def aoi_from_circle(self):
        self._cur_maptool: QgsMapTool = self._canvas.mapTool()
        self._aoi_box.reset(QgsWkbTypes.PolygonGeometry)
        aoi_draw = PlanetCircleMapTool(self._iface.mapCanvas())
        self._iface.mapCanvas().setMapTool(aoi_draw)
        aoi_draw.circleSelected.connect(self.set_draw_aoi)

    @pyqtSlot()
    def aoi_from_polygon(self):
        self._cur_maptool: QgsMapTool = self._canvas.mapTool()
        self._aoi_box.reset(QgsWkbTypes.PolygonGeometry)
        aoi_draw = PlanetPolyMapTool(self._iface.mapCanvas())
        self._iface.mapCanvas().setMapTool(aoi_draw)
        aoi_draw.polygonSelected.connect(self.set_draw_aoi)

    @pyqtSlot(object)
    def set_draw_aoi(self, aoi):
        # noinspection PyArgumentList
        transform = QgsCoordinateTransform(
            QgsProject.instance().crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance())

        aoi_json = None

        if isinstance(aoi, QgsRectangle):
            aoi_geom = QgsGeometry().fromRect(aoi)
            self._aoi_box.setToGeometry(aoi_geom)
            aoi_geom.transform(transform)
            aoi_json = aoi_geom.asJson(precision=6)

        if isinstance(aoi, QgsGeometry):
            self._aoi_box.setToGeometry(aoi)
            # TODO: validate geom is less than 500 vertices
            aoi.transform(transform)
            aoi_json = aoi.asJson(precision=6)

        if aoi_json:
            self.leAOI.setText(aoi_json)

            # noinspection PyUnresolvedReferences
            self._show_message('AOI set to drawn figure')
            self.zoom_to_aoi()
            if self._cur_maptool is not None:
                # Restore previously used maptool
                self._canvas.setMapTool(self._cur_maptool)
                self._cur_maptool = None
            else:
                # Fallback to activating pan tool
                self._iface.actionPan().trigger()
        else:
            # noinspection PyUnresolvedReferences
            self._show_message('AOI unable to be set',
                               level=Qgis.Warning,
                               duration=10)

    @pyqtSlot()
    def aoi_from_feature(self):
        layer = self._iface.activeLayer()
        if not isinstance(layer, QgsVectorLayer):
            self._show_message('Active layer must be a vector layer.',
                               level=Qgis.Warning,
                               duration=10)
            return

        if layer.selectedFeatureCount() > 1:
            self._show_message('More than 1 feature. Searching by bbox.',
                               level=Qgis.Warning,
                               duration=10)
            self.aoi_from_bound()
            return
        elif layer.selectedFeatureCount() < 1:
            self._show_message('No features selected.',
                               level=Qgis.Warning,
                               duration=10)
            return

        selected: QgsFeature = layer.selectedFeatures()[0]
        geom: QgsGeometry = selected.geometry()

        if geom.constGet().vertexCount() > 500:
            self._show_message(
                               'More than 500 vertices. Searching by bbox.',
                               level=Qgis.Warning,
                               duration=10
            )
            self.aoi_from_bound()
            return

        # noinspection PyArgumentList
        trans_layer = QgsCoordinateTransform(
            layer.sourceCrs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance()
        )

        # noinspection PyArgumentList
        trans_canvas = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance().crs(),
            QgsProject.instance()
        )

        # geom.transform(transform)
        geom.transform(trans_layer)
        geom_json = geom.asJson(precision=6)
        self.leAOI.setText(geom_json)

        geom.transform(trans_canvas)
        self._aoi_box.setToGeometry(
            geom,
            QgsCoordinateReferenceSystem("EPSG:4326")
        )
        self.zoom_to_aoi()

    @pyqtSlot()
    def aoi_from_bound(self):
        layer = self._iface.activeLayer()
        if not isinstance(layer, QgsVectorLayer):
            self._show_message('Active layer must be a vector layer.',
                               level=Qgis.Warning,
                               duration=10)
            return

        if layer.selectedFeatureCount() < 1:
            self._show_message('No features selected.',
                               level=Qgis.Warning,
                               duration=10)
            return

        bbox = layer.boundingBoxOfSelected()

        # noinspection PyArgumentList
        trans_layer = QgsCoordinateTransform(
            layer.sourceCrs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance()
        )

        # noinspection PyArgumentList
        trans_canvas = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance().crs(),
            QgsProject.instance()
        )

        transform_bbox = trans_layer.transformBoundingBox(bbox)
        # noinspection PyArgumentList
        geom_bbox = QgsGeometry.fromRect(transform_bbox)
        bbox_json = geom_bbox.asJson(precision=6)

        self.leAOI.setText(bbox_json)

        bbox_canvas = trans_canvas.transformBoundingBox(transform_bbox)
        # noinspection PyArgumentList
        self._aoi_box.setToGeometry(QgsGeometry.fromRect(bbox_canvas))

        self.zoom_to_aoi()

    def hide_aoi_if_matches_geom(self, geom):
            color = (QColor(0, 0, 0, 0) if self._aoi_box.asGeometry().equals(geom)
                    else self.color)
            self._aoi_box.setStrokeColor(color)

    def show_aoi(self):
        if self._aoi_box is not None:
            self._aoi_box.setStrokeColor(self.color)

    def aoi_geom(self):
        if self._aoi_box is not None:
            return self._aoi_box.asGeometry()

    def aoi_as_4326_geom(self):
        transform = QgsCoordinateTransform(
            QgsProject.instance().crs(),
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance()
        )
        geom = self.aoi_geom()
        if geom is not None:
            geom.transform(transform)
        return geom


    @pyqtSlot()
    def zoom_to_aoi(self):
        if not self._iface:
            log.debug('No iface object, skipping AOI extent')
            return

        if not self.leAOI.text():
            log.debug('No AOI defined, skipping zoom to AOI')
            return

        geom: QgsGeometry = qgsgeometry_from_geojson(self.leAOI.text())
        if geom.isEmpty():
            self._show_message('AOI GeoJSON geometry invalid',
                   level=Qgis.Warning,
                   duration=10)
            return

        self._aoi_box.setToGeometry(
            geom,
            QgsCoordinateReferenceSystem("EPSG:4326")
        )

        self.show_aoi()

        zoom_canvas_to_aoi(self.leAOI.text())

        self.zoomToAOIRequested.emit()

    @pyqtSlot()
    def copy_aoi_to_clipboard(self):
        if not self.leAOI.text():
            log.debug('No AOI defined, skipping zoom to AOI')
            return

        try:
            json_obj = json.loads(self.leAOI.text())
        except ValueError:
            return

        json_geom_txt = json.dumps(json_obj, indent=2)

        cb = QgsApplication.clipboard()
        cb.setText(json_geom_txt)

        # noinspection PyUnresolvedReferences
        self._show_message('AOI copied to clipboard')

    @pyqtSlot()
    def validate_aoi(self):
        # TODO:gather existing validation logic here
        # TODO: Check for valid json.loads

        # TODO: Check API verticie limit of 500
        pass

    @pyqtSlot()
    def validate_edited_aoi(self):
        json_txt = self.leAOI.text()
        if not json_txt:
            self.reset_aoi_box()
            log.debug('No AOI defined, skipping validation')
            return

        try:
            json_obj = json.loads(json_txt)
        except ValueError:
            # noinspection PyUnresolvedReferences
            self._show_message('AOI GeoJSON is invalid',
                               level=Qgis.Warning,
                               duration=10)
            return

        try:
            json_geom = geometry_from_json(json_obj)
        except:
            json_geom = None

        if not json_geom:
            # noinspection PyUnresolvedReferences
            self._show_message('AOI GeoJSON geometry invalid',
                               level=Qgis.Warning,
                               duration=10)
            return

        geom: QgsGeometry = qgsgeometry_from_geojson(json_geom)
        self._aoi_box.setToGeometry(
            geom,
            QgsCoordinateReferenceSystem("EPSG:4326")
        )

        self.leAOI.blockSignals(True)
        self.leAOI.setText(json.dumps(json_geom))
        self.leAOI.blockSignals(False)

        self.zoom_to_aoi()
class LatLonTools:
    digitizerDialog = None
    convertCoordinateDialog = None

    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.crossRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry)
        self.crossRb.setColor(Qt.red)
        self.provider = LatLonToolsProvider()
        self.toolbar = self.iface.addToolBar('Lat Lon Tools Toolbar')
        self.toolbar.setObjectName('LatLonToolsToolbar')

    def initGui(self):
        '''Initialize Lot Lon Tools GUI.'''
        # Initialize the Settings Dialog box
        self.settingsDialog = SettingsWidget(self, self.iface, self.iface.mainWindow())
        self.mapTool = CopyLatLonTool(self.settingsDialog, self.iface)
        self.showMapTool = ShowOnMapTool(self.iface)

        # Add Interface for Coordinate Capturing
        icon = QIcon(os.path.dirname(__file__) + "/images/copyicon.png")
        self.copyAction = QAction(icon, "Copy/Display Coordinate", self.iface.mainWindow())
        self.copyAction.setObjectName('latLonToolsCopy')
        self.copyAction.triggered.connect(self.startCapture)
        self.copyAction.setCheckable(True)
        self.toolbar.addAction(self.copyAction)
        self.iface.addPluginToMenu("Lat Lon Tools", self.copyAction)

        # Add Interface for External Map
        icon = QIcon(os.path.dirname(__file__) + "/images/mapicon.png")
        self.externMapAction = QAction(icon, "Show in External Map", self.iface.mainWindow())
        self.externMapAction.setObjectName('latLonToolsExternalMap')
        self.externMapAction.triggered.connect(self.setShowMapTool)
        self.externMapAction.setCheckable(True)
        self.toolbar.addAction(self.externMapAction)
        self.iface.addPluginToMenu("Lat Lon Tools", self.externMapAction)

        # Add Interface for Zoom to Coordinate
        icon = QIcon(os.path.dirname(__file__) + "/images/zoomicon.png")
        self.zoomToAction = QAction(icon, "Zoom To Coordinate", self.iface.mainWindow())
        self.zoomToAction.setObjectName('latLonToolsZoom')
        self.zoomToAction.triggered.connect(self.showZoomToDialog)
        self.toolbar.addAction(self.zoomToAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.zoomToAction)

        self.zoomToDialog = ZoomToLatLon(self, self.iface, self.iface.mainWindow())
        self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.zoomToDialog)
        self.zoomToDialog.hide()

        # Add Interface for Multi point zoom
        icon = QIcon(os.path.dirname(__file__) + '/images/multizoom.png')
        self.multiZoomToAction = QAction(icon, "Multi-location Zoom", self.iface.mainWindow())
        self.multiZoomToAction.setObjectName('latLonToolsMultiZoom')
        self.multiZoomToAction.triggered.connect(self.multiZoomTo)
        self.toolbar.addAction(self.multiZoomToAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.multiZoomToAction)

        self.multiZoomDialog = MultiZoomWidget(self, self.settingsDialog, self.iface.mainWindow())
        self.multiZoomDialog.hide()
        self.multiZoomDialog.setFloating(True)

        # Create the coordinate converter menu
        icon = QIcon(':/images/themes/default/mIconProjectionEnabled.svg')
        self.convertCoordinatesAction = QAction(icon, "Coordinate Conversion", self.iface.mainWindow())
        self.convertCoordinatesAction.setObjectName('latLonToolsCoordinateConversion')
        self.convertCoordinatesAction.triggered.connect(self.convertCoordinatesTool)
        self.toolbar.addAction(self.convertCoordinatesAction)
        self.iface.addPluginToMenu("Lat Lon Tools", self.convertCoordinatesAction)

        # Create the conversions menu
        menu = QMenu()
        icon = QIcon(os.path.dirname(__file__) + '/images/field2geom.png')
        action = menu.addAction(icon, "Fields to point layer", self.field2geom)
        action.setObjectName('latLonToolsField2Geom')
        icon = QIcon(os.path.dirname(__file__) + '/images/geom2field.png')
        action = menu.addAction(icon, "Point layer to fields", self.geom2Field)
        action.setObjectName('latLonToolsGeom2Field')
        icon = QIcon(os.path.dirname(__file__) + '/images/pluscodes.png')
        action = menu.addAction(icon, "Plus Codes to point layer", self.PlusCodestoLayer)
        action.setObjectName('latLonToolsPlusCodes2Geom')
        action = menu.addAction(icon, "Point layer to Plus Codes", self.toPlusCodes)
        action.setObjectName('latLonToolsGeom2PlusCodes')
        icon = QIcon(os.path.dirname(__file__) + '/images/mgrs2point.png')
        action = menu.addAction(icon, "MGRS to point layer", self.MGRStoLayer)
        action.setObjectName('latLonToolsMGRS2Geom')
        icon = QIcon(os.path.dirname(__file__) + '/images/point2mgrs.png')
        action = menu.addAction(icon, "Point layer to MGRS", self.toMGRS)
        action.setObjectName('latLonToolsGeom2MGRS')
        self.conversionsAction = QAction(icon, "Conversions", self.iface.mainWindow())
        self.conversionsAction.setMenu(menu)
        self.iface.addPluginToMenu('Lat Lon Tools', self.conversionsAction)

        # Add to Digitize Toolbar
        icon = QIcon(os.path.dirname(__file__) + '/images/latLonDigitize.png')
        self.digitizeAction = QAction(icon, "Lat Lon Digitize", self.iface.mainWindow())
        self.digitizeAction.setObjectName('latLonToolsDigitize')
        self.digitizeAction.triggered.connect(self.digitizeClicked)
        self.digitizeAction.setEnabled(False)
        self.toolbar.addAction(self.digitizeAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.digitizeAction)

        # Add Interface for copying the canvas extent
        icon = QIcon(os.path.dirname(__file__) + "/images/copycanvas.png")
        self.copyCanvasAction = QAction(icon, "Copy Canvas Bounding Box", self.iface.mainWindow())
        self.copyCanvasAction.setObjectName('latLonToolsCopyCanvas')
        self.copyCanvasAction.triggered.connect(self.copyCanvas)
        self.toolbar.addAction(self.copyCanvasAction)
        self.iface.addPluginToMenu("Lat Lon Tools", self.copyCanvasAction)

        # Initialize the Settings Dialog Box
        settingsicon = QIcon(os.path.dirname(__file__) + '/images/settings.png')
        self.settingsAction = QAction(settingsicon, "Settings", self.iface.mainWindow())
        self.settingsAction.setObjectName('latLonToolsSettings')
        self.settingsAction.triggered.connect(self.settings)
        self.iface.addPluginToMenu('Lat Lon Tools', self.settingsAction)

        # Help
        icon = QIcon(os.path.dirname(__file__) + '/images/help.png')
        self.helpAction = QAction(icon, "Help", self.iface.mainWindow())
        self.helpAction.setObjectName('latLonToolsHelp')
        self.helpAction.triggered.connect(self.help)
        self.iface.addPluginToMenu('Lat Lon Tools', self.helpAction)

        self.iface.currentLayerChanged.connect(self.currentLayerChanged)
        self.canvas.mapToolSet.connect(self.unsetTool)
        self.enableDigitizeTool()

        # Add the processing provider
        QgsApplication.processingRegistry().addProvider(self.provider)

    def unsetTool(self, tool):
        '''Uncheck the Copy Lat Lon tool'''
        try:
            if not isinstance(tool, CopyLatLonTool):
                self.copyAction.setChecked(False)
                self.multiZoomDialog.stopCapture()
                self.mapTool.capture4326 = False
            if not isinstance(tool, ShowOnMapTool):
                self.externMapAction.setChecked(False)
        except Exception:
            pass

    def unload(self):
        '''Unload LatLonTools from the QGIS interface'''
        self.zoomToDialog.removeMarker()
        self.multiZoomDialog.removeMarkers()
        self.canvas.unsetMapTool(self.mapTool)
        self.canvas.unsetMapTool(self.showMapTool)
        self.iface.removePluginMenu('Lat Lon Tools', self.copyAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.copyCanvasAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.externMapAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.zoomToAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.multiZoomToAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.convertCoordinatesAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.conversionsAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.settingsAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.helpAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.digitizeAction)
        self.iface.removeDockWidget(self.zoomToDialog)
        self.iface.removeDockWidget(self.multiZoomDialog)
        # Remove Toolbar Icons
        self.iface.removeToolBarIcon(self.copyAction)
        self.iface.removeToolBarIcon(self.copyCanvasAction)
        self.iface.removeToolBarIcon(self.zoomToAction)
        self.iface.removeToolBarIcon(self.externMapAction)
        self.iface.removeToolBarIcon(self.multiZoomToAction)
        self.iface.removeToolBarIcon(self.convertCoordinatesAction)
        self.iface.removeToolBarIcon(self.digitizeAction)
        del self.toolbar

        self.zoomToDialog = None
        self.multiZoomDialog = None
        self.settingsDialog = None
        self.showMapTool = None
        self.mapTool = None
        self.digitizerDialog = None
        self.convertCoordinateDialog = None
        QgsApplication.processingRegistry().removeProvider(self.provider)

    def startCapture(self):
        '''Set the focus of the copy coordinate tool and check it'''
        self.copyAction.setChecked(True)
        self.canvas.setMapTool(self.mapTool)

    def copyCanvas(self):
        extent = self.iface.mapCanvas().extent()
        canvasCrs = self.canvas.mapSettings().destinationCrs()
        if settings.bBoxCrs == 0 and canvasCrs != epsg4326:
            transform = QgsCoordinateTransform(canvasCrs, epsg4326, QgsProject.instance())
            p1x, p1y = transform.transform(float(extent.xMinimum()), float(extent.yMinimum()))
            p2x, p2y = transform.transform(float(extent.xMaximum()), float(extent.yMaximum()))
            extent.set(p1x, p1y, p2x, p2y)
        delim = settings.bBoxDelimiter
        prefix = settings.bBoxPrefix
        suffix = settings.bBoxSuffix
        precision = settings.bBoxDigits
        outStr = ''
        minX = extent.xMinimum()
        minY = extent.yMinimum()
        maxX = extent.xMaximum()
        maxY = extent.yMaximum()
        if settings.bBoxFormat == 0:  # minX,minY,maxX,maxY - using the delimiter
            outStr = '{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}'.format(
                minX, delim, minY, delim, maxX, delim, maxY, prec=precision)
        elif settings.bBoxFormat == 1:  # minX,maxX,minY,maxY - Using the selected delimiter'
            outStr = '{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}'.format(
                minX, delim, maxX, delim, minY, delim, maxY, prec=precision)
        elif settings.bBoxFormat == 2:  # x1 y1,x2 y2,x3 y3,x4 y4,x1 y1 - Polygon format
            outStr = '{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f}'.format(
                minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY, prec=precision)
        elif settings.bBoxFormat == 3:  # x1,y1 x2,y2 x3,y3 x4,y4 x1,y1 - Polygon format
            outStr = '{:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f}'.format(
                minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY, prec=precision)
        elif settings.bBoxFormat == 4:  # WKT Polygon
            outStr = extent.asWktPolygon()
        elif settings.bBoxFormat == 5:  # bbox: [minX, minY, maxX, maxY] - MapProxy
            outStr = 'bbox: [{}, {}, {}, {}]'.format(
                minX, minY, maxX, maxY)
        elif settings.bBoxFormat == 6:  # bbox: [minX, minY, maxX, maxY] - MapProxy
            outStr = 'bbox={},{},{},{}'.format(
                minX, minY, maxX, maxY)
        outStr = '{}{}{}'.format(prefix, outStr, suffix)
        clipboard = QApplication.clipboard()
        clipboard.setText(outStr)
        self.iface.messageBar().pushMessage("", "'{}' copied to the clipboard".format(outStr), level=Qgis.Info, duration=4)

    def setShowMapTool(self):
        '''Set the focus of the external map tool and check it'''
        self.externMapAction.setChecked(True)
        self.canvas.setMapTool(self.showMapTool)

    def showZoomToDialog(self):
        '''Show the zoom to docked widget.'''
        self.zoomToDialog.show()

    def convertCoordinatesTool(self):
        '''Display the Convert Coordinate Tool Dialog box.'''
        if self.convertCoordinateDialog is None:
            from .coordinateConverter import CoordinateConverterWidget
            self.convertCoordinateDialog = CoordinateConverterWidget(self, self.settingsDialog, self.iface, self.iface.mainWindow())
        self.convertCoordinateDialog.show()

    def multiZoomTo(self):
        '''Display the Multi-zoom to dialog box'''
        self.multiZoomDialog.show()

    def field2geom(self):
        '''Convert layer containing a point x & y coordinate to a new point layer'''
        processing.execAlgorithmDialog('latlontools:field2geom', {})

    def geom2Field(self):
        '''Convert layer geometry to a text string'''
        processing.execAlgorithmDialog('latlontools:geom2field', {})

    def toMGRS(self):
        '''Display the to MGRS  dialog box'''
        processing.execAlgorithmDialog('latlontools:point2mgrs', {})

    def MGRStoLayer(self):
        '''Display the to MGRS  dialog box'''
        processing.execAlgorithmDialog('latlontools:mgrs2point', {})

    def toPlusCodes(self):
        processing.execAlgorithmDialog('latlontools:point2pluscodes', {})

    def PlusCodestoLayer(self):
        processing.execAlgorithmDialog('latlontools:pluscodes2point', {})

    def settings(self):
        '''Show the settings dialog box'''
        self.settingsDialog.show()

    def help(self):
        '''Display a help page'''
        url = QUrl.fromLocalFile(os.path.dirname(__file__) + "/index.html").toString()
        webbrowser.open(url, new=2)

    def settingsChanged(self):
        # Settings may have changed so we need to make sure the zoomToDialog window is configured properly
        self.zoomToDialog.configure()
        self.multiZoomDialog.settingsChanged()

    def zoomTo(self, srcCrs, lat, lon):
        canvasCrs = self.canvas.mapSettings().destinationCrs()
        transform = QgsCoordinateTransform(srcCrs, canvasCrs, QgsProject.instance())
        x, y = transform.transform(float(lon), float(lat))

        rect = QgsRectangle(x, y, x, y)
        self.canvas.setExtent(rect)

        pt = QgsPointXY(x, y)
        self.highlight(pt)
        self.canvas.refresh()
        return pt

    def highlight(self, point):
        currExt = self.canvas.extent()

        leftPt = QgsPoint(currExt.xMinimum(), point.y())
        rightPt = QgsPoint(currExt.xMaximum(), point.y())

        topPt = QgsPoint(point.x(), currExt.yMaximum())
        bottomPt = QgsPoint(point.x(), currExt.yMinimum())

        horizLine = QgsGeometry.fromPolyline([leftPt, rightPt])
        vertLine = QgsGeometry.fromPolyline([topPt, bottomPt])

        self.crossRb.reset(QgsWkbTypes.LineGeometry)
        self.crossRb.addGeometry(horizLine, None)
        self.crossRb.addGeometry(vertLine, None)

        QTimer.singleShot(700, self.resetRubberbands)

    def resetRubberbands(self):
        self.crossRb.reset()

    def digitizeClicked(self):
        if self.digitizerDialog is None:
            from .digitizer import DigitizerWidget
            self.digitizerDialog = DigitizerWidget(self, self.iface, self.iface.mainWindow())
        self.digitizerDialog.show()

    def currentLayerChanged(self):
        layer = self.iface.activeLayer()
        if layer is not None:
            try:
                layer.editingStarted.disconnect(self.layerEditingChanged)
            except Exception:
                pass
            try:
                layer.editingStopped.disconnect(self.layerEditingChanged)
            except Exception:
                pass

            if isinstance(layer, QgsVectorLayer):
                layer.editingStarted.connect(self.layerEditingChanged)
                layer.editingStopped.connect(self.layerEditingChanged)

        self.enableDigitizeTool()

    def layerEditingChanged(self):
        self.enableDigitizeTool()

    def enableDigitizeTool(self):
        self.digitizeAction.setEnabled(False)
        layer = self.iface.activeLayer()

        if layer is not None and isinstance(layer, QgsVectorLayer) and (layer.geometryType() == QgsWkbTypes.PointGeometry) and layer.isEditable():
            self.digitizeAction.setEnabled(True)
        else:
            if self.digitizerDialog is not None:
                self.digitizerDialog.close()
class LatLonTools:
    digitizerDialog = None
    
    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.crossRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry)
        self.crossRb.setColor(Qt.red)
        self.provider = LatLonToolsProvider()
        self.toolbar = self.iface.addToolBar('Lat Lon Tools Toolbar')
        self.toolbar.setObjectName('LatLonToolsToolbar')

    def initGui(self):
        '''Initialize Lot Lon Tools GUI.'''
        # Initialize the Settings Dialog box
        self.settingsDialog = SettingsWidget(self, self.iface, self.iface.mainWindow())
        self.mapTool = CopyLatLonTool(self.settingsDialog, self.iface)
        self.showMapTool = ShowOnMapTool(self.iface)
        
        # Add Interface for Coordinate Capturing
        icon = QIcon(os.path.dirname(__file__) + "/images/copyicon.png")
        self.copyAction = QAction(icon, "Copy Latitude, Longitude", self.iface.mainWindow())
        self.copyAction.setObjectName('latLonToolsCopy')
        self.copyAction.triggered.connect(self.startCapture)
        self.copyAction.setCheckable(True)
        self.toolbar.addAction(self.copyAction)
        self.iface.addPluginToMenu("Lat Lon Tools", self.copyAction)
        
        # Add Interface for External Map
        icon = QIcon(os.path.dirname(__file__) + "/images/mapicon.png")
        self.externMapAction = QAction(icon, "Show in External Map", self.iface.mainWindow())
        self.externMapAction.setObjectName('latLonToolsExternalMap')
        self.externMapAction.triggered.connect(self.setShowMapTool)
        self.externMapAction.setCheckable(True)
        self.toolbar.addAction(self.externMapAction)
        self.iface.addPluginToMenu("Lat Lon Tools", self.externMapAction)

        # Add Interface for Zoom to Coordinate
        icon = QIcon(os.path.dirname(__file__) + "/images/zoomicon.png")
        self.zoomToAction = QAction(icon, "Zoom To Latitude, Longitude", self.iface.mainWindow())
        self.zoomToAction.setObjectName('latLonToolsZoom')
        self.zoomToAction.triggered.connect(self.showZoomToDialog)
        self.toolbar.addAction(self.zoomToAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.zoomToAction)

        self.zoomToDialog = ZoomToLatLon(self, self.iface, self.iface.mainWindow())
        self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.zoomToDialog)
        self.zoomToDialog.hide()
        
        # Add Interface for Multi point zoom
        icon = QIcon(os.path.dirname(__file__) + '/images/multizoom.png')
        self.multiZoomToAction = QAction(icon, "Multi-location Zoom", self.iface.mainWindow())
        self.multiZoomToAction.setObjectName('latLonToolsMultiZoom')
        self.multiZoomToAction.triggered.connect(self.multiZoomTo)
        self.toolbar.addAction(self.multiZoomToAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.multiZoomToAction)

        self.multiZoomDialog = MultiZoomWidget(self, self.settingsDialog, self.iface.mainWindow())
        self.multiZoomDialog.hide()
        self.multiZoomDialog.setFloating(True)
        
        # Create the conversions menu
        menu = QMenu()
        icon = QIcon(os.path.dirname(__file__) + '/images/field2geom.png')
        action = menu.addAction(icon, "Fields to point layer", self.field2geom)
        action.setObjectName('latLonToolsField2Geom')
        icon = QIcon(os.path.dirname(__file__) + '/images/geom2field.png')
        action = menu.addAction(icon, "Point layer to fields", self.geom2Field)
        action.setObjectName('latLonToolsGeom2Field')
        icon = QIcon(os.path.dirname(__file__) + '/images/pluscodes.png')
        action = menu.addAction(icon, "Plus Codes to point layer", self.PlusCodestoLayer)
        action.setObjectName('latLonToolsPlusCodes2Geom')
        action = menu.addAction(icon, "Point layer to Plus Codes", self.toPlusCodes)
        action.setObjectName('latLonToolsGeom2PlusCodes')
        icon = QIcon(os.path.dirname(__file__) + '/images/mgrs2point.png')
        action = menu.addAction(icon, "MGRS to point layer", self.MGRStoLayer)
        action.setObjectName('latLonToolsMGRS2Geom')
        icon = QIcon(os.path.dirname(__file__) + '/images/point2mgrs.png')
        action = menu.addAction(icon, "Point layer to MGRS", self.toMGRS)
        action.setObjectName('latLonToolsGeom2MGRS')
        self.conversionsAction = QAction(icon, "Conversions", self.iface.mainWindow())
        self.conversionsAction.setMenu(menu)
        self.iface.addPluginToMenu('Lat Lon Tools', self.conversionsAction)
        
        # Add to Digitize Toolbar
        icon = QIcon(os.path.dirname(__file__) + '/images/latLonDigitize.png')
        self.digitizeAction = QAction(icon, "Lat Lon Digitize", self.iface.mainWindow())
        self.digitizeAction.setObjectName('latLonToolsDigitize')
        self.digitizeAction.triggered.connect(self.digitizeClicked)
        self.digitizeAction.setEnabled(False)
        self.toolbar.addAction(self.digitizeAction)
        self.iface.addPluginToMenu('Lat Lon Tools', self.digitizeAction)
        
        # Add Interface for copying the canvas extent
        icon = QIcon(os.path.dirname(__file__) + "/images/copycanvas.png")
        self.copyCanvasAction = QAction(icon, "Copy Canvas Bounding Box", self.iface.mainWindow())
        self.copyCanvasAction.setObjectName('latLonToolsCopyCanvas')
        self.copyCanvasAction.triggered.connect(self.copyCanvas)
        self.toolbar.addAction(self.copyCanvasAction)
        self.iface.addPluginToMenu("Lat Lon Tools", self.copyCanvasAction)
        
        # Initialize the Settings Dialog Box
        settingsicon = QIcon(os.path.dirname(__file__) + '/images/settings.png')
        self.settingsAction = QAction(settingsicon, "Settings", self.iface.mainWindow())
        self.settingsAction.setObjectName('latLonToolsSettings')
        self.settingsAction.triggered.connect(self.settings)
        self.iface.addPluginToMenu('Lat Lon Tools', self.settingsAction)
        
        # Help
        icon = QIcon(os.path.dirname(__file__) + '/images/help.png')
        self.helpAction = QAction(icon, "Help", self.iface.mainWindow())
        self.helpAction.setObjectName('latLonToolsHelp')
        self.helpAction.triggered.connect(self.help)
        self.iface.addPluginToMenu('Lat Lon Tools', self.helpAction)
        
        
        self.iface.currentLayerChanged.connect(self.currentLayerChanged)
        self.canvas.mapToolSet.connect(self.unsetTool)
        self.enableDigitizeTool()
        # Add the processing provider
        
        QgsApplication.processingRegistry().addProvider(self.provider)
                
    def unsetTool(self, tool):
        '''Uncheck the Copy Lat Lon tool'''
        try:
            if not isinstance(tool, CopyLatLonTool):
                self.copyAction.setChecked(False)
                self.multiZoomDialog.stopCapture()
                self.mapTool.capture4326 = False
            if not isinstance(tool, ShowOnMapTool):
                self.externMapAction.setChecked(False)
        except:
            pass

    def unload(self):
        '''Unload LatLonTools from the QGIS interface'''
        self.zoomToDialog.removeMarker()
        self.multiZoomDialog.removeMarkers()
        self.canvas.unsetMapTool(self.mapTool)
        self.canvas.unsetMapTool(self.showMapTool)
        self.iface.removePluginMenu('Lat Lon Tools', self.copyAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.copyCanvasAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.externMapAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.zoomToAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.multiZoomToAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.conversionsAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.settingsAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.helpAction)
        self.iface.removePluginMenu('Lat Lon Tools', self.digitizeAction)
        self.iface.removeDockWidget(self.zoomToDialog)
        self.iface.removeDockWidget(self.multiZoomDialog)
        # Remove Toolbar Icons
        self.iface.removeToolBarIcon(self.copyAction)
        self.iface.removeToolBarIcon(self.copyCanvasAction)
        self.iface.removeToolBarIcon(self.zoomToAction)
        self.iface.removeToolBarIcon(self.externMapAction)
        self.iface.removeToolBarIcon(self.multiZoomToAction)
        self.iface.removeToolBarIcon(self.digitizeAction)
        del self.toolbar
        
        self.zoomToDialog = None
        self.multiZoomDialog = None
        self.settingsDialog = None
        self.showMapTool = None
        self.mapTool = None
        self.digitizerDialog = None
        QgsApplication.processingRegistry().removeProvider(self.provider)

    def startCapture(self):
        '''Set the focus of the copy coordinate tool and check it'''
        self.copyAction.setChecked(True)
        self.canvas.setMapTool(self.mapTool)

    def copyCanvas(self):
        extent = self.iface.mapCanvas().extent()
        canvasCrs = self.canvas.mapSettings().destinationCrs()
        if settings.bBoxCrs == 0 and canvasCrs != epsg4326:
            transform = QgsCoordinateTransform(canvasCrs, epsg4326, QgsProject.instance())
            p1x, p1y = transform.transform(float(extent.xMinimum()), float(extent.yMinimum()))
            p2x, p2y = transform.transform(float(extent.xMaximum()), float(extent.yMaximum()))
            extent.set(p1x, p1y, p2x, p2y)
        delim = settings.bBoxDelimiter
        prefix = settings.bBoxPrefix
        suffix = settings.bBoxSuffix
        precision = settings.bBoxDigits
        outStr = ''
        minX = extent.xMinimum()
        minY = extent.yMinimum()
        maxX = extent.xMaximum()
        maxY = extent.yMaximum()
        if settings.bBoxFormat == 0: # minX,minY,maxX,maxY - using the delimiter
            outStr = '{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}'.format(
                minX, delim, minY, delim, maxX, delim, maxY, prec=precision)
        elif settings.bBoxFormat == 1: # minX,maxX,minY,maxY - Using the selected delimiter'
            outStr = '{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}'.format(
                minX, delim, maxX, delim, minY, delim, maxY, prec=precision)
        elif settings.bBoxFormat == 2: # x1 y1,x2 y2,x3 y3,x4 y4,x1 y1 - Polygon format
            outStr = '{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f}'.format(
                minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY, prec=precision)
        elif settings.bBoxFormat == 3: # x1,y1 x2,y2 x3,y3 x4,y4 x1,y1 - Polygon format
            outStr = '{:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f}'.format(
                minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY, prec=precision)
        elif settings.bBoxFormat == 4: # WKT Polygon
            outStr = extent.asWktPolygon()
        elif settings.bBoxFormat == 5: # bbox: [minX, minY, maxX, maxY] - MapProxy
            outStr = 'bbox: [{}, {}, {}, {}]'.format(
                minX, minY, maxX, maxY)
        elif settings.bBoxFormat == 6: # bbox: [minX, minY, maxX, maxY] - MapProxy
            outStr = 'bbox={},{},{},{}'.format(
                minX, minY, maxX, maxY)
        outStr = '{}{}{}'.format(prefix, outStr, suffix)
        clipboard = QApplication.clipboard()
        clipboard.setText(outStr)
        self.iface.messageBar().pushMessage("", "'{}' copied to the clipboard".format(outStr), level=Qgis.Info, duration=4)
        
    def setShowMapTool(self):
        '''Set the focus of the external map tool and check it'''
        self.externMapAction.setChecked(True)
        self.canvas.setMapTool(self.showMapTool)

    def showZoomToDialog(self):
        '''Show the zoom to docked widget.'''
        self.zoomToDialog.show()

    def multiZoomTo(self):
        '''Display the Multi-zoom to dialog box'''
        self.multiZoomDialog.show()

    def field2geom(self):
        '''Convert layer containing a point x & y coordinate to a new point layer'''
        results = processing.execAlgorithmDialog('latlontools:field2geom', {})

    def geom2Field(self):
        '''Convert layer geometry to a text string'''
        results = processing.execAlgorithmDialog('latlontools:geom2field', {})

    def toMGRS(self):
        '''Display the to MGRS  dialog box'''
        results = processing.execAlgorithmDialog('latlontools:point2mgrs', {})

    def MGRStoLayer(self):
        '''Display the to MGRS  dialog box'''
        results = processing.execAlgorithmDialog('latlontools:mgrs2point', {})

    def toPlusCodes(self):
        results = processing.execAlgorithmDialog('latlontools:point2pluscodes', {})

    def PlusCodestoLayer(self):
        results = processing.execAlgorithmDialog('latlontools:pluscodes2point', {})
    
    def settings(self):
        '''Show the settings dialog box'''
        self.settingsDialog.show()
        
    def help(self):
        '''Display a help page'''
        url = QUrl.fromLocalFile(os.path.dirname(__file__) + "/index.html").toString()
        webbrowser.open(url, new=2)
        
    def settingsChanged(self):
        # Settings may have changed so we need to make sure the zoomToDialog window is configured properly
        self.zoomToDialog.configure()
        self.multiZoomDialog.settingsChanged()
            
 
    def zoomTo(self, srcCrs, lat, lon):
        canvasCrs = self.canvas.mapSettings().destinationCrs()
        transform = QgsCoordinateTransform(srcCrs, canvasCrs, QgsProject.instance())
        x, y = transform.transform(float(lon), float(lat))
            
        rect = QgsRectangle(x, y, x, y)
        self.canvas.setExtent(rect)

        pt = QgsPointXY(x, y)
        self.highlight(pt)
        self.canvas.refresh()
        return pt
        
    def highlight(self, point):
        currExt = self.canvas.extent()
        
        leftPt = QgsPoint(currExt.xMinimum(), point.y())
        rightPt = QgsPoint(currExt.xMaximum(), point.y())
        
        topPt = QgsPoint(point.x(), currExt.yMaximum())
        bottomPt = QgsPoint(point.x(), currExt.yMinimum())
        
        horizLine = QgsGeometry.fromPolyline( [ leftPt , rightPt ] )
        vertLine = QgsGeometry.fromPolyline( [ topPt , bottomPt ] )
        
        self.crossRb.reset(QgsWkbTypes.LineGeometry)
        self.crossRb.addGeometry(horizLine, None)
        self.crossRb.addGeometry(vertLine, None)
        
        QTimer.singleShot(700, self.resetRubberbands)
        
    def resetRubberbands(self):
        self.crossRb.reset()
        
    def digitizeClicked(self):
        if self.digitizerDialog == None:
            from .digitizer import DigitizerWidget
            self.digitizerDialog = DigitizerWidget(self, self.iface, self.iface.mainWindow())
        self.digitizerDialog.show()
        
    def currentLayerChanged(self):
        layer = self.iface.activeLayer()
        if layer != None:
            try:
                layer.editingStarted.disconnect(self.layerEditingChanged)
            except:
                pass
            try:
                layer.editingStopped.disconnect(self.layerEditingChanged)
            except:
                pass
            
            if isinstance(layer, QgsVectorLayer):
                layer.editingStarted.connect(self.layerEditingChanged)
                layer.editingStopped.connect(self.layerEditingChanged)
                
        self.enableDigitizeTool()

    def layerEditingChanged(self):
        self.enableDigitizeTool()

    def enableDigitizeTool(self):
        self.digitizeAction.setEnabled(False)
        layer = self.iface.activeLayer()
        
        if layer != None and isinstance(layer, QgsVectorLayer) and (layer.geometryType() == QgsWkbTypes.PointGeometry) and layer.isEditable():
            self.digitizeAction.setEnabled(True)
        else:
            if self.digitizerDialog != None:
                self.digitizerDialog.close()