class FloatingToolBar(QToolBar): """ A floating QToolBar with no border and is offset under its parent """ def __init__(self, name, parent): """ parent: The parent of this toolbar. Should be another toolbar """ QToolBar.__init__(self, name, parent) self.setMovable(False) self.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint | Qt.X11BypassWindowManagerHint) self.setAllowedAreas(Qt.NoToolBarArea) self.actiongroup = QActionGroup(self) def addToActionGroup(self, action): self.actiongroup.addAction(action) def showToolbar(self, parentaction, defaultaction, toggled): if toggled: self.show() if defaultaction: defaultaction.toggle() widget = self.parent().widgetForAction(parentaction) x = self.parent().mapToGlobal(widget.pos()).x() y = self.parent().mapToGlobal(widget.pos()).y() newpoint = QPoint(x, y + self.parent().rect().height()) # if self.orientation() == Qt.Vertical: # newpoint = QPoint(x, y + self.parent().rect().width()) self.move(newpoint) else: action = self.actiongroup.checkedAction() if action: action.toggle() self.hide()
def setActionsExclusive(self): # Build an action list from QGIS navigation toolbar actionList = self.iface.mapNavToolToolBar().actions() # Add actions from QGIS attributes toolbar (handling QWidgetActions) tmpActionList = self.iface.attributesToolBar().actions() for action in tmpActionList: if isinstance(action, QWidgetAction): actionList.extend(action.defaultWidget().actions()) else: actionList.append(action) # ... add other toolbars' action lists... # Build a group with actions from actionList and add your own action group = QActionGroup(self.iface.mainWindow()) group.setExclusive(True) for action in actionList: group.addAction(action) group.addAction(self.identifyParcelleAction)
def setActionsExclusive(self): # Build an action list from QGIS navigation toolbar actionList = self.iface.mapNavToolToolBar().actions() # Add actions from QGIS attributes toolbar (handling QWidgetActions) tmpActionList = self.iface.attributesToolBar().actions() for action in tmpActionList: if isinstance(action, QWidgetAction): actionList.extend( action.defaultWidget().actions() ) else: actionList.append( action ) # ... add other toolbars' action lists... # Build a group with actions from actionList and add your own action group = QActionGroup( self.iface.mainWindow() ) group.setExclusive(True) for action in actionList: group.addAction( action ) group.addAction( self.identifyParcelleAction )
def _getActions(self): """Get actions that are displayed in the context menu""" actions = [] scaleAction = QAction("Scale...", self) scaleAction.triggered.connect(self._scale) scaleAction.setEnabled(True) actions.append(scaleAction) exportAction = QAction("Export as...", self) exportAction.triggered.connect(self._exportToFile) exportAction.setEnabled(True) actions.append(exportAction) sep = QAction("", self) sep.setSeparator(True) actions.append(sep) group = QActionGroup(self) group.addAction(self._nsAction) actions.append(self._nsAction) group.addAction(self._snAction) actions.append(self._snAction) group.addAction(self._weAction) actions.append(self._weAction) group.addAction(self._ewAction) actions.append(self._ewAction) sepAbout = QAction("", self) sepAbout.setSeparator(True) actions.append(sepAbout) manualAction = QAction("Manual...", self) manualAction.triggered.connect(self._openManual) manualAction.setEnabled(True) actions.append(manualAction) aboutAction = QAction("About...", self) aboutAction.triggered.connect(self._aboutPlugin) aboutAction.setEnabled(True) actions.append(aboutAction) return actions
class ProfilesPlugin(object): def __init__(self, iface): self.iface = iface QSettings().setValue('/UI/Customization/enabled', False) try: from profiles.tests import testerplugin from qgistester.tests import addTestModule addTestModule(testerplugin, 'Profiles plugin') except: pass self.userProfileAction = None self.profilesMenu = None iface.initializationCompleted.connect(self.initProfile) def unload(self): if self.profilesMenu is not None: self.profilesMenu.deleteLater() self.iface.removePluginMenu(self.tr('Profiles'), self.autoloadAction) self.iface.removePluginMenu(self.tr('Profiles'), self.saveProfileAction) try: from profiles.tests import testerplugin from qgistester.tests import removeTestModule removeTestModule(testerplugin, 'Profiles plugin') except: pass saveCurrentPluginState() def initGui(self): self.addMenus() def addMenus(self): if self.profilesMenu is not None: self.profilesMenu.clear() self.actions = defaultdict(list) settings = QSettings() defaultProfile = settings.value('profilesplugin/LastProfile', 'Default', str) autoLoad = settings.value('profilesplugin/AutoLoad', False, bool) for k, v in profiles.items(): action = QAction(k, self.iface.mainWindow()) action.setCheckable(True) if k == defaultProfile and autoLoad: action.setChecked(True) action.triggered.connect(lambda _, menuName=k: self.applyProfile(menuName)) action.setObjectName('mProfilesPlugin_' + k) self.actions[v.group].append(action) actions = self.iface.mainWindow().menuBar().actions() settingsMenu = None self.profilesGroup = QActionGroup(self.iface.mainWindow()) if self.profilesMenu is None: for action in actions: if action.menu().objectName() == 'mSettingsMenu': settingsMenu = action.menu() self.profilesMenu = QMenu(settingsMenu) self.profilesMenu.setObjectName('mProfilesPlugin') self.profilesMenu.setTitle(self.tr('Profiles')) settingsMenu.addMenu(self.profilesMenu) break if self.profilesMenu is not None: for k,v in self.actions.items(): submenu = QMenu(self.profilesMenu) submenu.setObjectName('mProfilesPlugin_submenu_' + k) submenu.setTitle(k) for action in v: self.profilesGroup.addAction(action) submenu.addAction(action) self.profilesMenu.addMenu(submenu) self.profilesMenu.addSeparator() settings = QSettings() def _setAutoLoad(): settings.setValue('profilesplugin/AutoLoad', self.autoloadAction.isChecked()) self.autoloadAction = QAction(self.tr('Auto-load last profile on QGIS start'), iface.mainWindow()) self.autoloadAction.setCheckable(True) autoLoad = settings.value('profilesplugin/AutoLoad', False, bool) self.autoloadAction.setChecked(autoLoad) self.autoloadAction.setObjectName('mProfilesPluginAutoLoad') self.autoloadAction.triggered.connect(_setAutoLoad) self.profilesMenu.addAction(self.autoloadAction) self.saveProfileAction = QAction(self.tr('Profiles manager...'), self.iface.mainWindow()) self.saveProfileAction.setObjectName('mProfilesPluginProfilesManager') self.saveProfileAction.triggered.connect(self.saveProfile) self.profilesMenu.addAction(self.saveProfileAction) self.showHelpAction = QAction(self.tr('Help'), self.iface.mainWindow()) self.showHelpAction.setIcon(QgsApplication.getThemeIcon('/mActionHelpContents.svg')) self.showHelpAction.setObjectName('mProfilesPluginShowHelp') self.showHelpAction.triggered.connect(self.showHelp) self.profilesMenu.addAction(self.showHelpAction) def applyProfile(self, name): profile = profiles.get(name) applyProfile(profile) def initProfile(self): settings = QSettings() autoLoad = settings.value('profilesplugin/AutoLoad', False, bool) if autoLoad: profileName = settings.value('profilesplugin/LastProfile', '', str) if profileName in profiles: profile = profiles[profileName] if not profile.hasToInstallPlugins(): applyProfile(profile, False) def saveProfile(self): dlg = ProfileManager(iface.mainWindow()) dlg.exec_() def showHelp(self): if not QDesktopServices.openUrl( QUrl('file://{}'.format(os.path.join(pluginPath, 'docs', 'html', 'index.html')))): QMessageBox.warning(None, self.tr('Error'), self.tr('Can not open help URL in browser')) def tr(self, text): return QCoreApplication.translate('Profiles', text)
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.snapping = True icon = roam_style.iconsize() self.projecttoolbar.setIconSize(QSize(icon, icon)) self.defaultextent = None self.current_form = None self.last_form = None self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.bridge = QgsLayerTreeMapCanvasBridge( QgsProject.instance().layerTreeRoot(), self.canvas) self.bridge.setAutoSetupOnFirstLayer(False) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.snappingutils = SnappingUtils(self.canvas, self) self.canvas.setSnappingUtils(self.snappingutils) threadcount = QThread.idealThreadCount() threadcount = 2 if threadcount > 2 else 1 QgsApplication.setMaxThreads(threadcount) self.canvas.setParallelRenderingEnabled(True) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) if roam.config.settings.get('north_arrow', False): self.northarrow = NorthArrow(":/icons/north", self.canvas) self.northarrow.setPos(10, 10) self.canvas.scene().addItem(self.northarrow) smallmode = roam.config.settings.get("smallmode", False) self.projecttoolbar.setSmallMode(smallmode) self.projecttoolbar.setContextMenuPolicy(Qt.CustomContextMenu) gpsspacewidget = QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget( self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction( self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.gpsMarker = GPSMarker(self.canvas) self.gpsMarker.hide() self.currentfeatureband = CurrentSelection(self.canvas) self.currentfeatureband.setIconSize(30) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(88, 64, 173, 50)) self.currentfeatureband.setOutlineColour(QColor(88, 64, 173)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(165, 111, 212, 75)) self.gpsband.setWidth(5) RoamEvents.refresh_map.connect(self.refresh_map) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.openfeatureform.connect(self.feature_form_loaded) RoamEvents.sync_complete.connect(self.refresh_map) RoamEvents.snappingChanged.connect(self.snapping_changed) self.snappingbutton = QToolButton() self.snappingbutton.setText("Snapping: On") self.snappingbutton.setAutoRaise(True) self.snappingbutton.pressed.connect(self.toggle_snapping) spacer = QWidget() spacer2 = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.scalewidget = QgsScaleComboBox() self.scalebutton = QToolButton() self.scalebutton.setAutoRaise(True) self.scalebutton.setMaximumHeight(self.statusbar.height()) self.scalebutton.pressed.connect(self.selectscale) self.scalebutton.setText("Scale") self.scalelist = BigList(parent=self.canvas, centeronparent=True, showsave=False) self.scalelist.hide() self.scalelist.setlabel("Map Scale") self.scalelist.setmodel(self.scalewidget.model()) self.scalelist.closewidget.connect(self.scalelist.close) self.scalelist.itemselected.connect(self.update_scale_from_item) self.scalelist.itemselected.connect(self.scalelist.close) self.positionlabel = QLabel('') self.gpslabel = QLabel("GPS: Not active") self.gpslabelposition = QLabel("") self.statusbar.addWidget(self.snappingbutton) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.statusbar.addWidget(self.gpslabelposition) self.statusbar.addPermanentWidget(self.scalebutton) self.canvas.extentsChanged.connect(self.update_status_label) self.canvas.scaleChanged.connect(self.update_status_label) self.connectButtons() scalebar_enabled = roam.config.settings.get('scale_bar', False) self.scalebar_enabled = False if scalebar_enabled: roam.utils.warning( "Unsupported feature: Scale bar support not ported to QGIS 3 API yet." ) RoamEvents.raisemessage( "Unsupported feature", "Scale bar support not ported to QGIS 3 API yet", level=RoamEvents.CRITICAL) self.scalebar_enabled = False # self.scalebar = ScaleBarItem(self.canvas) # self.canvas.scene().addItem(self.scalebar) def clear_plugins(self) -> None: """ Clear all the plugin added toolbars from the map interface. """ toolbars = self.findChildren(QToolBar) for toolbar in toolbars: if toolbar.property("plugin_toolbar"): toolbar.unload() self.removeToolBar(toolbar) toolbar.deleteLater() def add_plugins(self, pluginnames) -> None: """ Add the given plugins to to the mapping interface. Adds the toolbars the plugin exposes as new toolbars for the user. :param pluginnames: The names of the plugins to load. Must already be loaded by the plugin loader """ for name in pluginnames: # Get the plugin try: plugin_mod = plugins.loaded_plugins[name] except KeyError: continue if not hasattr(plugin_mod, 'toolbars'): roam.utils.warning( "No toolbars() function found in {}".format(name)) continue toolbars = plugin_mod.toolbars() self.load_plugin_toolbars(toolbars) def load_plugin_toolbars(self, toolbars): """ Load the plugin toolbars into the mapping interface. :param toolbars: The list of toolbars class objects to load. :return: """ for ToolBarClass in toolbars: toolbar = ToolBarClass(plugins.api, self) self.addToolBar(Qt.BottomToolBarArea, toolbar) toolbar.setProperty("plugin_toolbar", True) def snapping_changed(self, snapping): """ Called when the snapping settings have changed. Updates the label in the status bar. :param snapping: """ self.snapping = snapping if snapping: self.snappingbutton.setText("Snapping: On") else: self.snappingbutton.setText("Snapping: Off") def toggle_snapping(self): """ Toggle snapping on or off. """ self.snapping = not self.snapping try: self.canvas.mapTool().toggle_snapping() except AttributeError: pass RoamEvents.snappingChanged.emit(self.snapping) def selectscale(self): """ Show the select scale widget. :return: """ self.scalelist.show() def update_scale_from_item(self, index): """ Update the canvas scale from the selected scale item. :param index: The index of the selected item. """ scale, _ = self.scalewidget.toDouble(index.data(Qt.DisplayRole)) self.canvas.zoomScale(1.0 / scale) def update_gps_fixed_label(self, fixed, gpsinfo): if not fixed: self.gpslabel.setText("GPS: Acquiring fix") self.gpslabelposition.setText("") quality_mappings = { 0: "invalid", 1: "GPS", 2: "DGPS", 3: "PPS", 4: "Real Time Kinematic", 5: "Float RTK", 6: "Estimated", 7: "Manual input mode", 8: "Simulation mode" } def update_gps_label(self, position, gpsinfo): """ Update the GPS label in the status bar with the GPS status. :param position: The current GPS position. :param gpsinfo: The current extra GPS information. """ if not self.gps.connected: return fixtype = self.quality_mappings.get(gpsinfo.quality, "") self.gpslabel.setText( "DOP P:<b>{0:.2f}</b> H:<b>{1:.2f}</b> V:<b>{2:.2f}</b> " "Fix: <b>{3}</b> " "Sats: <b>{4}</b> ".format(gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop, fixtype, gpsinfo.satellitesUsed)) places = roam.config.settings.get("gpsplaces", 8) self.gpslabelposition.setText("X: <b>{x:.{places}f}</b> " "Y: <b>{y:.{places}f}</b> " "Z: <b>{z}m</b> ".format( x=position.x(), y=position.y(), z=gpsinfo.elevation, places=places)) def gps_disconnected(self): self.gpslabel.setText("GPS: Not Active") self.gpslabelposition.setText("") self.gpsMarker.hide() def zoom_to_feature(self, feature): """ Zoom to the given feature in the map. :param feature: :return: """ box = feature.geometry().boundingBox() xmin, xmax, ymin, ymax = box.xMinimum(), box.xMaximum(), box.yMinimum( ), box.yMaximum() xmin -= 5 xmax += 5 ymin -= 5 ymax += 5 box = QgsRectangle(xmin, ymin, xmax, ymax) self.canvas.setExtent(box) self.canvas.refresh() def update_status_label(self, *args) -> None: """ Update the status bar labels when the information has changed. """ extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format( extent.center().toString())) scale = 1.0 / self.canvas.scale() scale = self.scalewidget.toString(scale) self.scalebutton.setText(scale) def refresh_map(self) -> None: """ Refresh the map """ self.canvas.refresh() def updatescale(self) -> None: """ Update the scale of the map with the current scale from the scale widget :return: """ self.canvas.zoomScale(1.0 / self.scalewidget.scale()) @property def crs(self) -> QgsCoordinateReferenceSystem: """ Get the CRS used that is being used in the canvas :return: The QgsCoordinateReferenceSystem that is used by the canvas """ return self.canvas.mapSettings().destinationCrs() def feature_form_loaded(self, form, feature, *args): """ Called when the feature form is loaded. :param form: The Form object. Holds a reference to the forms layer. :param feature: The current capture feature """ self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): """ Highlight the selection on the canvas. This updates all selected objects based on the result set. :param results: A dict-of-list of layer-features. """ self.clear_selection() for layer, features in results.items(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0)) band.setIconSize(25) band.setWidth(5) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) band.setZValue(self.currentfeatureband.zValue() - 1) for feature in features: band.addGeometry(feature.geometry(), layer) self.canvas.update() def highlight_active_selection(self, layer, feature, features): """ Update the current active selected feature. :param layer: The layer of the active feature. :param feature: The active feature. :param features: The other features in the set to show as non active selection. :return: """ self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) self.canvas.update() def clear_selection(self): """ Clear the selection from the canvas. Resets all selection rubber bands. :return: """ # Clear the main selection rubber band self.canvas.scene().update() self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.values(): band.reset() self.canvas.update() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): """ Push a feature on the edit stack so the feature can have the geometry edited. :note: This is a big hack and I don't like it! :param form: The form for the current feature :param feature: The active feature. """ def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() self.canvas.currentLayer().startEditing() self.canvas.mapTool().setEditMode(True, feature.geometry(), feature) break self.editfeaturestack.append((form, feature)) self.save_current_form() self.load_form(form) trigger_default_action() def save_current_form(self): self.last_form = self.current_form def restore_last_form(self): self.load_form(self.last_form) def clear_temp_objects(self): """ Clear all temp objects from the canvas. :return: """ def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() if hasattr(tool, "clearBand"): tool.clearBand() self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): """ Called when the settings have been updated in the Roam config. :param settings: A dict of the settings. """ self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging smallmode = settings.get("smallmode", False) self.projecttoolbar.setSmallMode(smallmode) def set_gps(self, gps, logging): """ Set the GPS for the map widget. Connects GPS signals """ self.gps = gps self.gpslogging = logging self.gps.gpsfixed.connect(self.update_gps_fixed_label) self.gps.gpsposition.connect(self.update_gps_label) self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) self.gpsMarker.setgps(self.gps) self.actionGPS.setgps(gps) def gps_update_canvas(self, position, gpsinfo): """ Updates the map canvas based on the GPS position. By default if the GPS is outside the canvas extent the canvas will move to center on the GPS. Can be turned off in settings. :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastgpsposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.gpsMarker.show() self.gpsMarker.setCenter(position, gpsinfo) def gps_first_fix(self, postion, gpsinfo): """ Called the first time the GPS gets a fix. If set this will zoom to the GPS after the first fix :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): """ Zoom to ta given position on the map.. """ rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def select_data_entry(self): """ Open the form selection widget to allow the user to pick the active capture form. """ def showformerror(form): pass def actions(): for form in self.project.forms: if not self.form_valid_for_capture(form): continue action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format( form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form", wrap=5) formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): """ Called when the project is loaded. Main entry point for a loade project. :param project: The Roam project that has been loaded. """ self.snappingutils.setConfig(QgsProject.instance().snappingConfig()) self.project = project self.actionPan.trigger() firstform = self.first_capture_form() if firstform: self.load_form(firstform) self.dataentryselection.setVisible(True) else: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = roam.api.utils.layers() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.refresh() projectscales, _ = QgsProject.instance().readBoolEntry( "Scales", "/useProjectScales") if projectscales: projectscales, _ = QgsProject.instance().readListEntry( "Scales", "/ScalesList") self.scalewidget.updateScales(projectscales) else: scales = [ "1:50000", "1:25000", "1:10000", "1:5000", "1:2500", "1:1000", "1:500", "1:250", "1:200", "1:100" ] scales = roam.config.settings.get('scales', scales) self.scalewidget.updateScales(scales) if self.scalebar_enabled: self.scalebar.update() red = QgsProject.instance().readNumEntry("Gui", "/CanvasColorRedPart", 255)[0] green = QgsProject.instance().readNumEntry("Gui", "/CanvasColorGreenPart", 255)[0] blue = QgsProject.instance().readNumEntry("Gui", "/CanvasColorBluePart", 255)[0] myColor = QColor(red, green, blue) self.canvas.setCanvasColor(myColor) self.actionPan.toggle() self.clear_plugins() self.add_plugins(project.enabled_plugins) def setMapTool(self, tool, *args): """ Set the active map tool in the canvas. :param tool: The QgsMapTool to set. """ if tool == self.canvas.mapTool(): return if hasattr(tool, "setSnapping"): tool.setSnapping(self.snapping) self.canvas.setMapTool(tool) def connectButtons(self): """ Connect the default buttons in the interface. Zoom, pan, etc """ def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24, 24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = QgsMapToolPan(self.canvas) self.infoTool = InfoTool(self.canvas) self.infoTool.setAction(self.actionInfo) self.zoomInTool.setAction(self.actionZoom_In) self.zoomOutTool.setAction(self.actionZoom_Out) self.panTool.setAction(self.actionPan) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/select')) self.actionRaster.triggered.connect(self.toggle_raster_layers) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ if self.defaultextent: self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def form_valid_for_capture(self, form): """ Check if the given form is valid for capture. :param form: The form to check. :return: True if valid form for capture """ return form.has_geometry and self.project.layer_can_capture( form.QGISLayer) def first_capture_form(self): """ Return the first valid form for capture. """ for form in self.project.forms: if self.form_valid_for_capture(form): return form def load_form(self, form): """ Load the given form so it's the active one for capture :param form: The form to load """ self.clear_capture_tools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) self.current_form = form def create_capture_buttons(self, form): """ Create the capture buttons in the toolbar for the given form. :param form: The active form. """ tool = form.getMaptool()(self.canvas, form.settings) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(self.show_invalid_geometry_message) def show_invalid_geometry_message(self, message) -> None: """ Shows the message to the user if the there is a invalid geometry capture. :param message: The message to show the user. """ RoamEvents.raisemessage("Invalid geometry capture", message, level=RoamEvents.CRITICAL) if self.canvas.currentLayer() is not None: self.canvas.currentLayer().rollBack() RoamEvents.editgeometry_invalid.emit() def add_new_feature(self, form, geometry: QgsGeometry): """ Add a new new feature to the given layer :param form: The form to use for the new feature. :param geometry: The new geometry to create the feature for. """ # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if geometry.isMultipart(): geometry.convertToMultiType() # Transform the new geometry back into the map layers geometry if it's needed transform = self.canvas.mapSettings().layerTransform(layer) if transform.isValid(): geometry.transform(transform, QgsCoordinateTransform.ReverseTransform) try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass feature = form.new_feature(geometry=geometry) RoamEvents.load_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) self.canvas.mapTool().setEditMode(False, None, None) self.restore_last_form() def clear_capture_tools(self): """ Clear the capture tools from the toolbar. :return: True if the capture button was active at the time of clearing. """ captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggle_raster_layers(self) -> None: """ Toggle all raster layers on or off. """ # Freeze the canvas to save on UI refresh dlg = PickActionDialog(msg="Raster visibility") actions = [ (":/icons/raster_0", "Off", partial(self._set_basemaps_opacity, 0), "photo_off"), (":/icons/raster_25", "25%", partial(self._set_basemaps_opacity, .25), "photo_25"), (":/icons/raster_50", "50%", partial(self._set_basemaps_opacity, .50), "photo_50"), (":/icons/raster_75", "75%", partial(self._set_basemaps_opacity, .75), "photo_75"), (":/icons/raster_100", "100%", partial(self._set_basemaps_opacity, 1), "photo_100"), ] # ":/icons/raster_100"), "100%", self, triggered=partial(self._set_raster_layer_value, 1), # objectName="photo_100") dialog_actions = [] for action in actions: icon = QIcon(action[0]) qaction = QAction(icon, action[1], self, triggered=action[2], objectName=action[3]) dialog_actions.append(qaction) dlg.addactions(dialog_actions) dlg.exec_() def _set_basemaps_opacity(self, value=0) -> None: """ Set the opacity for all basemap raster layers. :param value: The opacity value betwen 0 and 1 """ tree = QgsProject.instance().layerTreeRoot() for node in tree.findLayers(): layer = node.layer() if node.layer().type() == QgsMapLayer.RasterLayer: if value > 0: node.setItemVisibilityChecked(Qt.Checked) renderer = layer.renderer() renderer.setOpacity(value) if value == 0: node.setItemVisibilityChecked(Qt.Unchecked) self.canvas.refresh() def cleanup(self): """ Clean up when the project has changed. :return: """ # TODO Review cleanup # self.bridge.clear() self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clear_capture_tools() for action in self.layerbuttons: self.editgroup.removeAction(action)
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()
def __createToolbarsAndConnect(self): actionGroup = QActionGroup(self) actionGroup.addAction(self.actionImport) actionGroup.addAction(self.actionVyhledavani) actionGroup.addAction(self.actionZpracujZmeny) # QSignalMapper self.signalMapper = QtCore.QSignalMapper(self) self.actionImport.triggered.connect(self.signalMapper.map) self.actionVyhledavani.triggered.connect(self.signalMapper.map) self.actionZpracujZmeny.triggered.connect(self.signalMapper.map) # setMapping on each button to the QStackedWidget index we'd like to # switch to self.signalMapper.setMapping(self.actionImport, 0) self.signalMapper.setMapping(self.actionVyhledavani, 2) self.signalMapper.setMapping(self.actionZpracujZmeny, 1) # connect mapper to stackedWidget self.signalMapper.mapped.connect(self.stackedWidget.setCurrentIndex) self.vfkBrowser.switchToPanelImport.connect(self.switchToImport) self.vfkBrowser.switchToPanelSearch.connect(self.switchToSearch) self.vfkBrowser.switchToPanelChanges.connect(self.switchToChanges) # Browser toolbar # --------------- self.__mBrowserToolbar = QToolBar(self) self.actionBack.triggered.connect(self.vfkBrowser.goBack) self.actionForward.triggered.connect(self.vfkBrowser.goForth) self.actionSelectBudInMap.triggered.connect(self.selectBudInMap) self.actionSelectParInMap.triggered.connect(self.selectParInMap) self.actionCuzkPage.triggered.connect(self.showOnCuzk) self.actionExportLatex.triggered.connect(self.latexExport) self.actionExportHtml.triggered.connect(self.htmlExport) self.actionShowInfoaboutSelection.toggled.connect( self.setSelectionChangedConnected) self.actionShowHelpPage.triggered.connect(self.vfkBrowser.showHelpPage) self.loadVfkButton.clicked.connect(self.loadVfkButton_clicked) self.__browseButtons['browseButton_1'] = self.browseButton self.__browseButtons['browseButton_1'].clicked.connect( lambda: self.browseButton_clicked( int('{}'.format(len(self.__vfkLineEdits))))) self.__vfkLineEdits['vfkLineEdit_1'] = self.vfkFileLineEdit bt = QToolButton(self.__mBrowserToolbar) bt.setPopupMode(QToolButton.InstantPopup) bt.setText("Export ") menu = QMenu(bt) menu.addAction(self.actionExportLatex) menu.addAction(self.actionExportHtml) bt.setMenu(menu) # add actions to toolbar icons self.__mBrowserToolbar.addAction(self.actionImport) self.__mBrowserToolbar.addAction(self.actionVyhledavani) self.__mBrowserToolbar.addAction(self.actionZpracujZmeny) self.__mBrowserToolbar.addSeparator() self.__mBrowserToolbar.addAction(self.actionBack) self.__mBrowserToolbar.addAction(self.actionForward) self.__mBrowserToolbar.addAction(self.actionSelectParInMap) self.__mBrowserToolbar.addAction(self.actionSelectBudInMap) self.__mBrowserToolbar.addAction(self.actionCuzkPage) self.__mBrowserToolbar.addSeparator() self.__mBrowserToolbar.addAction(self.actionShowInfoaboutSelection) self.__mBrowserToolbar.addSeparator() self.__mBrowserToolbar.addWidget(bt) self.__mBrowserToolbar.addSeparator() self.__mBrowserToolbar.addAction(self.actionShowHelpPage) self.rightWidgetLayout.insertWidget(0, self.__mBrowserToolbar) # connect signals from vfkbrowser when changing history self.vfkBrowser.currentParIdsChanged.connect( self.actionSelectParInMap.setEnabled) self.vfkBrowser.currentBudIdsChanged.connect( self.actionSelectBudInMap.setEnabled) self.vfkBrowser.historyBefore.connect(self.actionBack.setEnabled) self.vfkBrowser.historyAfter.connect(self.actionForward.setEnabled) self.vfkBrowser.definitionPointAvailable.connect( self.actionCuzkPage.setEnabled) # add toolTips self.pb_nextFile.setToolTip(u'Přidej další soubor VFK') self.parCheckBox.setToolTip(u'Načti vrstvu parcel') self.budCheckBox.setToolTip(u'Načti vrstvu budov') # add new VFK file self.pb_nextFile.clicked.connect(self.__addRowToGridLayout) # widget apply changes self.pb_mainDb.clicked.connect(lambda: self.browseDb_clicked('mainDb')) self.pb_amendmentDb.clicked.connect( lambda: self.browseDb_clicked('amendmentDb')) self.pb_exportDb.clicked.connect( lambda: self.browseDb_clicked('exportDb')) self.pb_applyChanges.clicked.connect(self.applyChanges) self.pb_applyChanges.setEnabled(False) self.changes_instance.maxRangeProgressBar.connect( self.__setRangeProgressBarChanges) self.changes_instance.updateStatus.connect( self.__updateProgressBarChanges) self.changes_instance.finishedStatus.connect(self.__changesApplied) self.changes_instance.preprocessingDatabase.connect( self.__changesPreprocessingDatabase) # connect radio boxes self.rb_file.clicked.connect(self.radioButtonValue) self.rb_directory.clicked.connect(self.radioButtonValue)
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow): """ Main application window """ def __init__(self, roamapp): super(MainWindow, self).__init__() self.setupUi(self) self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) import roam self.projectwidget.project_base = roamapp.projectsroot QgsApplication.instance().setStyleSheet(roam.roam_style.appstyle()) self.menutoolbar.setStyleSheet(roam.roam_style.menubarstyle()) icon = roam.roam_style.iconsize() self.menutoolbar.setIconSize(QSize(icon, icon)) smallmode = roam.config.settings.get("smallmode", False) self.menutoolbar.setSmallMode(smallmode) self.projectupdater = ProjectUpdater( projects_base=roamapp.projectsroot) self.projectupdater.foundProjects.connect( self.projectwidget.show_new_updateable) self.projectupdater.projectUpdateStatus.connect( self.projectwidget.update_project_status) self.projectupdater.projectInstalled.connect( self.projectwidget.project_installed) self.projectwidget.search_for_updates.connect(self.search_for_projects) self.projectwidget.projectUpdate.connect( self.projectupdater.update_project) self.projectwidget.projectInstall.connect( self.projectupdater.install_project) self.project = None self.tracking = GPSLogging(GPS) self.canvas_page.set_gps(GPS, self.tracking) self.canvas = self.canvas_page.canvas # self.canvas_page.projecttoolbar.stateChanged.connect(self.menutoolbar.setSmallMode) # self.menutoolbar.stateChanged.connect(self.canvas_page.projecttoolbar.setSmallMode) roam.defaults.canvas = self.canvas self.bar = roam.messagebaritems.MessageBar(self.centralwidget) self.actionMap.setVisible(False) self.actionLegend.setVisible(False) self.menuGroup = QActionGroup(self) self.menuGroup.setExclusive(True) self.menuGroup.addAction(self.actionMap) self.menuGroup.addAction(self.actionDataEntry) self.menuGroup.addAction(self.actionLegend) self.menuGroup.addAction(self.actionProject) self.menuGroup.addAction(self.actionSync) self.menuGroup.addAction(self.actionSettings) self.menuGroup.addAction(self.actionGPS) self.menuGroup.triggered.connect(self.updatePage) self.projectbuttons = [] self.pluginactions = [] self.actionQuit.triggered.connect(self.exit) self.init_legend() self.projectwidget.requestOpenProject.connect(self.load_roam_project) QgsProject.instance().readProject.connect(self.project_opened) self.gpswidget.setgps(GPS) self.gpswidget.settracking(self.tracking) self.settings = {} self.actionSettings.toggled.connect( self.settingswidget.populateControls) self.actionSettings.toggled.connect(self.settingswidget.readSettings) self.settingswidget.settingsupdated.connect(self.settingsupdated) self.dataentrywidget = DataEntryWidget(self.canvas) self.dataentrywidget.lastwidgetremoved.connect(self.dataentryfinished) self.widgetpage.layout().addWidget(self.dataentrywidget) self.dataentrywidget.rejected.connect(self.formrejected) RoamEvents.featuresaved.connect(self.featureSaved) RoamEvents.helprequest.connect(self.showhelp) RoamEvents.deletefeature.connect(self.delete_feature) RoamEvents.layerLoaded.connect(self.update_layer_loaded_progress) def createSpacer(width=0, height=0): widget = QWidget() widget.setMinimumWidth(width) widget.setMinimumHeight(height) return widget sidespacewidget = createSpacer(30) sidespacewidget2 = createSpacer(height=20) sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2) self.spaceraction = self.menutoolbar.insertWidget( self.actionProject, sidespacewidget) self.panels = [] self.actionGPSFeature.setProperty('dataentry', True) self.infodock = InfoDock(self.canvas) self.infodock.featureupdated.connect(self.highlightfeature) self.infodock.activeLayerChanged.connect(self.canvas.setCurrentLayer) self.infodock.hide() self.hidedataentry() RoamEvents.openimage.connect(self.openimage) RoamEvents.openurl.connect(self.viewurl) RoamEvents.openfeatureform.connect(self.openForm) RoamEvents.openkeyboard.connect(self.openkeyboard) RoamEvents.editgeometry_complete.connect(self.on_geometryedit) RoamEvents.onShowMessage.connect(self.show_ui_message) RoamEvents.selectionchanged.connect(self.show_info_results) RoamEvents.show_widget.connect(self.dataentrywidget.add_widget) RoamEvents.closeProject.connect(self.close_project) self.legendpage.showmap.connect(self.showmap) self.currentselection = {} iface = RoamInterface(RoamEvents, GPS, self, self.canvas_page, self) plugins.api = iface def update_layer_loaded_progress(self, layerno, outof): pass # print(layerno, outof) def delete_feature(self, form, feature) -> None: """ Slot called when a feature needs to be deleted. :param form: The form to use when deleting the feature. :param feature: The feature to delete. """ featureform = form.create_featureform(feature) try: msg = featureform.deletemessage except AttributeError: msg = 'Do you really want to delete this feature?' box = DeleteFeatureDialog(msg) if not box.exec_(): return try: featureform.delete() except featureform.DeleteFeatureException as ex: RoamEvents.raisemessage(*ex.error) return featureform.featuredeleted(feature) def show_project_menu_buttons(self, visible: bool) -> None: """ Show/Hide the the project side menu buttons. :param visible: The visible state for the menu buttons. """ for action in self.projectbuttons: action.setVisible(visible) def add_plugin_pages(self, pages) -> None: """ Add pages from the plugin to the side pabel. :param pages: List of plugin page classes to create and attach to the side panel. """ def safe_connect(method, to): try: method.connect(to) except AttributeError: pass for PageClass in pages: action = QAction(self.menutoolbar) text = PageClass.title.ljust(13) action.setIconText(text) action.setIcon(QIcon(PageClass.icon)) action.setCheckable(True) if PageClass.projectpage: action.setVisible(False) self.projectbuttons.append(action) self.menutoolbar.insertAction(self.spaceraction, action) else: self.menutoolbar.insertAction(self.actionProject, action) pagewidget = PageClass(plugins.api, self) safe_connect(RoamEvents.selectionchanged, pagewidget.selection_changed) safe_connect(RoamEvents.projectloaded, pagewidget.project_loaded) pageindex = self.stackedWidget.insertWidget(-1, pagewidget) action.setProperty('page', pageindex) self.pluginactions.append(action) self.menuGroup.addAction(action) def show_ui_message(self, label, message, level=Qgis.Info, time=0, extra='') -> None: """ Show a message to the user in the message bar. :param label: The label used as the message header. :param message: The main message. :param level: The level of the message. Default Qgis.Info :param time: The length of time in seconds to show the message. :param extra: An extra data to show the user. :return: """ self.bar.pushMessage(label, message, level, duration=time, extrainfo=extra) def init_legend(self) -> None: """ Init the legend object with the canvas. :return: """ self.legendpage.init(self.canvas) def openkeyboard(self) -> None: """ Open the on screen keyboard :return: """ if not roam.config.settings.get('keyboard', True): return # TODO Use the Qt keyboard roam.api.utils.open_keyboard() def viewurl(self, url: QUrl) -> None: """ Open a URL in Roam :param url: The URL to view. bb :return: """ key = url.toString().lstrip('file://') try: # Hack. Eww fix me. data, imagetype = roam.htmlviewer.images[os.path.basename(key)] pix = QPixmap() if imagetype == 'base64': pix.loadFromData(data) else: pix.load(data) self.openimage(pix) except KeyError: pix = QPixmap() pix.load(key) if pix.isNull(): QDesktopServices.openUrl(url) return self.openimage(pix) def openimage(self, pixmap: QPixmap) -> None: """ Open the image viewer for the given pixmap :param pixmap: The pixmap to open in the viewer """ viewer = ImageViewer(self.stackedWidget) viewer.resize(self.stackedWidget.size()) viewer.openimage(pixmap) def show_undo(self, title, message, form, feature): item = roam.messagebaritems.UndoMessageItem(title, message, form, feature) item.undo.connect(self.undo_delete) self.bar.pushItem(item) def undo_delete(self, form, feature) -> None: """ Undo a delete of a feature. :param form: :param feature: :return: """ # Add the feature back to the layer self.bar.popWidget() layer = form.QGISLayer layer.startEditing() layer.addFeature(feature) layer.commitChanges() def search_for_projects(self) -> None: """ Search for plugins from the update server. """ server = roam.config.settings.get('updateserver', '') self.projectupdater.update_server(server, self.projects) def settingsupdated(self, settings) -> None: """ Called when the settings have been updated. Used to keep the UI in sync with any settings changes. :param settings: The new settings. """ self.settings = settings self.show() smallmode = self.settings.get("smallmode", False) self.menutoolbar.setSmallMode(smallmode) self.canvas_page.settings_updated(settings) def on_geometryedit(self, form, feature) -> None: """ Called when a features geometry has been edited. :param form: The form to pull the QGIS layer from. :param feature: The feature that had it's geometry updated. """ layer = form.QGISLayer self.reloadselection(layer, updated=[feature]) def handle_removed_features(self, layer, layerid, deleted_feature_ids): self.canvas.refresh() self.reloadselection(layer, deleted=deleted_feature_ids) def reloadselection(self, layer, deleted=[], updated=[]): """ Reload the selection after features have been updated or deleted. :param layer: :param deleted: :param updated: :return: """ selectedfeatures = [] for selection_layer, features in self.currentselection.items(): if layer.name() == selection_layer.name(): selectedfeatures = features layer = selection_layer break if not selectedfeatures: return # Update any features that have changed. for updatedfeature in updated: oldfeatures = [ f for f in selectedfeatures if f.id() == updatedfeature.id() ] for feature in oldfeatures: self.currentselection[layer].remove(feature) self.currentselection[layer].append(updatedfeature) # Delete any old ones for deletedid in deleted: oldfeatures = [f for f in selectedfeatures if f.id() == deletedid] for feature in oldfeatures: self.currentselection[layer].remove(feature) RoamEvents.selectionchanged.emit(self.currentselection) def highlightfeature(self, layer, feature, features): self.canvas_page.highlight_active_selection(layer, feature, features) RoamEvents.activeselectionchanged.emit(layer, feature, features) def showmap(self): self.actionMap.setVisible(True) self.actionLegend.setVisible(True) self.actionMap.trigger() def hidedataentry(self): self.actionDataEntry.setVisible(False) def showdataentry(self): self.actionDataEntry.setVisible(True) self.actionDataEntry.trigger() def raiseerror(self, *exinfo): self.bar.pushError(*exinfo) def showhelp(self, parent, url): """ Show the help page in the UI. :param parent: :param url: :return: """ help = HelpPage(parent) help.setHelpPage(url) help.show() def dataentryfinished(self): self.hidedataentry() self.showmap() self.cleartempobjects() self.infodock.refreshcurrent() def featuresdeleted(self, layerid, featureids): layer = QgsProject.instance().mapLayer(layerid) self.reloadselection(layer, deleted=featureids) self.canvas.refresh() def featureSaved(self, *args): # self.reloadselection(layer, deleted=[featureid]) self.canvas.refresh() def cleartempobjects(self): self.canvas_page.clear_temp_objects() def formrejected(self, message, level): if message: RoamEvents.raisemessage("Form Message", message, level, duration=2) def openForm(self, form, feature, editmode, *args): """ Open the form that is assigned to the layer """ self.showdataentry() self.dataentrywidget.load_feature_form(feature, form, editmode, *args) def editfeaturegeometry(self, form, feature, newgeometry): layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() RoamEvents.editgeometry_complete.emit(form, feature) def exit(self) -> None: """ Exit the application. """ self.close() def show_info_results(self, results) -> None: """ Show the info panel with the given results :param results: The results to show in the info panel. """ forms = {} for layer in results.keys(): layername = layer.name() if not layername in forms: forms[layername] = list(self.project.formsforlayer(layername)) self.currentselection = results self.infodock.setResults(results, forms, self.project) self.infodock.show() def missing_layers_handler(self, layers): """ Called when layers have failed to load from the current project """ roam.utils.warning("Missing layers") map(roam.utils.warning, layers) missinglayers = roam.messagebaritems.MissingLayerItem(layers) self.bar.pushItem(missinglayers) def load_projects(self, projects): """ Load the given projects into the project list """ projects = list(projects) self.projects = projects self.projectwidget.loadProjectList(projects) self.syncwidget.loadprojects(projects) self.search_for_projects() def updatePage(self, action): """ Update the current stack page based on the current selected action """ page = action.property("page") self.stackedWidget.setCurrentIndex(page) def show(self): """ Override show method. Handles showing the app in fullscreen mode or just maximized """ fullscreen = roam.config.settings.get("fullscreen", False) if fullscreen: self.showFullScreen() else: self.showMaximized() def show_project_list(self): """ Show the project list as the current page in the app. :return: """ self.stackedWidget.setCurrentIndex(1) @property def enabled_plugins(self) -> list: """ Return the names of the enabled plugins :return: List of plugin names that should be enabled. """ return self.settings.get('plugins', []) @roam.utils.timeit def project_opened(self, doc): """ Called when a new project is opened in QGIS. :param: doc The project document that was opened in QGIS. """ GPS.crs = self.canvas_page.crs projectpath = QgsProject.instance().fileName() self.project = Project.from_folder(os.path.dirname(projectpath)) # Show panels try: ## TODO Port this logic or drop in Roam 3 for panel in self.project.getPanels(): self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel) self.panels.append(panel) except NotImplementedError: pass self.clear_plugins() self.add_plugins(self.project.enabled_plugins) self.legendpage.setRoot(QgsProject.instance().layerTreeRoot()) gps_loglayer = self.project.gpslog_layer() if gps_loglayer: self.tracking.enable_logging_on(gps_loglayer) else: roam.utils.info("No gps_log found for GPS logging") self.tracking.clear_logging() for layer in roam.api.utils.layers(): if not layer.type() == QgsMapLayer.VectorLayer: continue layer.committedFeaturesRemoved.connect( partial(self.handle_removed_features, layer)) self.canvas_page.project_loaded(self.project) self.showmap() self.show_project_menu_buttons(True) self.dataentrywidget.project = self.project RoamEvents.projectloaded.emit(self.project) def clear_plugins(self) -> None: """ Clear the loaded plugins. """ self.projectbuttons = [] self.projectbuttons.append(self.actionMap) self.projectbuttons.append(self.actionLegend) for action in self.pluginactions: # Remove the page widget, because we make it on each load widget = self.stackedWidget.widget(action.property("page")) self.stackedWidget.removeWidget(widget) if widget: widget.deleteLater() self.menutoolbar.removeAction(action) self.pluginactions = [] def add_plugins(self, pluginnames) -> None: """ Add loaded plugins panels to the main interface. :param pluginnames: The names of the plugins to load into the interface :return: """ for name in pluginnames: # Get the plugin try: plugin_mod = plugins.loaded_plugins[name] except KeyError: continue if not hasattr(plugin_mod, 'pages'): roam.utils.warning( "No pages() function found in {}".format(name)) continue pages = plugin_mod.pages() self.add_plugin_pages(pages) @roam.utils.timeit def load_roam_project(self, project): """ Load a project into the application. """ roam.utils.log(project) roam.utils.log(project.name) roam.utils.log(project.projectfile) roam.utils.log(project.valid) (passed, message) = project.onProjectLoad() if not passed: self.bar.pushMessage("Project load rejected", "Sorry this project couldn't" "be loaded. Click for me details.", Qgis.Warning, extrainfo=message) return self.actionMap.trigger() self.close_project() # No idea why we have to set this each time. Maybe QGIS deletes it for # some reason. self.badLayerHandler = BadLayerHandler( callback=self.missing_layers_handler) QgsProject.instance().setBadLayerHandler(self.badLayerHandler) # Project loading screen self.stackedWidget.setCurrentIndex(3) self.projectloading_label.setText("Project {} Loading".format( project.name)) pixmap = QPixmap(project.splash) w = self.projectimage.width() h = self.projectimage.height() self.projectimage.setPixmap(pixmap.scaled(w, h, Qt.KeepAspectRatio)) QApplication.processEvents() QDir.setCurrent(os.path.dirname(project.projectfile)) project.load_project() def close_project(self, project=None): """ Close the current open project """ if project is not None and not project == self.project: return RoamEvents.projectClosing.emit() self.tracking.clear_logging() self.dataentrywidget.clear() self.canvas_page.cleanup() QgsProject.instance().removeAllMapLayers() for panel in self.panels: self.removeDockWidget(panel) del panel # Remove all the old buttons self.panels = [] oldproject = self.project self.project = None self.show_project_menu_buttons(False) self.hidedataentry() self.infodock.close() RoamEvents.selectioncleared.emit() RoamEvents.projectClosed.emit(oldproject) self.projectwidget.set_open_project(None)