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())
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())
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
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()))
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
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)
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)
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)
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')
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))
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)
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
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> </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()
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_()
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
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
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
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
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
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)
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())
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()))
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()
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)
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()
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
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()
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()